建造者模式
对于建造者模式,我们首先来说明建造者模式是用来干嘛的。建造模式一般用于创建复杂对象,这些复杂对象的构建过程是稳定的,但是内部的构件通常要面临比较复杂的变化。怎么来解释呢?我们利用《大话设计模式》中的例子来解释,创建一个胖子和一个瘦子,我们需要画出头、手、脚、身体这几个部分,最没水平的写法是写两个类,一个胖子类,一个瘦子类,这不像我们一门面向对象语言所写出来的代码。通常我们可能会抽象出一个创建人的接口,并有画出头、手、脚、身体这几个抽象方法,胖子、瘦子分别来实现这几个方法,但是我们还需要将这几个方法组装起来,建造者模式利用一个指挥者类来控制建造过程,但是集绕我们的建造过程是稳定的,我们是否可以在我们出来的创建人接口修改为抽象类,定义一个建造方法呢?咦,写到这个地方,我发现好像建造者模式是不是把问题复杂化了?我们来通过这两周方法来对比看看。
我们先来看看我们通常所做的方法来实现这个创建胖子、瘦子的需求:
第一种方法
创建人的抽象类:
1 package day_11_createPerson; 2 3 /** 4 * 创建人的抽象类 5 * @author turbo 6 * 7 * 2016年9月16日 8 */ 9 public abstract class Person { 10 public abstract void head(); 11 public abstract void arm(); 12 public abstract void leg(); 13 public abstract void body(); 14 15 public void createPerson(){ 16 head(); 17 arm(); 18 leg(); 19 body(); 20 } 21 }
胖子、瘦子对于头等的建造细节由它们自己去实现,因为小人的建造过程是问题的,所以这里提供一个createPerson具体方法。
下面是胖子、瘦子的实现:
1 package day_11_createPerson; 2 3 /** 4 * 建造胖子 5 * @author turbo 6 * 7 * 2016年9月16日 8 */ 9 public class Fat extends Person { 10 11 /* (non-Javadoc) 12 * @see day_11_createPerson.Person#head() 13 */ 14 @Override 15 public void head() { 16 System.out.println("创建胖子的头"); 17 } 18 19 /* (non-Javadoc) 20 * @see day_11_createPerson.Person#arm() 21 */ 22 @Override 23 public void arm() { 24 System.out.println("创建胖子的手"); 25 } 26 27 /* (non-Javadoc) 28 * @see day_11_createPerson.Person#leg() 29 */ 30 @Override 31 public void leg() { 32 System.out.println("创建胖子的脚"); 33 } 34 35 /* (non-Javadoc) 36 * @see day_11_createPerson.Person#body() 37 */ 38 @Override 39 public void body() { 40 System.out.println("创建胖子的身体"); 41 } 42 43 }
1 package day_11_createPerson; 2 3 /** 4 * 建造瘦子 5 * @author turbo 6 * 7 * 2016年9月16日 8 */ 9 public class Thin extends Person { 10 11 /* (non-Javadoc) 12 * @see day_11_createPerson.Person#head() 13 */ 14 @Override 15 public void head() { 16 System.out.println("创建瘦子的头"); 17 } 18 19 /* (non-Javadoc) 20 * @see day_11_createPerson.Person#arm() 21 */ 22 @Override 23 public void arm() { 24 System.out.println("创建瘦子的手"); 25 } 26 27 /* (non-Javadoc) 28 * @see day_11_createPerson.Person#leg() 29 */ 30 @Override 31 public void leg() { 32 System.out.println("创建瘦子的脚"); 33 } 34 35 /* (non-Javadoc) 36 * @see day_11_createPerson.Person#body() 37 */ 38 @Override 39 public void body() { 40 System.out.println("创建瘦子的身体"); 41 } 42 43 }
客户端测试代码:
1 package day_11_createPerson; 2 3 /** 4 * @author turbo 5 * 6 * 2016年9月16日 7 */ 8 public class Main { 9 10 /** 11 * @param args 12 */ 13 public static void main(String[] args) { 14 Fat fat = new Fat(); 15 fat.createPerson(); 16 17 Thin thin = new Thin(); 18 thin.createPerson(); 19 } 20 21 }
第一种方法简单易懂,并且满足了我们了需求,那第二种利用建造者模式呢?
第二种方法
这里我们要实现一个Product类,这个额外的Product类是用来干嘛的呢?在这里我们的Product代表的就是创建出来的具体的胖子、瘦子“产品”。它由多个部件构成,也就是头、手、腿、身体。
1 package day_11_builder; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /** 7 * 产品类——由多个部件组成 8 * @author turbo 9 * 10 * 2016年9月16日 11 */ 12 public class Product { 13 private List<String> parts = new ArrayList<String>(); 14 15 public void add(String part){ 16 parts.add(part); 17 } 18 19 public void show(){ 20 for (String part : parts){ 21 System.out.println(part); 22 } 23 } 24 }
接着我们同样有一个建造者,这里是接口,只负责提供创建人的头等的方法,建造过程不由建造者实现,而是由另一个类指挥者(Director)来实现。
1 package day_11_builder; 2 3 /** 4 * 建造者接口 5 * @author turbo 6 * 7 * 2016年9月16日 8 */ 9 public interface Builder { 10 void head(); 11 void arm(); 12 void leg(); 13 void body(); 14 Product getResult(); 15 }
我们来实现这个建造者接口:
1 package day_11_builder; 2 3 /** 4 * @author turbo 5 * 6 * 2016年9月16日 7 */ 8 public class FatBuilder implements Builder { 9 private Product product = new Product(); 10 11 /* (non-Javadoc) 12 * @see day_11_builder.Builder#head() 13 */ 14 @Override 15 public void head() { 16 product.add("创建胖子的头"); 17 } 18 19 /* (non-Javadoc) 20 * @see day_11_builder.Builder#arm() 21 */ 22 @Override 23 public void arm() { 24 product.add("创建胖子的手"); 25 } 26 27 /* (non-Javadoc) 28 * @see day_11_builder.Builder#leg() 29 */ 30 @Override 31 public void leg() { 32 product.add("创建胖子的腿"); 33 } 34 35 /* (non-Javadoc) 36 * @see day_11_builder.Builder#body() 37 */ 38 @Override 39 public void body() { 40 product.add("创建胖子的身体"); 41 } 42 43 /* (non-Javadoc) 44 * @see day_11_builder.Builder#getResult() 45 */ 46 @Override 47 public Product getResult() { 48 return product; 49 } 50 51 }
1 package day_11_builder; 2 3 /** 4 * 具体建造者2 5 * @author turbo 6 * 7 * 2016年9月16日 8 */ 9 public class ThinBuilder implements Builder { 10 private Product product = new Product(); 11 12 /* (non-Javadoc) 13 * @see day_11_builder.Builder#head() 14 */ 15 @Override 16 public void head() { 17 product.add("创建瘦子的头"); 18 } 19 20 /* (non-Javadoc) 21 * @see day_11_builder.Builder#arm() 22 */ 23 @Override 24 public void arm() { 25 product.add("创建瘦子的手"); 26 } 27 28 /* (non-Javadoc) 29 * @see day_11_builder.Builder#leg() 30 */ 31 @Override 32 public void leg() { 33 product.add("创建瘦子的腿"); 34 } 35 36 /* (non-Javadoc) 37 * @see day_11_builder.Builder#body() 38 */ 39 @Override 40 public void body() { 41 product.add("创建瘦子的身体"); 42 } 43 44 /* (non-Javadoc) 45 * @see day_11_builder.Builder#getResult() 46 */ 47 @Override 48 public Product getResult() { 49 return product; 50 } 51 52 }
建造者方法中有一个getResult方法,返回一个Product类型,也就是返回一个具体的产品,具体的胖子还是瘦子。那么我们的建造过程去哪儿了呢?上面提到了由一个指挥者来指挥建造过程。
1 package day_11_builder; 2 3 /** 4 * 指挥者——指挥具体的建造过程 5 * @author turbo 6 * 7 * 2016年9月16日 8 */ 9 public class Director { 10 public void construct(Builder builder){ 11 builder.head(); 12 builder.arm(); 13 builder.leg(); 14 builder.body(); 15 } 16 }
建造者模式将建造过程和实现组件的具体细节相分离。客户端测试代码:
1 package day_11_builder; 2 3 /** 4 * 客户端测试 5 * @author turbo 6 * 7 * 2016年9月16日 8 */ 9 public class Main { 10 public static void main(String[] args){ 11 Director director = new Director(); 12 Builder b1 = new FatBuilder(); 13 Builder b2 = new ThinBuilder(); 14 15 director.construct(b1); 16 Product product1 = b1.getResult(); 17 product1.show(); 18 19 director.construct(b2); 20 Product product2 = b2.getResult(); 21 product2.show(); 22 } 23 }
这样我们就用建造者模式实现了上面创建一个胖子和瘦子的需求。
但是这两种方法到底有什么不同呢?第一种方法确实简单易懂。第二种方法将建造小人各个部分即Builder、具体小人Product、构建过程Director相互分离。到底是第一种方法简单易读好?还是第二种利用建造者模式好呢?
其实我认为在程序不复杂的情况下完全可以使用第一种方法,但是在程序开始的时候不都是简单的么?不都是因为不断更改的需求导致初期设计不佳导致代码冗余庞大不可维护的么?设计模式之所以存在必然有它存在的原因,或因良好的可扩展性或因良好的可维护性等等,模式是死的,人是活的,灵活应用设计模式,写出灵活的代码,这才是本质。