7

私はEffectiveJavaの本を読み、将来の参考のためにメモを作成していましたが、BuilderPatternに出くわしました。

私はそれが何であるか、そしてそれがどのように使用されると想定されているかをよく理解しました。その過程で私はビルダーパターンの2つの例のバリエーションを作成しました。

それぞれの違いと利点をリストアップするのに助けが必要ですか?確かに私は、Example 1公開するメソッドが少なく、制限が少なく、より一般的であることに気づきました。これにより、より柔軟に使用できるようになります。

私が見逃した他のことを指摘してください?

例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();

    }
}

例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();
    }
}
4

2 に答える 2

9

上記のどれでもない。

最初のものは不変の車両を構築することを許可していません。それがしばしばビルダーパターンが使用される理由です。

2番目の例は、最初の例のバリエーションであり、追加のゲッターメソッドを使用してビルダーから情報を取得できます。ただし、これらのメソッドは、ビルダーフィールドに直接アクセスできるVehicleコンストラクターを除いて、どこでも使用されていません。それらを追加する意味がわかりません。

改善すべきもう2つの重要なことがわかります。

  1. 2つのビルダータイプはまったく同じことをします。2種類は必要ありません。1つで十分です。
  2. createVehicle()メソッドが行うことは、ビルダーコンストラクターによって実行される必要があります。CarBuilderを構築する場合、それは明らかに車を構築することなので、ビルダーが構築されたらすぐに車両のタイプを設定する必要があります。これが私がそれを書く方法です:

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();
    }
}
于 2013-03-17T13:36:13.317 に答える
3

コードが少ない3番目のバリアントもあります。

独自のインスタンスフィールドを持つ代わりに、ビルダーはの状態を変更することもできますVehicle。内部クラスは、外部クラスのプライベートメンバーを作成できます。

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

フィールドはプライベートであり、一度だけビルドできるようにするため(buildingフラグ)、フィールドを作成できなくなっても、ビルドされたVehicleインスタンスはコンシューマーに対して不変finalです(realio-trulioの不変性はなくなります。C#にあるEricのブログ記事を参照してください。概念は似ています)。

非最終フィールドはオブジェクトの構築中に初期化する必要がないため(コンパイラーによって強制される)、より注意する必要があり、building状態を注意深くチェックする必要があります。ただし、すべてのインスタンスフィールドの完全な追加コピーを保存します。一般に、これは、各メソッドが一度にいくつかのフィールドを構築する、かなり少数のメソッドで構築されるインスタンス変数のかなり大きなセットがある場合に役立ちます。

これはあなたのアプローチの長所や短所を指摘していないことを私は知っています。ただし、このアプローチでは、フィールドをである必要がない場合、多くの余分なコードを節約できますfinal

于 2013-03-17T13:35:44.020 に答える