Java设计模式之建造者模式
概论
什么是建造者模式呢?将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
建造者模式示例
我们先来回忆一下在模板模式中的 示例,[内外部系统交互]中的四个步骤:
第一步:参数校验
第二步:封装对外的请求参数
第三步:对外提交请求
第四步:后置处理,例如记录操作日志
最后核心的算法设计在run方法中。如下代码所示:
1 public void run (ExampleContext context) { 2 3 validate(context); 4 5 prepare(context); 6 7 proccess(context); 8 9 after(context); 10 }
客户端只要取调用run方法就可以。一切看来都很美好,但是如果我们想要把第一步和第二步交换执行顺序。或者把第二步舍弃。或者第三步和第四步交换执行顺序等等。
简单的描述一下问题:我们想要在一个算法中若干个步骤,客户端需要自定义步骤的顺序以及确定是否采用或者舍弃一些步骤。
此时需要用到建造者模式。首先我们需要有抽象产品类:
1 public abstract class AbstractProccessor { 2 3 private List<String> sequence = new ArrayList<String>(); 4 5 6 7 public boolean validate(ExampleContext context) { 8 if (context == null) { 9 return false; 10 } 11 12 return true; 13 } 14 15 public abstract void prepare(ExampleContext context); 16 17 public abstract void proccess(ExampleContext context); 18 19 public abstract void after(ExampleContext context); 20 21 protected boolean needAfterProccessing () { 22 return true; 23 } 24 25 26 public List<String> getSequence() { 27 return sequence; 28 } 29 30 public void setSequence(List<String> sequence) { 31 this.sequence = sequence; 32 } 33 34 public void run (ExampleContext context) { 35 36 for(String methodName : sequence) { 37 if(StringUtils.isEmpty(methodName)) { 38 continue; 39 } 40 41 Method method = null; 42 43 try { 44 method = this.getClass().getMethod(methodName, ExampleContext.class); 45 } catch (Exception e) { 46 47 } 48 49 if(method == null) { 50 continue; 51 } 52 53 try { 54 method.invoke(this, context); 55 } catch (Exception e) { 56 57 } 58 59 60 } 61 } 62 63 }
第3行:声明一个表示顺序的集合,集合中存储的是算法中每个步骤的方法名。类型为String类型。
第31行-61行:run方法通过遍历顺序集合的方式依次执行。通过反射的方式来调用。
接下来,和模板方法中的两个具体产品类完全一样,一个是HttpProccessor,另外一个是OtherProccessor。代码如下所示:
1 public class HttpProccessor extends AbstractProccessor { 2 3 protected boolean needAfterProccessing = false; 4 5 @Override 6 public void prepare(ExampleContext context) { 7 8 System.out.println("http 前置处理"); 9 10 } 11 12 @Override 13 public void proccess(ExampleContext context) { 14 15 System.out.println("http 提交请求"); 16 17 } 18 19 @Override 20 public void after(ExampleContext context) { 21 22 System.out.println("http 后置处理"); 23 24 } 25 26 }
1 public class OtherProccessor extends AbstractProccessor { 2 3 @Override 4 public void prepare(ExampleContext context) { 5 6 System.out.println("other 前置处理"); 7 8 } 9 10 @Override 11 public void proccess(ExampleContext context) { 12 13 System.out.println("other 提交请求"); 14 15 } 16 17 @Override 18 public void after(ExampleContext context) { 19 20 System.out.println("other 后置处理"); 21 22 } 23 }
最后我们需要修改一下我们的场景类Client:
1 public class Client { 2 3 public static void main(String[] args) { 4 5 AbstractProccessor proccessor = new HttpProccessor(); 6 7 List<String> list = new ArrayList<String>(); 8 list.add("prepare"); 9 list.add("proccess"); 10 proccessor.setSequence(list); 11 12 proccessor.run(new ExampleContext()); 13 14 15 } 16 }
第7行-10行:设置要执行的步骤以及顺序。
运行结果如下所示:
http 前置处理
http 提交请求
当我们需要很多种场景,是不是需要去写很多种负责的场景类?在建造者设计模式中引入一个很重要的角色--建造者。场景类需要什么顺序,只需要告诉建造者就行。
我们新增一个建造者抽象类AbtractorBuilder。
1 public abstract class AbstractBuilder { 2 3 public abstract void setSequence(List<String> list); 4 5 public abstract AbstractProccessor getProccessor(); 6 }
再新增一个HttpBuilder和OtherBuilder
public class HttpBuilder extends AbstractBuilder { private AbstractProccessor proccessor = new HttpProccessor(); @Override public void setSequence(List<String> list) { proccessor.setSequence(list); } @Override public AbstractProccessor getProccessor() { return proccessor; } }
public class OtherBuilder extends AbstractBuilder { private AbstractProccessor proccessor = new OtherProccessor(); @Override public void setSequence(List<String> list) { proccessor.setSequence(list); } @Override public AbstractProccessor getProccessor() { return proccessor; } }
继续修改一下场景类:
1 public class Client { 2 3 public static void main(String[] args) { 4 5 List<String> list = new ArrayList<String>(); 6 list.add("prepare"); 7 list.add("proccess"); 8 9 HttpBuilder httpBuilder = new HttpBuilder(); 10 httpBuilder.setSequence(list); 11 12 AbstractProccessor proccessor = httpBuilder.getProccessor(); 13 proccessor.run(new ExampleContext()); 14 15 } 16 }
但是我们的HttpBuilder还是在场景类中调用时,顺序还是不够灵活。 这时需要产生一个导演类,将所有预知的情况 组合的情况一一列出来。如下代码所示:
1 public class Director { 2 3 private List<String> sequence = new ArrayList<String>(); 4 private AbstractBuilder httpBuilder = new HttpBuilder(); 5 private AbstractBuilder otherBuilder = new OtherBuilder(); 6 7 public AbstractProccessor getHttpProccessorFirst() { 8 9 this.sequence.clear(); 10 sequence.add("prepare"); 11 12 httpBuilder.setSequence(sequence); 13 14 AbstractProccessor proccessor = httpBuilder.getProccessor(); 15 16 return proccessor; 17 } 18 19 public AbstractProccessor getHttpProccessorSecond() { 20 21 this.sequence.clear(); 22 sequence.add("prepare"); 23 sequence.add("after"); 24 25 httpBuilder.setSequence(sequence); 26 27 AbstractProccessor proccessor = httpBuilder.getProccessor(); 28 29 return proccessor; 30 } 31 32 public AbstractProccessor getOtherProccessorFirst() { 33 34 this.sequence.clear(); 35 sequence.add("prepare"); 36 37 otherBuilder.setSequence(sequence); 38 39 AbstractProccessor proccessor = otherBuilder.getProccessor(); 40 41 return proccessor; 42 } 43 44 public AbstractProccessor getOtherProccessorSecond() { 45 46 this.sequence.clear(); 47 sequence.add("prepare"); 48 sequence.add("after"); 49 50 otherBuilder.setSequence(sequence); 51 52 AbstractProccessor proccessor = otherBuilder.getProccessor(); 53 54 return proccessor; 55 } 56 }
最后我们在修改一下场景类:
1 public class Client { 2 3 public static void main(String[] args) { 4 5 AbstractProccessor proccessor = null; 6 7 Director director = new Director(); 8 proccessor = director.getHttpProccessorFirst(); 9 proccessor.run(new ExampleContext()); 10 11 proccessor = director.getHttpProccessorSecond(); 12 proccessor.run(new ExampleContext()); 13 14 15 proccessor = director.getOtherProccessorFirst(); 16 proccessor.run(new ExampleContext()); 17 18 proccessor = director.getOtherProccessorSecond(); 19 proccessor.run(new ExampleContext()); 20 21 22 23 } 24 }
简单而明了,笔者在开发的过程中,遇到很多的开发者不注意代码质量,甚至连资深的技术专家都没有意识到这一点。写下一行代码需要10秒钟,而看懂一行代码确实需要很大的力气。因此设计模式是用简单的方式将复杂的业务需求清晰的表达出来。
最后总结一下。建造者模式是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
在建造者模式中有4个角色,分别是产品类,抽象建造者,具体建造者,导演。
建造者模式的优点
1.良好的封装:场景类不必知道产品内部的细节。
2.建造者的独立,良好的扩展:存在不同的具体建造者,每个建造者之间独立。
3.良好的控制风险:因为具体的建造者是相互独立的,因此建造过程也是独立的,不会某一个建造者的变动对其他造成影响。