引言
我们知道Java是纯粹的面向对象的语言,现实生活中的一切事物都是可以映射为对应的Java类对象,而往往许多时候事物往往十分复杂,由很多小部件组成,我们在创建对象的时候不仅需要考虑如何创建组成这个对象的小部件,还得去考虑怎么把这些部件全部组装到一起形成一个完整的对象,这就给后期的升级增加了成本,而且耦合度也较高,此时Builder模式应运而生。
一、Builder模式概述
建造者模式Buider(又叫生成器模式、构造者模式)是属于创建性的设计模式,主要用于一步一步创建一个较复杂的对象,通过使用Builder模式向用户隐藏内部构建的细节,专注于控制对象的组装流程,从而达到将构建复杂对象的过程和其他部件解耦,最终实现两个自由扩展构建流程和部件。
二、Builder模式的优点和缺点及常见可用场景
1、Builder模式的优点
- 良好的封装性和独立性,可以对客户隐藏产品内部的实现和组成细节
- 模块之间相对独立,耦合度不高利于程序扩展。
2、Builder模式的缺点
由于产品自身的组成复杂和组成流程复杂,可能会产生额外的副产品,消耗额外的内存资源,比如说额外的Builder对象和Director对象等。
3、适合使用Builder模式的场景
- 当产品类十分复杂或者产品类中的调用顺序不同产生了不同的作用。
- 相同的方法不同的执行顺序,产生不同的事件结果时。
- 一个对象由不定数量和种类的部件组成,不同的装配流程产生的对象不同时。
- 当初始化一个十分复杂的对象时,比如说参数多且许多参数都要求赋予默认值时
三、Builder模式的实现
1、隐藏构建细节,创建固定部件数量、固定部件组装流程的复杂对象
以构造Android和Iphone手机为例,我们把构造手机简化为三个部件和三个固定的组装流程 :装配CPU、装配手机屏幕、编译系统,由上类图可知经典的Builder模式一般有四个角色(但不一定Builder模式一定包含着四种角色):
- Director:统一管理装配过程
- Builder:抽象的Builder类,用于定义产品的组成,一般由子类去实现具体构建细节
- ConcreteBuilder:具体去实现构建工作
- Product:要构建的产品类
1.1、创建Product角色
由于此例中我们需要创建Android 智能手机和Iphone,两类手机拥有共性,只是细节不同,所以应该把他们的共性抽到抽象类中。
package builder;
/**
* 抽象Porduct类,充当Product角色
* @author cmo
*
*/
public abstract class AbstractPhoneProduct {
protected String cpu;
protected String screen;
protected String os;
public void setCpu(String cpu) {
this.cpu = cpu;
}
public void setScreen(String screen) {
this.screen = screen;
}
public abstract void setOs();
public String toString() {
return "PhoneProduc:"+" cpu:"+cpu+" screen:"+screen+" OS:"+os;
}
}
假如说我们要创建Android智能手机,则新建一个代表Android智能手机的实体类,创建Iphone的话也类似。
package builder;
/**
* 具体的产品,Android智能手机类,充当具体的Product角色
* @author cmo
*
*/
public class AndroidPhone extends AbstractPhoneProduct {
public void setOs() {
os="android 6.0";
}
}
1.2、创建Builder角色
手机的Builder抽象类,定义了构建手机的三个流程,此处也是基于后期扩展考虑,比如说创建Iphone的时候流程都差不多,不同于在于系统。(其实也可以不定义这个抽象Buidler类)
package builder;
/**
* 手机的Builder抽象类,定义了构建手机的三个流程
* @author cmo
*
*/
public abstract class AbstractPhoneBuilder {
public abstract void buildCpu(String cpu);
public abstract void buildSreen(String screen);
public abstract void buildOS();
public abstract AbstractPhoneProduct create();
}
1.3、创建Android手机时用到的ConcreteBuilder角色
package builder;
public class AndroidPhoneBuilder extends AbstractPhoneBuilder {
private AbstractPhoneProduct product=new AndroidPhone();
public void buildCpu(String cpu) {
product.setCpu(cpu);
}
public void buildSreen(String screen) {
product.setScreen(screen);
}
public void buildOS() {
product.setOs();
}
public AbstractPhoneProduct create() {
return product;
}
}
1.4、创建Director角色
在实际开发过程中,Director角色往往被省略,直接使用一个Builder来完成对象的组装。
package builder;
public class PhoneDirector {
AbstractPhoneBuilder builder=null;
public PhoneDirector(AbstractPhoneBuilder builder){
this.builder=builder;
}
/**
*统一规范组装流程
*/
public void construct(String cpu,String screen){
builder.buildCpu(cpu);
builder.buildSreen(screen);
builder.buildOS();
}
}
1.5、测试
package builder;
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
AbstractPhoneBuilder builder=new AndroidPhoneBuilder();
PhoneDirector director=new PhoneDirector(builder);
director.construct("骁龙835", "LCD");//Director 统一管理组装流程
AndroidPhone phone=(AndroidPhone) builder.create();//最后再通过Buidler生成复杂对象
System.out.print(phone.toString());
}
}
2、使用Builder模式创建组件数量不定、组装流程不定的复杂对象。
采用创建组件数量不定、组装流程不定的复杂对象时候,往往Director会被省略,Builder角色会去完成组件的创建和组装,而且一般可以采用链式调用,每个setter都要返回自身。以DIY汽车为例,假设一辆汽车可由车轮、车架、发动机、车载导航、车载电视、天窗等组成。
2.1、创建Product角色
该例中,我们都是创建汽车这个对象,所以为了简化就没有再进行抽象化处理了。
package builder2;
public class CarProduct {
protected String wheel;
protected String racks;
protected String engine;
protected String nav;
protected String window;
protected String tv;
public void setWheel(String wheel) {
this.wheel = wheel;
}
public void setRacks(String racks) {
this.racks = racks;
}
public void setEngine(String engine) {
this.engine = engine;
}
public void setNav(String nav) {
this.nav = nav;
}
public void setWindow(String window) {
this.window = window;
}
public void setTv(String tv) {
this.tv = tv;
}
}
2.2、创建Builder角色
package builder2;
public class CarBuilder {
CarParams params;
CarProduct product = new CarProduct();
public CarBuilder(CarParams param) {
this.params = param;
}
public CarBuilder setWheel(String wheel) {
params.wheel = wheel;
return this;
}
public CarBuilder setRacks(String racks) {
params.racks = racks;
return this;
}
public CarBuilder setEngine(String engine) {
params.engine = engine;
return this;
}
public CarBuilder setNav(String nav) {
params.nav = nav;
return this;
}
public CarBuilder setWindow(String window) {
params.window = window;
return this;
}
public CarBuilder setTv(String tv) {
params.tv = tv;
return this;
}
public CarProduct create() {
if (params.wheel != null) {
product.setWheel(params.wheel);
}
if (params.racks != null) {
product.setRacks(params.racks);
}
if (params.engine != null) {
product.setEngine(params.engine);
}
if (params.nav != null) {
product.setNav(params.nav);
}
if (params.window != null) {
product.setWindow(params.window);
}
if (params.tv != null) {
product.setTv(params.tv);
}
return product;
}
public static class CarParams {
protected String wheel;
protected String racks;
protected String engine;
protected String nav;
protected String window;
protected String tv;
public CarParams(String wheel, String racks, String engine, String nav,
String window, String tv) {
super();
this.wheel = wheel;
this.racks = racks;
this.engine = engine;
this.nav = nav;
this.window = window;
this.tv = tv;
}
}
}
2.3、测试
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
CarParams params=new CarParams("米其林", "BMW", "V8", "高德导航", "全景天窗", "互联网电视");//可以通过构建不同的CarParams对象来实现自由选择不同的参数
CarBuilder builder=new CarBuilder(params);
builder.create();
}
}
四、源码中变种使用的建造者模式Buidler——>Product
以上是所谓的标准模式建造者模式,虽然可以灵活创建各种复杂的对象,但是代码略过臃肿,而且单一类中职责臃肿,实际开发中很少会这样子使用(Android 源码中也没有这样子使用而是使用了更优雅的变种模式比如Dialog等),以使用变形建造者模式创建不同的快餐为例:
1、定义Product角色,在标准的建造者模式中,Product角色就相当于是一个JavaBean,所有的成员属性都是直接赋值,而在变种的模式中,Product是间接被赋值的。
import actualbuild.SnackBuilder.SnackBuilderParams;
//Product 角色,声明可能拥有的属性,以及应用属性"集"(因为创建这个产品不是每次都需要这些成员属性,所以把这些属性都封装到一个类中)
public class Snack {
private String rice;
private String meat;
private String beef;
private boolean isHot;
//真正给相应的成员属性设置值
public void apply(SnackBuilderParams params){
this.rice=params.rice;
this.meat=params.meat;
this.beef=params.beef;
this.isHot=params.isHot;
}
@Override
public String toString() {
return "外卖一份:"+rice +"、 "+meat+"、 "+beef+"、 是否加辣:"+isHot;
}
}
2、Builder角色
package actualbuild;
//现实开发和源码中采用的变种Build模式,Builder角色
public class SnackBuilder {
private SnackBuilderParams params;
public SnackBuilder() {
this.params=new SnackBuilder.SnackBuilderParams();
}
//把Product 对象的属性封装到一个类中
static class SnackBuilderParams{
public String rice;
public String meat;
public String beef;
public boolean isHot;
}
//返回自身,灵活实现链式调用
public SnackBuilder setRice(String rice){
params.rice=rice;
return this;
}
public SnackBuilder setMeat(String meat){
params.meat=meat;
return this;
}
public SnackBuilder setBeef(String beef){
params.beef=beef;
return this;
}
public SnackBuilder setIsHot(boolean isHot){
params.isHot=isHot;
return this;
}
//真正创建出Product
public Snack build() {
Snack snack=new Snack();
snack.apply(params);
return snack;
}
}
测试
public class Client {
public static void main(String[] args) {
SnackBuilder builder=new SnackBuilder().setRice("大米饭").setMeat("猪里脊").setBeef("牛肉");
Snack snack=builder.build();
System.out.println(snack.toString());
SnackBuilder builder2=new SnackBuilder().setRice("大米饭").setMeat("猪里脊").setIsHot(true);
Snack snack2=builder2.build();
System.out.println(snack2.toString());
}
}