2013-03-17 8 views
5

Ich ging durch Effektive Java-Buch, und Erstellen von Notizen für meine zukünftige Referenz, Ich stieß auf Builder-Muster.Builder-Muster: Welche Variante ist bevorzugt?

Nun habe ich verstanden, was es ist und wie es verwendet werden soll.In dem Prozess habe ich zwei Beispiel Variationen des Builder-Muster erstellt.

Ich brauche Hilfe bei der Auflistung der Unterschiede und den Vorteil, den jeder hat? Nun, ich habe sicherlich bemerkt, dass Example 1 weniger Methoden, da durch weniger restriktiv und mehr generische, da es erlaubt, flexibler verwendet werden.

Bitte weisen Sie auf andere Dinge, die ich verpasst habe?

Beispiel 1

package item2; 

/** 
* @author Sudhakar Duraiswamy 
* 
*/ 
public class Vehicle { 

    private String type; 
    private int wheels; 

    interface Builder<T>{ 
     public T build(); 
    } 

    public static class CarBuilder implements Builder<Vehicle>{ 
     private String type; 
     private int wheels;  

     CarBuilder createVehicle(){ 
      this.type= "Car"; 
      return this; 
     } 

     CarBuilder addWheels(int wheels){ 
      this.wheels = wheels; 
      return this; 
     } 

     public Vehicle build(){ 
      Vehicle v = new Vehicle(); 
      v.type = type; 
      v.wheels = wheels; 
      return v; 
     }    
    } 

    public static class TruckBuilder implements Builder<Vehicle>{  
     private String type; 
     private int wheels; 

     TruckBuilder createVehicle(){   
      this.type= "Truck"; 
      return this; 
     } 

     TruckBuilder addWheels(int wheels){ 
      this.wheels = wheels; 
      return this; 
     } 

     public Vehicle build(){ 
      Vehicle v = new Vehicle(); 
      v.type = type; 
      v.wheels = wheels; 
      return v; 
     } 
    } 

    public Vehicle(){ 

    } 

    public static void main(String[] args) { 
     //This builds a car with 4 wheels 
     Vehicle car = new Vehicle.CarBuilder().createVehicle().addWheels(4).build(); 

     //THis builds a Truck with 10 wheels 
     Vehicle truck = new Vehicle.TruckBuilder().createVehicle().addWheels(10).build(); 

    } 
} 

Beispiel 2

package item2; 
/** 
* @author Sudhakar Duraiswamy 
* 
*/ 
public class Vehicle2 { 

    private String type; 
    private int wheels; 

    interface Builder<T>{ 
     public T build();  
     public String getType(); 
     public int getWheels() ; 
    } 

    public static class CarBuilder implements Builder<Vehicle2>{ 
     private String type; 
     private int wheels;  

     public String getType() { 
      return type; 
     } 
     public int getWheels() { 
      return wheels; 
     } 

     CarBuilder createVehicle(){ 
      this.type= "Car"; 
      return this; 
     } 

     CarBuilder addWheels(int wheels){ 
      this.wheels = wheels; 
      return this; 
     } 

     public Vehicle2 build(){   
      return new Vehicle2(this); 
     }    
    } 

    public static class TruckBuilder implements Builder<Vehicle2>{  
     private String type; 
     private int wheels; 

     public String getType() { 
      return type; 
     } 

     public int getWheels() { 
      return wheels; 
     } 

     TruckBuilder createVehicle(){   
      this.type= "Truck"; 
      return this; 
     } 

     TruckBuilder addWheels(int wheels){ 
      this.wheels = wheels; 
      return this; 
     } 

     public Vehicle2 build(){ 
      return new Vehicle2(this); 
     } 
    } 


public Vehicle2(Builder<? extends Vehicle2> builder){ 
    Vehicle2 v = new Vehicle2(); 
    v.type = builder.getType(); 
    v.wheels = builder.getWheels(); 
} 

    public Vehicle2(){ 
    } 

    public static void main(String[] args) {    
     //This builds a car with 4 wheels 
     Vehicle2 car = new Vehicle2.CarBuilder().createVehicle().addWheels(4).build(); 

     //THis builds a Truck with 10 wheels 
     Vehicle2 truck = new Vehicle2.TruckBuilder().createVehicle().addWheels(10).build(); 
    } 
} 
+7

Ernsthaft unten Abstimmung nicht verlassen Kommentare ist lahm – Sudhakar

+2

Jungs, wenn Sie Kommentare, warum diese Frage ist nicht angebracht verlassen könnte, wäre es mir wirklich helfen und die anderen – Sudhakar

+0

Sie über diese Website http gehen sollte: // en .wikipedia.org/wiki/ Builder_pattern # Java, um das entsprechende Beispiel für die 'Builder Pattern'-Implementierung zu sehen. –

Antwort

5

Keine der oben genannten.

Die erste erlaubt nicht, ein unveränderliches Fahrzeug zu bauen, weshalb oft das Builder-Muster verwendet wird.

Das zweite Beispiel ist eine Variation des ersten Beispiels, das es ermöglicht, Informationen vom Builder mithilfe zusätzlicher Gettermethoden abzurufen. Diese Methoden werden jedoch nirgendwo verwendet, außer im Vehicle-Konstruktor, der direkt auf die Builder-Felder zugreifen kann. Ich sehe keinen Sinn darin, sie hinzuzufügen.

Ich sehe zwei wichtigere Dinge zu verbessern:

  1. Die beiden Builder-Typen tun genau das Gleiche. Es gibt keine Notwendigkeit für zwei Arten. Ein einziger ist ausreichend.
  2. Was die createVehicle()-Methode tut, sollte vom Builder-Konstruktor durchgeführt werden. Wenn Sie einen CarBuilder bauen, ist es offensichtlich, ein Auto zu bauen, also sollte der Typ des Fahrzeugs eingestellt werden, sobald der Erbauer gebaut wird. Hier ist, wie ich es schreiben würde:

.

public final class Vehicle { 

    private final String type; 
    private final int wheels; 

    private Vehicle(Builder builder) { 
     this.type = builder.type; 
     this.wheels = builder.wheels; 
    } 

    public static Builder carBuilder() { 
     return new Builder("car"); 
    } 

    public static Builder truckBuilder() { 
     return new Builder("truck"); 
    } 

    public static class Builder { 
     private final String type; 
     private int wheels; 

     private Builder(String type) { 
      this.type = type; 
     } 

     public Builder addWheels(int wheels){ 
      this.wheels = wheels; 
      return this; 
     } 

     public Vehicle build() { 
      return new Vehicle(this); 
     }    
    } 

    public static void main(String[] args) { 
     Vehicle car = Vehicle.carBuilder().addWheels(4).build(); 
     Vehicle truck = Vehicle.truckBuilder().addWheels(10).build(); 
    } 
} 
+0

Vielen Dank für das Hinweis auf "Unveränderlichkeit", ich denke, das war ich falsch. Ich dachte, das Builder-Muster wird überwiegend verwendet, um Schritte mit der Konstruktion von komplexen Objekten zu verbergen, aber Unveränderlichkeit ist keine zwingende Voraussetzung. Schätze, du hast mit example.cheers gepostet – Sudhakar

+0

JB Nizet: also wenn ich 'final' zu den Instanzvariablen in 'Example 1' führe, dann wird es ein gültiges BuilderPattern ergeben, oder? – Sudhakar

+0

Wenn Sie die Felder endgültig machen, wird es nicht mehr kompiliert, da Sie versuchen, den Wert der Felder vom Builder zu ändern. Ihre Builder * sind * Beispiele für das Builder-Muster. Aber der erste erlaubt keine Unveränderbarkeit und hat redundanten Code, und der zweite hat auch redundanten Code. Sie können beide verbessert werden, indem der Code verwendet wird, den ich in meiner Antwort gezeigt habe, der prägnanter, sicherer und unwandelbarer ist. –

2

Es gibt eine dritte Variante ist auch, mit weniger Code:

Statt ihre eigene Instanz mit Feldern der Erbauer könnte auch den Zustand Vehicle mutieren. Innere Klassen können private Mitglieder ihrer äußeren Klasse schreiben:

class Vehicle { 
    private int wheels; 

    private Vehicle() {} 

    public static class Builder { 
    private boolean building = true; 
    private Vehicle vehicle = new Vehicle(); 

    public Builder buildWheels(int wheels) { 
     if(!this.building) throw new IllegalStateException(); 
     this.vehicle.wheels = wheels; 
     return this; 
    } 

    public Vehicle build() { 
     this.building = false; 
     return this.vehicle; 
    } 
    } 
} 

Da die Felder privat sind und Sie erlauben es zu bauen sein nur einmal (building Flag), Vehicle Instanzen gebaut sind noch unveränderlich die Verbraucher selbst obwohl die Felder nicht mehr final sein können (nicht mehr realio-trulio immutability, see Eric's blog article was auf C# ist, aber die Konzepte sind ähnlich).

Sie müssen vorsichtiger sein, da nicht-finale Felder während der Objektkonstruktion nicht initialisiert werden müssen (vom Compiler erzwungen) und Sie müssen den building Zustand sorgfältig überprüfen. Sie speichern jedoch eine vollständige Extrakopie aller Instanzfelder. Im Allgemeinen ist dies nützlich, wenn Sie eine ziemlich große Gruppe von Instanzvariablen haben, die mit relativ wenigen Methoden erstellt werden, wobei jede Methode ein paar Felder gleichzeitig erstellt.

Ich weiß, dass dies keine Vorteile oder Nachteile Ihrer Ansätze hervorhebt. Dieser Ansatz kann jedoch viel zusätzlichen Code speichern, wenn Sie nicht die Felder final benötigen.

+0

Nein, die Vechicle-Instanzen sind nicht unveränderlich; Siehe diese Antwort: http://stackoverflow.com/a/6388762/262683 –

+0

@CostiCiudatu Danke für das Aufzeigen; In meinem ersten Beispiel habe ich das Flag "building" weggelassen (seine Bedeutung wurde unterschätzt). Bearbeitete meine Antwort. –

Verwandte Themen