Apa saja contoh umum , dunia nyata dalam menggunakan Pola Builder? Apa yang membelikanmu? Mengapa tidak menggunakan Pola Pabrik?
Apa saja contoh umum , dunia nyata dalam menggunakan Pola Builder? Apa yang membelikanmu? Mengapa tidak menggunakan Pola Pabrik?
Jawaban:
Perbedaan utama antara pembangun dan pabrik IMHO, adalah pembangun berguna ketika Anda perlu melakukan banyak hal untuk membangun objek. Misalnya bayangkan DOM. Anda harus membuat banyak node dan atribut untuk mendapatkan objek akhir Anda. Pabrik digunakan ketika pabrik dapat dengan mudah membuat seluruh objek dalam satu panggilan metode.
Salah satu contoh menggunakan pembangun adalah membangun dokumen XML, saya telah menggunakan model ini ketika membangun fragmen HTML misalnya saya mungkin memiliki Builder untuk membangun jenis tabel tertentu dan mungkin memiliki metode berikut (parameter tidak ditampilkan) :
BuildOrderHeaderRow()
BuildLineItemSubHeaderRow()
BuildOrderRow()
BuildLineItemSubRow()
Pembangun ini kemudian akan memuntahkan HTML untuk saya. Ini jauh lebih mudah dibaca daripada berjalan melalui metode prosedural yang besar.
Lihat Pola Builder di Wikipedia .
Berikut adalah beberapa alasan yang memperdebatkan penggunaan pola dan kode contoh di Jawa, tetapi ini merupakan implementasi dari Pola Builder yang dicakup oleh Geng Empat dalam Pola Desain . Alasan Anda menggunakannya di Jawa juga berlaku untuk bahasa pemrograman lain.
Seperti yang dikatakan Joshua Bloch dalam Java Efektif, Edisi ke-2 :
Pola pembangun adalah pilihan yang baik ketika merancang kelas yang konstruktor atau pabrik statisnya akan memiliki lebih dari beberapa parameter.
Kita semua pada beberapa titik menghadapi kelas dengan daftar konstruktor di mana setiap penambahan menambahkan parameter opsi baru:
Pizza(int size) { ... }
Pizza(int size, boolean cheese) { ... }
Pizza(int size, boolean cheese, boolean pepperoni) { ... }
Pizza(int size, boolean cheese, boolean pepperoni, boolean bacon) { ... }
Ini disebut Pola Konstruktor Telescoping. Masalah dengan pola ini adalah bahwa setelah konstruktor 4 atau 5 parameter panjang menjadi sulit untuk mengingat yang diperlukan urutan parameter serta apa konstruktor tertentu Anda mungkin ingin dalam situasi tertentu.
Salah satu alternatif yang Anda miliki untuk Pola Konstruktor Telescoping adalah Pola JavaBean di mana Anda memanggil konstruktor dengan parameter wajib dan kemudian memanggil setter opsional setelah:
Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);
Masalahnya di sini adalah bahwa karena objek dibuat melalui beberapa panggilan itu mungkin dalam keadaan tidak konsisten di tengah-tengah pembangunannya. Ini juga membutuhkan banyak upaya ekstra untuk memastikan keamanan benang.
Alternatif yang lebih baik adalah dengan menggunakan Pola Builder.
public class Pizza {
private int size;
private boolean cheese;
private boolean pepperoni;
private boolean bacon;
public static class Builder {
//required
private final int size;
//optional
private boolean cheese = false;
private boolean pepperoni = false;
private boolean bacon = false;
public Builder(int size) {
this.size = size;
}
public Builder cheese(boolean value) {
cheese = value;
return this;
}
public Builder pepperoni(boolean value) {
pepperoni = value;
return this;
}
public Builder bacon(boolean value) {
bacon = value;
return this;
}
public Pizza build() {
return new Pizza(this);
}
}
private Pizza(Builder builder) {
size = builder.size;
cheese = builder.cheese;
pepperoni = builder.pepperoni;
bacon = builder.bacon;
}
}
Perhatikan bahwa Pizza tidak dapat diubah dan bahwa nilai parameter semuanya dalam satu lokasi . Karena metode setter Builder mengembalikan objek Builder, mereka dapat dirantai .
Pizza pizza = new Pizza.Builder(12)
.cheese(true)
.pepperoni(true)
.bacon(true)
.build();
Ini menghasilkan kode yang mudah ditulis dan sangat mudah dibaca dan dimengerti. Dalam contoh ini, metode build dapat dimodifikasi untuk memeriksa parameter setelah mereka disalin dari builder ke objek Pizza dan melempar IllegalStateException jika nilai parameter tidak valid telah diberikan. Pola ini fleksibel dan mudah untuk menambahkan lebih banyak parameter ke depannya. Ini benar-benar hanya berguna jika Anda akan memiliki lebih dari 4 atau 5 parameter untuk konstruktor. Yang mengatakan, mungkin bermanfaat pada awalnya jika Anda curiga Anda mungkin menambahkan lebih banyak parameter di masa depan.
Saya telah meminjam banyak pada topik ini dari buku Effective Java, 2nd Edition oleh Joshua Bloch. Untuk mempelajari lebih lanjut tentang pola ini dan praktik Java efektif lainnya, saya sangat merekomendasikannya.
new Pizza.Builder(12).cheese().pepperoni().bacon().build();
Pizza.Builder(12).cheese().pepperoni().bacon().build();
Anda perlu mengkompilasi ulang kode Anda atau memiliki logika yang tidak perlu jika Anda hanya memerlukan pepperoni pizza. Paling tidak Anda juga harus memberikan versi parametrized seperti yang disarankan @Kamikaze Mercenary. Pizza.Builder(12).cheese(true).pepperoni(false).bacon(false).build();
. Kemudian lagi, kita tidak pernah menguji unit, bukan?
Pertimbangkan sebuah restoran. Penciptaan "makanan hari ini" adalah pola pabrik, karena Anda memberi tahu dapur "ambilkan saya makanan hari ini" dan dapur (pabrik) memutuskan objek apa yang akan dihasilkan, berdasarkan kriteria tersembunyi.
Pembuat akan muncul jika Anda memesan pizza khusus. Dalam hal ini, pelayan memberi tahu koki (pembangun), "Saya butuh pizza; tambahkan keju, bawang, dan daging asap!" Dengan demikian, pembangun memperlihatkan atribut yang seharusnya dimiliki objek, tetapi menyembunyikan cara mengaturnya.
.NET StringBuilder class adalah contoh yang bagus dari pola builder. Sebagian besar digunakan untuk membuat string dalam serangkaian langkah. Hasil akhir yang Anda dapatkan saat melakukan ToString () selalu berupa string tetapi pembuatan string tersebut bervariasi sesuai dengan fungsi apa yang digunakan di kelas StringBuilder. Singkatnya, ide dasarnya adalah untuk membangun objek yang kompleks dan menyembunyikan detail implementasi bagaimana itu sedang dibangun.
b.append(...).append(...)
sebelum akhirnya memanggil toString()
. Kutipan: infoq.com/articles/internal-dsls-java
Untuk masalah multi-utas, kami membutuhkan objek kompleks yang akan dibangun untuk setiap utas. Objek mewakili data yang sedang diproses, dan bisa berubah tergantung pada input pengguna.
Bisakah kita menggunakan pabrik saja? Iya
Kenapa kita tidak melakukannya? Builder lebih masuk akal kurasa.
Pabrik digunakan untuk membuat berbagai jenis objek yang memiliki tipe dasar yang sama (mengimplementasikan antarmuka atau kelas dasar yang sama).
Pembangun membangun jenis objek yang sama berulang-ulang, tetapi konstruksinya dinamis sehingga dapat diubah saat runtime.
Anda menggunakannya ketika Anda memiliki banyak opsi untuk dihadapi. Pikirkan tentang hal-hal seperti jmock:
m.expects(once())
.method("testMethod")
.with(eq(1), eq(2))
.returns("someResponse");
Rasanya jauh lebih alami dan ... mungkin.
Ada juga xml building, string building, dan banyak hal lainnya. Bayangkan jika java.util.Map
sudah dimasukkan sebagai pembangun. Anda dapat melakukan hal-hal seperti ini:
Map<String, Integer> m = new HashMap<String, Integer>()
.put("a", 1)
.put("b", 2)
.put("c", 3);
Saat melalui kerangka kerja Microsoft MVC, saya mendapat pemikiran tentang pola pembangun. Saya menemukan pola di kelas ControllerBuilder. Kelas ini untuk mengembalikan kelas pabrik pengontrol, yang kemudian digunakan untuk membangun pengontrol beton.
Keuntungan yang saya lihat dalam menggunakan pola builder adalah, Anda dapat membuat pabrik sendiri dan memasukkannya ke dalam kerangka kerja.
@Tetha, mungkin ada restoran (Kerangka kerja) yang dijalankan oleh pria Italia, yang menyajikan Pizza. Untuk menyiapkan pizza pria Italia (Object Builder) menggunakan Owen (Factory) dengan basis pizza (kelas dasar).
Sekarang pria India mengambil alih restoran dari pria Italia. Restoran India (Kerangka kerja) server dosa bukannya pizza. Untuk mempersiapkan dosa orang India (pembangun objek) menggunakan Frying Pan (Factory) dengan Maida (kelas dasar)
Jika Anda melihat skenario, makanan berbeda, cara menyiapkan makanan berbeda, tetapi di restoran yang sama (di bawah kerangka kerja yang sama). Restoran harus dibangun sedemikian rupa sehingga dapat mendukung masakan Cina, Meksiko atau lainnya. Pembangun objek di dalam kerangka kerja memfasilitasi plugin jenis masakan yang Anda inginkan. sebagai contoh
class RestaurantObjectBuilder
{
IFactory _factory = new DefaultFoodFactory();
//This can be used when you want to plugin the
public void SetFoodFactory(IFactory customFactory)
{
_factory = customFactory;
}
public IFactory GetFoodFactory()
{
return _factory;
}
}
Saya selalu tidak menyukai pola Builder sebagai sesuatu yang berat, menonjol dan sangat sering disalahgunakan oleh programmer yang kurang berpengalaman. Ini adalah pola yang hanya masuk akal jika Anda perlu mengumpulkan objek dari beberapa data yang memerlukan langkah post-inisialisasi (yaitu setelah semua data dikumpulkan - lakukan sesuatu dengannya). Sebaliknya, dalam 99% pembangun waktu hanya digunakan untuk menginisialisasi anggota kelas.
Dalam kasus seperti itu, jauh lebih baik untuk mendeklarasikan withXyz(...)
tipe setter di dalam kelas dan membuatnya mengembalikan referensi ke dirinya sendiri.
Pertimbangkan ini:
public class Complex {
private String first;
private String second;
private String third;
public String getFirst(){
return first;
}
public void setFirst(String first){
this.first=first;
}
...
public Complex withFirst(String first){
this.first=first;
return this;
}
public Complex withSecond(String second){
this.second=second;
return this;
}
public Complex withThird(String third){
this.third=third;
return this;
}
}
Complex complex = new Complex()
.withFirst("first value")
.withSecond("second value")
.withThird("third value");
Sekarang kami memiliki kelas tunggal yang rapi yang mengelola inisialisasi sendiri dan melakukan pekerjaan yang hampir sama dengan pembangun, kecuali bahwa kelasnya jauh lebih elegan.
Keuntungan lain dari pembangun adalah bahwa jika Anda memiliki Pabrik, masih ada beberapa kopling dalam kode Anda, karena agar Pabrik berfungsi, ia harus mengetahui semua objek yang dapat dibuatnya . Jika Anda menambahkan objek lain yang bisa dibuat, Anda harus memodifikasi kelas pabrik untuk memasukkannya. Ini terjadi di Pabrik Abstrak juga.
Dengan builder, di sisi lain, Anda hanya perlu membuat builder beton baru untuk kelas baru ini. Kelas direktur akan tetap sama, karena menerima pembangun di konstruktor.
Juga, ada banyak rasa pembangun. Kamikaze Mercenary memberi satu lagi.
/// <summary>
/// Builder
/// </summary>
public interface IWebRequestBuilder
{
IWebRequestBuilder BuildHost(string host);
IWebRequestBuilder BuildPort(int port);
IWebRequestBuilder BuildPath(string path);
IWebRequestBuilder BuildQuery(string query);
IWebRequestBuilder BuildScheme(string scheme);
IWebRequestBuilder BuildTimeout(int timeout);
WebRequest Build();
}
/// <summary>
/// ConcreteBuilder #1
/// </summary>
public class HttpWebRequestBuilder : IWebRequestBuilder
{
private string _host;
private string _path = string.Empty;
private string _query = string.Empty;
private string _scheme = "http";
private int _port = 80;
private int _timeout = -1;
public IWebRequestBuilder BuildHost(string host)
{
_host = host;
return this;
}
public IWebRequestBuilder BuildPort(int port)
{
_port = port;
return this;
}
public IWebRequestBuilder BuildPath(string path)
{
_path = path;
return this;
}
public IWebRequestBuilder BuildQuery(string query)
{
_query = query;
return this;
}
public IWebRequestBuilder BuildScheme(string scheme)
{
_scheme = scheme;
return this;
}
public IWebRequestBuilder BuildTimeout(int timeout)
{
_timeout = timeout;
return this;
}
protected virtual void BeforeBuild(HttpWebRequest httpWebRequest) {
}
public WebRequest Build()
{
var uri = _scheme + "://" + _host + ":" + _port + "/" + _path + "?" + _query;
var httpWebRequest = WebRequest.CreateHttp(uri);
httpWebRequest.Timeout = _timeout;
BeforeBuild(httpWebRequest);
return httpWebRequest;
}
}
/// <summary>
/// ConcreteBuilder #2
/// </summary>
public class ProxyHttpWebRequestBuilder : HttpWebRequestBuilder
{
private string _proxy = null;
public ProxyHttpWebRequestBuilder(string proxy)
{
_proxy = proxy;
}
protected override void BeforeBuild(HttpWebRequest httpWebRequest)
{
httpWebRequest.Proxy = new WebProxy(_proxy);
}
}
/// <summary>
/// Director
/// </summary>
public class SearchRequest
{
private IWebRequestBuilder _requestBuilder;
public SearchRequest(IWebRequestBuilder requestBuilder)
{
_requestBuilder = requestBuilder;
}
public WebRequest Construct(string searchQuery)
{
return _requestBuilder
.BuildHost("ajax.googleapis.com")
.BuildPort(80)
.BuildPath("ajax/services/search/web")
.BuildQuery("v=1.0&q=" + HttpUtility.UrlEncode(searchQuery))
.BuildScheme("http")
.BuildTimeout(-1)
.Build();
}
public string GetResults(string searchQuery) {
var request = Construct(searchQuery);
var resp = request.GetResponse();
using (StreamReader stream = new StreamReader(resp.GetResponseStream()))
{
return stream.ReadToEnd();
}
}
}
class Program
{
/// <summary>
/// Inside both requests the same SearchRequest.Construct(string) method is used.
/// But finally different HttpWebRequest objects are built.
/// </summary>
static void Main(string[] args)
{
var request1 = new SearchRequest(new HttpWebRequestBuilder());
var results1 = request1.GetResults("IBM");
Console.WriteLine(results1);
var request2 = new SearchRequest(new ProxyHttpWebRequestBuilder("localhost:80"));
var results2 = request2.GetResults("IBM");
Console.WriteLine(results2);
}
}
Membangun pada jawaban sebelumnya (pun intended), contoh dunia nyata yang sangat baik adalah dukungan bawaan Groovy untuk Builders
.
MarkupBuilder
StreamingMarkupBuilder
SwingXBuilder
Lihat Pembangun dalam Dokumentasi Groovy
Saya menggunakan builder di perpustakaan perpesanan rumah-tumbuh. Inti pustaka menerima data dari kawat, mengumpulkannya dengan instance Builder, kemudian, setelah Builder memutuskan itu sudah mendapatkan semua yang diperlukan untuk membuat instance Message, Builder.GetMessage () sedang membangun instance pesan menggunakan data yang dikumpulkan dari kawat.
Lihat InnerBuilder, sebuah plugin IntelliJ IDEA yang menambahkan tindakan 'Builder' ke menu Generate (Alt + Insert) yang menghasilkan kelas builder dalam seperti yang dijelaskan dalam Java Efektif.
Ketika saya ingin menggunakan XMLGregorianCalendar standar untuk XML saya untuk objek marshalling DateTime di Jawa, saya mendengar banyak komentar tentang betapa berat dan rumitnya menggunakannya. Saya mencoba untuk comtrol bidang XML di xs: struct datetime untuk mengelola zona waktu, milidetik, dll.
Jadi saya merancang sebuah utilitas untuk membangun kalender XMLGregorian dari GregorianCalendar atau java.util.Date.
Karena tempat saya bekerja, saya tidak diizinkan membaginya secara online tanpa hukum, tetapi inilah contoh bagaimana klien menggunakannya. Ini abstrak rincian dan filter beberapa implementasi XMLGregorianCalendar yang kurang digunakan untuk xs: datetime.
XMLGregorianCalendarBuilder builder = XMLGregorianCalendarBuilder.newInstance(jdkDate);
XMLGregorianCalendar xmlCalendar = builder.excludeMillis().excludeOffset().build();
Pola ini memang lebih merupakan filter karena menyetel bidang dalam xmlCalendar sebagai tidak terdefinisi sehingga dikecualikan, masih "membuatnya". Saya telah dengan mudah menambahkan opsi lain ke pembuat untuk membuat xs: date, dan xs: struct waktu dan juga untuk memanipulasi zona waktu ketika diperlukan.
Jika Anda pernah melihat kode yang membuat dan menggunakan XMLGregorianCalendar, Anda akan melihat bagaimana ini membuatnya lebih mudah untuk dimanipulasi.
Contoh dunia nyata yang hebat adalah untuk digunakan ketika unit menguji kelas Anda. Anda menggunakan pembuat sut (System Under Test).
Contoh:
Kelas:
public class CustomAuthenticationService
{
private ICloudService _cloudService;
private IDatabaseService _databaseService;
public CustomAuthenticationService(ICloudService cloudService, IDatabaseService databaseService)
{
_cloudService = cloudService;
_databaseService = databaseService;
}
public bool IsAuthorized(User user)
{
//Implementation Details
return true;
}
Uji:
[Test]
public void Given_a_User_With_Permission_When_Verifying_If_Authorized_Then_Authorize_It_Returning_True()
{
CustomAuthenticationService sut = new CustomAuthenticationServiceBuilder();
User userWithAuthorization = null;
var result = sut.IsAuthorized(userWithAuthorization);
Assert.That(result, Is.True);
}
Pembuat Sutra:
public class CustomAuthenticationServiceBuilder
{
private ICloudService _cloudService;
private IDatabaseService _databaseService;
public CustomAuthenticationServiceBuilder()
{
_cloudService = new AwsService();
_databaseService = new SqlServerService();
}
public CustomAuthenticationServiceBuilder WithAzureService(AzureService azureService)
{
_cloudService = azureService;
return this;
}
public CustomAuthenticationServiceBuilder WithOracleService(OracleService oracleService)
{
_databaseService = oracleService;
return this;
}
public CustomAuthenticationService Build()
{
return new CustomAuthenticationService(_cloudService, _databaseService);
}
public static implicit operator CustomAuthenticationService (CustomAuthenticationServiceBuilder builder)
{
return builder.Build();
}
}
CustomAuthenticationService
kelas Anda ?