建造者模式

来源:互联网 发布:sunrise软件 编辑:程序博客网 时间:2024/06/08 19:17

建造者模式

定义

由于它是根据英文翻译而来,根据不同的翻译,建造者模式又可以称为生成器模式。它将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

​ 建造者模式是一步一步的来创建一个复杂的对象;该对象一般不会自己去构建具体的内容,而是通过属于自己建造者去构建;该建造者独立于其它的对象。

​ 我们在实际生活中开的汽车,就不是它自己组装的,它是通过汽车工人一步一步组装起来的;比如汽车一般会先完成汽车白身的焊接喷漆,然后安装发动机–安装底盘–安装线束–安装仪表板–安装内饰板–安装车门附件–安装发动机相关件–安装座椅–安装前后挡玻璃–安装其他附件—安装完成。一般在汽车制造企业会有一定安装顺序。当然不同的安装顺序会造成不同效果,比如你把其它的所有的东西都安装完了,然后再安装发动机,必然是不行的。

​ 在软件开发中,汽车工人就相当于我们的建造者,而我们想创建的对象就是汽车。我们通过建造者去创建一个我们想要的对象;并且创建步骤的不同会造成不同的结果,当然有好的结果,也有坏的结果。

组成角色

Product:我们创建/组装 的目标产品对象,我们需要通过建造者模式最终创建的对象。这个可以是抽象的也可以是具体的。

Builder:一般为抽象类或者接口,为了规范产品的创建,它会包含创建产品的步骤,然后由子类去实现每个步骤具体的行为。

ConcreteBuilder:Builder的子类;由它实现Builder类/接口的具体实现。

Director:指挥者。由它来安排产品的创建/组装的具体步骤。它就相当于汽车工厂的车间主任。

它的结构图如下:

时序图如下:

具体示例

我们以汽车为例,我们只列出其中几个关键的步骤。

我们会创建奔驰和宝马汽车。一夜之间成为高富帅。

  • 首先我们创建抽象的一个汽车类;用来抽取奔驰和宝马的共同特征;它是我们创建的产品的抽象类。

    public abstract class Car {  //品牌  protected String mBrand;  //发动机  protected String mEngine;  //底盘  protected String mUnderpan;  //方向盘  protected String mStreetingWheel;  //座椅  protected String mSeat;  public Car() {  }  public abstract void setBrand(String brand);  public void setEngine(String engine) {      mEngine = engine;  }  public void setSeat(String seat) {      mSeat = seat;  }  public void setStreetingWheel(String streetingWheel) {      mStreetingWheel = streetingWheel;  }  public void setUnderpan(String underpan) {      mUnderpan = underpan;  }  @Override  public String toString() {      return "Car{" +              "mBrand='" + mBrand + '\'' +              ", mEngine='" + mEngine + '\'' +              ", mUnderpan='" + mUnderpan + '\'' +              ", mStreetingWheel='" + mStreetingWheel + '\'' +              ", mSeat='" + mSeat + '\'' +              '}';  }}
  • 然后我们创建 奔驰和宝马车;它们是我们创建的具体产品

    public class BenzCar extends Car {  @Override  public void setBrand(String brand) {      this.mBrand = brand;  }}
    public class BmwCar extends Car {  @Override  public void setBrand(String brand) {      this.mBrand = brand;  }}
  • 其后我们创建抽象汽车建造者;由来抽取汽车建造的共同点

    public abstract class CarBuilder {  //安装品牌车标  public abstract void buildBrand(String brand);  //安装发动机  public abstract void buildEngine();  //安装底盘  public abstract void buildUnderpan();  //安装方向盘  public abstract void buildSteeringWheel();  //安装座椅  public abstract void buildSeat();  //最后完成安装  public abstract Car create();}
  • 接下来创建具体的奔驰和宝马建造者

    public class BenzCarBuilder extends CarBuilder {  Car mCar = new BenzCar();  @Override  public void buildBrand(String brand) {      mCar.setBrand(brand);  }  @Override  public void buildEngine() {      mCar.setEngine("奔驰发动机");  }  @Override  public void buildUnderpan() {      mCar.setUnderpan("奔驰底盘");  }  @Override  public void buildSteeringWheel() {      mCar.setStreetingWheel("奔驰方向盘");  }  @Override  public void buildSeat() {      mCar.setSeat("奔驰座椅");  }  @Override  public Car create() {      return mCar;  }}
    public class BmwCarBuilder extends CarBuilder {  Car mCar = new BmwCar();  @Override  public void buildBrand(String brand) {      mCar.setBrand(brand);  }  @Override  public void buildEngine() {      mCar.setEngine("宝马发动机");  }  @Override  public void buildUnderpan() {      mCar.setUnderpan("宝马底盘");  }  @Override  public void buildSteeringWheel() {      mCar.setStreetingWheel("宝马方向盘");  }  @Override  public void buildSeat() {      mCar.setSeat("宝马座椅");  }  @Override  public Car create() {      return mCar;  }}
  • 最后我们创建一个指挥者来建造不同车

    public class Director {  private CarBuilder mCarBuilder = null;      public Director(CarBuilder carBuilder) {      mCarBuilder = carBuilder;  }  public Car construct(String brand) {      mCarBuilder.buildBrand(brand);      mCarBuilder.buildEngine();      mCarBuilder.buildUnderpan();      mCarBuilder.buildSteeringWheel();      mCarBuilder.buildSeat();      return mCarBuilder.create();  }}

做完上面的事情,建造者模式其实已经完了,我们来测试一下:

public static void main(String[] args) {    CarBuilder bmwCarBuilder = new BmwCarBuilder();    Director bmwDirector = new Director(bmwCarBuilder);    Car bmwCar = bmwDirector.construct("宝马车标");    System.out.println(bmwCar);    CarBuilder benzCarBuilder = new BenzCarBuilder();    Director benzDirector = new Director(benzCarBuilder);    Car benzCar = benzDirector.construct("奔驰车标");    System.out.println(benzCar);}

输出结果为:

Car{mBrand=’宝马车标’, mEngine=’宝马发动机’, mUnderpan=’宝马底盘’, mStreetingWheel=’宝马方向盘’, mSeat=’宝马座椅’}

Car{mBrand=’奔驰车标’, mEngine=’奔驰发动机’, mUnderpan=’奔驰底盘’, mStreetingWheel=’奔驰方向盘’, mSeat=’奔驰座椅’}

这样我们就通过把汽车的创建过程拆分,并且使用不同的建造者建造了不同的汽车。

模式扩展

上面的建造者模式其实还是有些复杂,它还可以做一些简化,

  • 省略抽象建造者: 很多情况下是不需要抽象建造者的,所以我们只需要一个具体的建造者就OK。
  • 省略指挥者:既然没有抽象的建造者,指挥者存在的意义也就不大了,所以我们把指挥者的省略掉,并将其功能合并到具体建造者中。

我们看一个具体的例子,我们还是以汽车为例,这里我们以Jeep为例,我们只使用一个类来完成。

public class JeepCar extends Car {    public JeepCar() {    }    @Override    public void setBrand(String brand) {        this.mBrand = brand;    }    public static class Builder {        JeepCar mCar = new JeepCar();        public void buildBrand(String brand) {            mCar.setBrand(brand);        }        public void buildEngine() {            mCar.setEngine("Jeep 发动机");        }        public void buildUnderpan() {            mCar.setUnderpan("Jeep 底盘");        }        public void buildSteeringWheel() {            mCar.setStreetingWheel("Jeep 方向盘");        }        public void buildSeat() {            mCar.setSeat("Jeep座椅");        }        public JeepCar create() {            buildBrand("Jeep 车标");            buildEngine();            buildUnderpan();            buildSteeringWheel();            buildSeat();            return mCar;        }    }}

我们将具体的建造者放到了JeepCar 的内部作为内部类;并且将指挥者的功能合并到建造者的 create() 方法内。

下面我们来测试一下:

public static void main(String[] args) {    JeepCar.Builder builder = new JeepCar.Builder();    JeepCar jeepCar = builder.create();    System.out.println(jeepCar);}

输出为:

Car{mBrand=’Jeep 车标’, mEngine=’Jeep 发动机’, mUnderpan=’Jeep 底盘’, mStreetingWheel=’Jeep 方向盘’, mSeat=’Jeep座椅’}

上面的例子其实跟Android中的AlertDaliog 很像,它也是讲Builder作为内部类,并将创建过程来放到了Builder内部。如果我们需要自定义一个Dialog 的话可以参考一下:

比如我们自定义一个简单的LoadingDialog:

public class LoadingDialog extends Dialog {    private LoadingDialog(Context context) {        super(context);    }    public LoadingDialog(Context context, int theme) {        super(context, theme);    }    protected LoadingDialog(Context context, boolean cancelable, OnCancelListener cancelListener) {        super(context, cancelable, cancelListener);    }    public static class Builder {        private LoadingDialog mDialog = null;        private Activity mActivity;        private LayoutInflater mInflater;        private View mContentView;        public Builder(Activity activity) {            mActivity = activity;            mInflater = LayoutInflater.from(mActivity);        }        public LoadingDialog create() {            mDialog = new LoadingDialog(mActivity, R.style.LoadingDialog);            mContentView = mInflater.inflate(R.layout.haima_dialog_loading, null);            // 取消title占位            mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);            mDialog.setCancelable(false);            // 取消原生背景颜色            mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0));            mDialog.setCanceledOnTouchOutside(false);            mDialog.setContentView(mContentView);            return mDialog;        }    }}

我们可以这样使用:

LoadingDialog.Builder loadingBuilder = new LoadingDialog.Builder(activity);LoadingDialog loadingDialog = loadingBuilder.create();loadingDialog.show();//显示loadingDialog.dismiss();//取消显示

优点

  • 在建造者模式中,调用者/用户 不需要知道产品的内部组成,将产品本身和产品的创建过程解耦;甚至可以使得不同的创建过程可以创建不同的产品对象。
  • 每一个具体的创建者都是相对独立的。我们可以很方便的替换具体建造者,或者添加新的建造者;用户可以使用不同的建造者创建不同的产品对象
  • 建造者将产品的创建过程细化分解,可以更精细的控制产品的创建过程。
  • 增加新的具体建造者无须修改原有类库的代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合“开闭原则”。

缺点

  • 建造者模式一般具有较多的共同点,比如上面的汽车,各个汽车之间共同点很多。其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
  • 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。

使用场景

  • 需要创建的产品内部比较复杂,这些产品通常有多个成员属性
  • 需要生成的产品对象的属性相互依赖,需要制定其生成顺序;甚至不同的顺序会产生不同的结果
  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
  • 如果一个对象的在初始化的时候参数太多,也可以使用构造者模式。

本文参考:http://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/builder.html

本文中的结构图和时序图来自:http://design-patterns.readthedocs.io/zh_CN/latest/creational_patterns/builder.html

原创粉丝点击