创建一个对象,有时候是很复杂的一件事,比如在模板方法模式中讲到的Car对象,创建它,就需要装配底盘、轮胎、发动机、车身等部件,甚至之后还需要喷漆等工序。模版方法模式,主要解决的是算法中不变部分与可变部分的解耦,将可变部分算法推迟到子类中去实现;而本文要介绍的建造者模式,则是一种创建模式,顾名思义,就是为了创建对象的设计模式。
1.建造者模式
建造者模式(Builder Pattern),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的对象。
所谓对象的构建与它的表示分离,就是创建一个对象的实例和成员变量的赋值相分离,因此,不同的建造者子类根据不同的赋值就可以创建出不同的对象。
实际上一个复杂对象就是一些“部件”的聚合体,建造者模式中,通常需要一个“指挥者”Director来组织管理对每一部分的构建,以组合成一个完整的对象,再将这个对象返回给客户。
建造者模式的UML类图如下所示:
Builder : 建造者抽象父类或者接口,定义创建对象各个“部件”的接口;
ConcreteBuilder: 建造者的具体实现类,可以有不同实现,不同实现返回不同对象;
Product: 建造者对象返回的最终产品(即由各个“部件”组合而成的完整对象);
Director: 指挥者,充当客户的接口,调用建造者的接口进行“部件”的建造和“组装”,将完整对象返回给客户。符合迪米特原则。
2.代码实现
汽车类,chassis底盘轴距, engine发动机功率, body车身颜色
/** * 汽车类,装配对象 */ class Car { private static int count = 0; private final int id; public Car() { id = count++; } /** 汽车底盘:轴距 */ private int chassis; /** 发动机: 功率*/ private double engine; /** 车身: 颜色 */ private String body; /** 安装底盘*/ public void setChassis(int wheelBase){ chassis = wheelBase; } /** 安装发动机*/ public void setEngine(double power){ engine = power; } /** 安装车身 */ public void setBody(String color) { body = color; } public int getChassis() { return this.chassis;} public double getEngine() { return this.engine;} public String getBody() {return this.body;} public String toString() { return "car" + id + "( chassis:" + chassis + ", engine:" + engine + ", body:" + body +")" ; } }
汽车建造者抽象类,结合模版方法模式, build方法实际就是模版方法。而chassis,engine,body则由具体子类实现。
/** 抽象建造者类 */ abstract class CarBuilder { /** 建造对象的"模板" */ protected Car carTemp; /** Builder对象*/ public CarBuilder() { carTemp = new Car(); } /** * 将建造者中的汽车模板中的属性取出来赋值给实际的Car对象 * 实际上,setChassis等是不需要的,因为这里没有参数 * 如果有参数,就需要这些方法 */ public Car build() { Car car = new Car(); car.setChassis(carTemp.getChassis()); car.setEngine(carTemp.getEngine()); car.setBody(carTemp.getBody()); return car; } /** 装配底盘 */ public abstract CarBuilder buildChassis(); /** 装配发动机 */ public abstract CarBuilder buildEngine(); /** 装配车身 */ public abstract CarBuilder buildBody(); }
低配车建造者
/** * 建造低配汽车的建造者 */ class LowCarBuilder extends CarBuilder { @Override public CarBuilder buildChassis() { carTemp.setChassis(4200); return this; //这样写可以实现对象方法的"链式调用" } @Override public CarBuilder buildEngine() { carTemp.setEngine(150.0); return this; } @Override public CarBuilder buildBody() { carTemp.setBody("Block"); return this; } }
高配车建造者
/** * 建造高配汽车的建造者 */ class HighCarBuilder extends CarBuilder { @Override public CarBuilder buildChassis() { carTemp.setChassis(4250); return this; //这样写可以实现对象方法的"链式调用" } @Override public CarBuilder buildEngine() { carTemp.setEngine(180.0); return this; } @Override public CarBuilder buildBody() { carTemp.setBody("White"); return this; } }
指挥者,作为客户调用建造者的实际接口,持有CarBuilder的引用,这里使用简单工厂模式,根据客户所传type来决定实例化哪一个Builder
/** * 指挥者 */ class Director { private CarBuilder builder; private static HashMap<String, Class<? extends CarBuilder>> map = new HashMap<>(); static { // 实际项目中可以扫描某包下的相关对象注册class map.put("low", LowCarBuilder.class); map.put("high", HighCarBuilder.class); } private Car invoke() { return builder.buildBody() .buildChassis() .buildEngine() .build(); } public Car build(String type) throws IllegalAccessException, InstantiationException { builder = map.get(type).newInstance(); return invoke(); } }
客户调用
public class BuilderDemo { public static void main(String[] args) throws InstantiationException, IllegalAccessException { Director director = new Director(); Car car = director.build("low"); System.out.println(car); car = director.build("high"); System.out.println(car); } }
输出结果,这里car1,car3,数字用来统计创建了多少Car实例,因为在建造者中创建了两个carTemp为car0和car2
car1( chassis:4200, engine:150.0, body:Block)
car3( chassis:4250, engine:180.0, body:White)
3.总结
建造者模式的意图是把构建复杂对象的逻辑分离出来,分别创建复杂对象的某个部分,然后将它们装配起来形成一个完整的复杂对象。实际开发过程中,笔者公司大量使用了lombok中的@Builder注解配合@Data,使创建DTO时自动实现了建造者模式,好处是你可以决定创建出什么样子的DTO对象而无需实现创建逻辑。还有一个地方会经常用到创建者模式,就是在做一些格式转换之类的工作时,往往创建一个Converter父类,然后根据实际业务需求,不同的子Converter类进行不同的格式转换,通常还需要一个Director或者叫Manager之类名称的类,来调用Converter,如果要实现动态地转换,在Director中还可能用到注册方式的简单工厂方法创建具体的Converter,结合创建者模式对格式进行转换。
总之,不同的设计模式要多学多用,很多时候它们不是孤立存在而是相生相伴的,这也许就是设计模式最困难,最需要大量实践经验之处吧。