引言
之前在家看两个小外甥玩轨道车,拆开包装,一堆小零件,兄弟两一个拼桥梁、弯道、路标,一个装车、搭立交、组装上下坡。不一会儿轨道就全拼好了,两兄弟用代表自己的车子在轨道上追逐,玩的很开心。我看了下轨道车包装,根据使用零件多少不同,组拼顺序不同,摆放不同可以创建不同的轨道和街道,有椭圆形的,上下立交式的,单层的……
忽然想到用程序来表述玩轨道车的流程,如果用类图描述轨道车的玩法,可以简单表示为:
分为两个部分:
红色为轨道车的各个部件,Road规定了轨道车可以有 坡度、弯道、桥梁、路标、汽车、立交这几个部分;
蓝色部分为建造不同的轨道,环形轨道(AnnularBuilder)和立交轨道(InterchangeBuilder)。
Road为模板方法的变形形式,轨定轨道车的部件和各个部件的组装顺序,实现如下:
public abstract class Road { //坡度 protected abstract void slope(); //弯道 protected abstract void curve(); //桥梁 protected abstract void bridge(); //路标 protected abstract void guide(); //汽车 protected abstract void car(); //立交 protected abstract void interchange(); private List<String> list = new ArrayList<>(); final public void create() throws InvocationTargetException, IllegalAccessException { Method[] methods = this.getClass().getDeclaredMethods(); for (String method : list) { for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(method)) { methods[i].invoke(this, null); } } } } final public void setList(List<String> methods){ this.list = methods; } }
组装轨道车的部件实现方法:
public class TrackRoad extends Road { //坡度 @Override protected void slope() { System.out.println("建造上下坡……"); } //弯道 @Override protected void curve() { System.out.println("建造曲线车道……"); } //桥梁 @Override protected void bridge() { System.out.println("建造桥梁……"); } //路标 @Override protected void guide() { System.out.println("放置公路路标……"); } //汽车 @Override protected void car() { System.out.println("建造汽车……"); } //立交 @Override protected void interchange() { System.out.println("建造立交……"); } }
抽象创建轨道车类Builder定义了轨道车的组装部件和获取组装的轨道车
public abstract class Builder { /** * 不同部件的创建 */ public abstract void setPart(List<String> methods); /** * 建造轨道 */ public abstract TrackRoad buildRoad() throws InvocationTargetException, IllegalAccessException; }
具体轨道车玩法建造类(立交轨道车):
public class InterchangeBuilder extends Builder { private TrackRoad road = new TrackRoad(); @Override public void setPart(List<String> methods) { this.road.setList(methods); } @Override public TrackRoad buildRoad() { return this.road; } }
如果想创建一个立交轨道,可以这么创建:
InterchangeBuilder interchangeBuilder = new InterchangeBuilder(); List<String> interMethods = new ArrayList<>(); interMethods.add("slope"); interMethods.add("bridge"); interMethods.add("guide"); interMethods.add("interchange"); interMethods.add("car"); interchangeBuilder.setPart(interMethods); interchangeBuilder.buildRoad().create();
建造上下坡……
建造桥梁……
放置公路路标……
建造立交……
建造汽车……
引入导演类
坡度、弯道、桥梁、路标、汽车、立交不同组装方式可以构造不同的轨道,为了方便支持的很多不同的轨道,可以增加个导演类(Director)。
导演类封装立交轨道和环形轨道的实现,对外提供直接获取的方法:
public class Director { private List<String> steps = new ArrayList<>(); private InterchangeBuilder interchangeBuilder = new InterchangeBuilder(); private AnnularBuilder annularBuilder = new AnnularBuilder(); public TrackRoad getInterchangeBuilder(){ System.out.println("===========================建造立交车道==========================="); this.steps.clear(); this.steps.add("slope"); this.steps.add("bridge"); this.steps.add("guide"); this.steps.add("interchange"); this.steps.add("car"); this.interchangeBuilder.setPart(this.steps); return this.interchangeBuilder.buildRoad(); } public TrackRoad getAnnularBuilder(){ System.out.println("===========================建造曲线车道===========================");
this.steps.clear(); this.steps.add("curve"); this.steps.add("bridge"); this.steps.add("guide"); this.steps.add("car"); this.annularBuilder.setPart(this.steps); return this.annularBuilder.buildRoad(); } }
客户端不关注如何实现,只需要拿来即用(可玩):
public void testDerictor() throws InvocationTargetException, IllegalAccessException { Director director = new Director(); director.getAnnularBuilder().create(); director.getInterchangeBuilder().create(); }
其实这就是建造者模式,由导演类决定如何构建具体的对象(产品)。
建造者模式
定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
通用类图
四要素
建造者模式中有以下4个角色:
Product产品类
通常是实现模板方法模式(有模板方法和基本方法的类),例子里的TrackRoad(轨道)就属于产品类。
Builder抽象建造者
规范产品的组建,一般是抽象类,约定功能由子类去实现具体的建造方法,对应例子里的Builder。
ConcreteBuilder具体建造者
实现抽象建造者的所有方法。例子里的InterchangeBuilder和AnnularBuilder就是具体轨道的建造者。
Director导演类
封装具体建造者,提供简单易用的构建产品类方法。
总结
优点
封装性:将产品本身与产品创建过程进行解耦,可以使用相同的创建过程来得到不同的产品。也就说细节依赖抽象。
建造者独立,容易扩展:增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则“。
缺点
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
使用场景
- 相同的方法,不同的执行顺序,产生不同的事件结果时
- 多个部件或零件都可以装配到一个对象中,但是产生的运行结果又不相同时
- 产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能