Frage

Was sind einige gemeinsam reale Welt Beispiele von dem Builder-Muster mit? Was bedeutet es Ihnen kaufen? Warum nicht einfach eine Fabrik Muster verwenden?

War es hilfreich?

Lösung

Der wesentliche Unterschied zwischen einem Builder und Fabrik IMHO, ist, dass ein Bauherr ist nützlich, wenn Sie viele Dinge tun müssen, um ein Objekt zu bauen. Zum Beispiel eines DOM vorstellen. Sie müssen viele Knoten erstellen und Attribute Ihr endgültiges Ziel zu bekommen. Eine Fabrik wird verwendet, wenn die Fabrik kann leicht das gesamte Objekt innerhalb eines Methodenaufrufes erstellen.

Ein Beispiel einen Builder der Verwendung eines Gebäudes ein XML-Dokument ist, habe ich dieses Modell verwendet, wenn zum Beispiel HTML-Fragmente bauen könnte ich einen Builder haben eine bestimmte Art der Tabelle für den Aufbau und es könnte haben die folgenden Methoden (Parameter werden nicht angezeigt) :

BuildOrderHeaderRow()
BuildLineItemSubHeaderRow()
BuildOrderRow()
BuildLineItemSubRow()

Dieser Builder würde ausspucken dann den HTML-Code für mich. Das ist viel einfacher zu lesen als durch eine große Verfahrensweise zu Fuß.

Schauen Sie sich Builder-Muster auf Wikipedia .

Andere Tipps

Im Folgenden einige Gründe sprechen dafür, die Verwendung des Musters und Beispiel-Code in Java, aber es ist eine Implementierung des Builder-Muster von der Gang of Four bedeckt in Design Patterns . Die Gründe, warum Sie es in Java sind auch auf andere Programmiersprachen als auch verwenden würden.

Wie Joshua Bloch heißt in Effective Java, 2nd Edition :

  

Der Erbauer ist eine gute Wahl, wenn Klassen, deren Konstrukteure oder statische Fabriken entwerfen würde mehr haben als eine Handvoll Parameter.

Wir haben alle an einem gewissen Punkt eine Klasse mit einer Liste des Konstrukteurs angetroffen, wo jeder zusätzlich einen neue Option Parameter ergänzt:

Pizza(int size) { ... }        
Pizza(int size, boolean cheese) { ... }    
Pizza(int size, boolean cheese, boolean pepperoni) { ... }    
Pizza(int size, boolean cheese, boolean pepperoni, boolean bacon) { ... }

Dies ist die Teles Constructor Pattern genannt. Das Problem mit diesem Muster das ist einmal Konstrukteuren sind 4 oder 5 Parameter lange es wird schwer zu merken die erforderliche Reihenfolge der Parameter als auch, was insbesondere Konstruktor Sie in einer bestimmten Situation wollen könnten.

Ein Alternative Sie zum Teles Constructor Muster haben, ist die JavaBean Muster , wo Sie einen Konstruktor mit den obligatorischen Parametern aufrufen und rufen dann alle optionalen Setter nach:

Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);

Das Problem hier ist, dass, da das Objekt über mehrere Anrufe erstellt wird er durch seine Konstruktion partway in einem inkonsistenten Zustand sein kann. Dies auch viele zusätzlichen Aufwand erfordert Thread-Sicherheit zu gewährleisten.

Die bessere Alternative ist es, das Builder-Muster zu verwenden.

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;
  }
}

Beachten Sie, dass Pizza ist unveränderlich und dass Parameterwerte sind alle in einem einzigen Standort . Da die Setter der Builder Methoden, um die Builder-Objekt zurückgeben sie sind können verkettet werden .

Pizza pizza = new Pizza.Builder(12)
                       .cheese(true)
                       .pepperoni(true)
                       .bacon(true)
                       .build();

Dies führt zu Code, der einfach zu schreiben und ist sehr einfach zu lesen und zu verstehen. In diesem Beispiel wird der bauen Verfahren modifiziert werden könnte, überprüfen Parameter, nachdem sie haben wurde vom Baumeister zum Pizza Objekt kopiert und ein Illegal werfen, wenn ein ungültiger Parameterwert geliefert wurde. Dieses Muster ist flexibel und es ist leicht zu mehr Parameter, um es in Zukunft hinzuzufügen. Es ist wirklich nur sinnvoll, wenn Sie mehr als 4 oder 5 Parameter für einen Konstruktor haben werden. Das heißt, es könnte in erster Linie sich lohnen, wenn Sie vermuten, dass Sie mehrere Parameter in der Zukunft sein kann hinzufügen.

Ich habe stark zu diesem Thema aus dem Buch Effective Java, 2nd Edition von Joshua Bloch entlehnt. Um mehr über dieses Muster und andere wirksame Java Praktiken lernen Ich kann es sehr empfehlen.

Betrachten wir ein Restaurant. Die Schaffung von „heutigen Mahlzeit“ ist ein Fabrik-Muster, weil man die Küche sagen „mir heutigen Mahlzeit“ und die Küche (Fabrik) entscheidet, was das Objekt auf versteckte Kriterien zu erzeugen, basiert.

Der Builder wird angezeigt, wenn Sie eine benutzerdefinierte Pizza bestellen. In diesem Fall sagt der Kellner des Koch (Baumeister) „Ich brauche eine Pizza;! Käse, Zwiebeln und Speck, um es hinzuzufügen“ Somit setzt der Bauherr die Attribute der erzeugte Gegenstand haben sollte, aber versteckt, wie sie eingestellt werden.

.NET Stringbuilder-Klasse ist ein großartiges Beispiel für Erbauer. Es ist vor allem verwendet, um eine Zeichenfolge in einer Reihe von Schritten zu erstellen. Das Endergebnis Sie tun ToString get () ist immer ein String sondern die Schaffung dieser Zeichenfolge variiert je nach, welche Funktionen in der Klasse String verwendet wurden. Um es zusammenzufassen, die Grundidee ist, komplexe Objekte zu bauen und verstecken die Details der Implementierung, wie es gebaut wird.

Für ein Multi-Threaded-Problem, brauchten wir ein komplexes Objekt für jeden Thread aufgebaut werden. Das Objekt repräsentiert die Daten verarbeitet werden und könnte in Abhängigkeit von der Benutzereingabe ändern.

Könnten wir eine Fabrik stattdessen verwenden? Ja

Warum haben wir das nicht? Builder macht mehr Sinn, denke ich.

Fabriken werden für die Erstellung von verschiedenen Arten von Objekten verwendet, die denselben Grundtyp (implementieren die gleiche Schnittstelle oder Basisklasse) sind.

Builders bauen die gleiche Art von Objekt immer und immer wieder, aber der Aufbau ist dynamisch, so kann es zur Laufzeit geändert werden.

Während gehen durch Microsoft MVC-Framework, bekam ich einen Gedanken über Erbauer. Ich kam über das Muster in der ControllerBuilder Klasse. Diese Klasse ist die Controller Factory-Klasse zurückkehren, die dann verwendet wird, konkrete Controller zu bauen.

Vorteil ich bei der Verwendung Erbauer sehen ist, dass Sie eine Fabrik der eigenen erstellen und sie in den Rahmen stecken.

@Tetha kann ein Restaurant (Framework) seine vom italienischen Typ lief, die Pizza serviert. Um Pizza Italian guy (Object Builder) verwendet Owen (Factory) mit einem Pizzaboden (Basisklasse).

Vorbereitung

Jetzt indischer Mann übernimmt das Restaurant vom italienischen Typ. Indian Restaurant (Framework) Server Dosa statt Pizza. Um Dosa indischen Mann (Object Builder) verwendet Bratpfanne (Factory) mit einer Maida (Basisklasse)

Vorbereitung

Wenn Sie Szenario aussehen, das Essen ist anders, Art und Weise Essen zubereitet wird, ist unterschiedlich, aber im selben Restaurant (unter gleichem Rahmen). Restaurant sollte so gebaut werden, dass es chinesische unterstützen kann, mexikanische oder jede Küche. Object Builder innerhalb Rahmen erleichtert Art von Küche Plugin Sie wollen. zum Beispiel

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;
   }
}

Sie es verwenden, wenn Sie viele Möglichkeiten haben, zu beschäftigen. Denken Sie an Dinge wie JMock:

m.expects(once())
    .method("testMethod")
    .with(eq(1), eq(2))
    .returns("someResponse");

Es fühlt sich viel natürlicher und ist ... möglich.

Es gibt auch xml Gebäude, string Gebäude und viele andere Dinge. Stellen Sie sich vor, wenn java.util.Map als Builder gesetzt hatte. Sie könnten Dinge tun, wie folgt:

Map<String, Integer> m = new HashMap<String, Integer>()
    .put("a", 1)
    .put("b", 2)
    .put("c", 3);

Aufbauend auf den bisherigen Antworten (Wortspiel beabsichtigt), ein ausgezeichnetes reale Welt Beispiel Groovy 's eingebaute Unterstützung für Builders.

Siehe Builders in der Groovy Dokumentation

Ein weiterer Vorteil des Bauherrn ist, dass, wenn Sie eine Fabrik haben, gibt es noch einige Einkopplung von Code ist, weil für die Fabrik zu arbeiten, hat es alle Objekte zu wissen, dass es möglicherweise erstellen . Wenn Sie ein anderes Objekt hinzufügen, das erstellt werden konnte, haben Sie die Factory-Klasse zu ändern, ihn aufzunehmen. Dies geschieht in der Abstract Factory als auch.

Mit dem Baumeister, auf der anderen Seite, die Sie gerade einen neuen Betonbauer für diese neue Klasse erstellen müssen. Die Direktor-Klasse wird gleich bleiben, weil es die Erbauer im Konstruktor erhält.

Auch gibt es viele Varianten von Builder. Kamikaze Mercenary gibt einen anderen.

/// <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);
    }
}

Ich mochte immer die Erbauer Muster als etwas unhandlich, aufdringliche und sehr oft von weniger erfahrenen Programmierern missbraucht. Es ist ein Muster, das macht nur Sinn, wenn Sie das Objekt aus einigen Daten montieren müssen, die erfordert eine post-Initialisierungsschritt (das heißt, sobald alle Daten gesammelt werden - etwas mit ihm). Stattdessen werden nur in 99% der Zeit Builder verwendet, um die Klassenmitglieder zu initialisieren.

In solchen Fällen ist es viel besser, einfach withXyz(...) Typ Setter innerhalb der Klasse zu erklären und ihnen eine Referenz auf sich selbst machen zurückzukehren.

Bedenken Sie:

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");

Jetzt haben wir eine ordentliche einzige Klasse, die ihre eigene Initialisierung verwaltet und macht ziemlich genau die gleiche Arbeit wie die Baumeister, mit der Ausnahme, dass seine viel eleganter.

Ich benutzen Builder in home-grown-Messaging-Bibliothek. Die Bibliothek Kern wurde Daten aus dem Drahtaufnahme, es mit Builder-Instanz zu sammeln, dann, sobald Builder entschieden it've alles bekam es erforderlich, um eine Nachricht Instanz zu erstellen, Builder.GetMessage () wurde eine Nachrichteninstanz Konstruktion unter Verwendung der Daten aus dem gesammelten Draht.

Wenn ich den Standard XMLGregorianCalendar für meine XML verwenden, wollte es das Objekt Rangierung von Datetime in Java, hörte ich eine Menge Kommentare, wie schweres Gewicht und unhandlich war es zu benutzen. Ich habe versucht, die XML-Felder in den xs Comtrol. Datetime structs Zeitzone zu verwalten, Millisekunden, etc.

Also habe ich ein Programm entwickelt, einen XMLGregorian Kalender von einem GregorianCalendar oder java.util.Date zu bauen.

Wegen, wo ich arbeite ich es nicht erlaubt online legal zu teilen, aber hier ist ein Beispiel dafür, wie es ein Client verwendet. Sie abstrahiert die Details und filtert einen Teil der Implementierung von XMLGregorianCalendar, die weniger verwendet sind für xs:. Datetime

XMLGregorianCalendarBuilder builder = XMLGregorianCalendarBuilder.newInstance(jdkDate);
XMLGregorianCalendar xmlCalendar = builder.excludeMillis().excludeOffset().build();

Zugegeben dieses Muster mehr eines Filters ist, wie es Felder in der xmlCalendar als undefiniert setzt, so dass sie ausgeschlossen werden, ist es nach wie vor „baut“ es. Ich habe hinzugefügt leicht andere Optionen zu dem Erbauer xs zu erstellen: Datum und xs:. Zeit-Struktur und auch Zeitzone Offsets zu manipulieren, wenn nötig

Wenn Sie jemals Code gesehen haben, die erstellt und verwendet XMLGregorianCalendar, würden Sie sehen, wie dies machte es viel einfacher zu handhaben.

überprüfen Sie heraus InnerBuilder, eine IntelliJ IDEA-Plugin, das einen 'Builder' Aktion zum Generieren Menü (Alt + Insert), die eine innere Builder-Klasse erzeugt fügt wie in Effective Java beschrieben

https://github.com/analytically/innerbuilder

Ein großes reale Welt Beispiel ist zu verwenden, wenn das Gerät Ihre Klassen zu testen. Sie verwenden sut (System Under Test) Bauer.

Beispiel:

Klasse:

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;

}

Test:

    [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);
    }

sut Builder:

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();
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top