模拟场景:在上次的模板设计中,我们根据不同的需求建立有不同特性的car,现在一辆car有什么特性都是由我们内部自己决定的,但是现在有新的要求:所有car的特性都由客户自己决定。比如现在要求要建立BMV,但是BMW也有不同的型号,假如型号A要求只需要start和stop方法,型号B要求start、stop、alarm,型号C先start、engineBoom、stop,型号D。。。怎么根据这些不同的需求来建立重用性高的代码?
分析:现在既然有不同需求,而且有顺序的要求了,那么就不可以像之前在CarModel中写死了顺序,这样达不到我们的要求。既然有顺序,那么我们可以在CarModel中实例化一个List<String>容器,主要用来装特性执行的顺序,在run方法中循环取出,进行迭代判断,先匹配的先进行执行,不匹配的不执行。
CarModel
1 package com.zqz.dp.builder; 2 import java.util.ArrayList; 3 import java.util.List; 4 /** 5 * @author Qin 生产car的模型,所有的才都按照此模板进行设计 6 */ 7 public abstract class CarModel { 8 /** 9 * 定义一个list<String>容器,用来装car跑动时候是否执行,执行的顺序。 10 * 封装好,提供getter、setter 11 */ 12 private List<String> orders=new ArrayList<String>(); 13 protected List<String> getOrders() { 14 return orders; 15 } 16 protected void setOrders(List<String> orders) { 17 this.orders = orders; 18 } 19 /** 20 * 首先car要可以启动起来 21 */ 22 protected abstract void start(); 23 /** 24 * 除了可以开动,car也要可以停止下来 25 */ 26 protected abstract void stop(); 27 /** 28 * car还要可以鸣笛 29 */ 30 public abstract void alarm(); 31 /** 32 * car还要有引擎 33 */ 34 protected abstract void engineBoom(); 35 /** 36 * 定义完规定之后,car要开始进行建立模型,下面就是进行具体的操作。设置为final是为了防止子类破坏进行覆写 37 */ 38 protected final void run() { 39 for(String order:orders){ //forEach循环,取出每一个order 40 if(order!=null){ //值不为空 41 if(order.equalsIgnoreCase("start")){ //如果start匹配成功 42 this.start(); // car开始启动 43 }else if(order.equalsIgnoreCase("engine boom")){ //如果engineBoom匹配成功 44 this.engineBoom(); // 引擎启动 45 }else if(order.equalsIgnoreCase("alarm")){ //如果alarm匹配成功 46 this.alarm(); // 开始鸣笛 47 }else if(order.equalsIgnoreCase("stop")){ //如果shop匹配成功 48 this.stop(); // car停下来 49 } 50 } 51 } 52 } 53 }
这样顺序的问题已经解决了,如果想要测试的话,可以在client端建立一个list容器,装上car的需要的特性与顺序,然后通过setter方法设置到自己的car中就可以了。
修改的Client
1 package com.zqz.dp.builder.model; 2 import java.util.ArrayList; 3 import java.util.List; 4 /** 5 * @author Qin 6 * 场景类,主要是按照模板CarModel生成不同的car,并开动起来 7 */ 8 public class Client { 9 public static void main(String[] args) { 10 System.out.println("-----------建立第一辆car-------------"); 11 CarModel car1=new Car1(); //根据模板建立第一辆car1 12 List<String> orders=new ArrayList<String>(); //建立一个容器来装执行的特性及顺序 13 orders.add("start"); //有start的方法 14 orders.add("stop"); //有stop的方法 15 car1.setOrders(orders); //设置到car1中 16 car1.run(); //开始建立car,让car跑动起来 17 } 18 }
这样确实可以解决问题了,但是否会发现,main方法中的代码太多了,而且如果现在要求有好几种不同型号的car,是否在main方法中要建立多个这样的list容器呢?这样明确是不符合的。所以这个时候用面向对象的知识,就要把这个list容器的全部操作抽象出来,成为独立的一个类。
在这里,因为是car的建造模型,所以把这个类抽象为CarBuilder,表示用来创建不同的模型的car,那么很明显,这个car肯定有一个setOrders(List<String>),这个方法是用来设置模型的特性及特性的顺序的,当我们建立好模型之后,肯定car就算建立好了,所以在CarBuilder中也应该有一个方法getCarMedol(),表示得到car的模板,当然设置什么模型,得到什么样模板,都是交给子类去实现,所以全部可以定义为abstract。在这里有人会觉得矛盾说,直接把setOrders(List<String>)放在CarModel不是也一样可以实现吗?其实是可以的,不过这样的话CarModel即负责car的特性又关系car的各种特性的执行顺序,兼顾的职责太多了。根据单一职责原则,这里还是构建出一个CarBuilder类会好点,该类只负责CarModel中car的有什么特性且特性的执行顺序。
认真的读者应该会发现,这里面虽然抽象出来了一个CarBuilder类,但是在main方法中还是存在着大量的代码。car的定制规则都写在main方法中,这样如果我有几百种建造模型的话,是否在main方法中会存在一大堆代码呢?当然你也可以定义成静态的方法来调用,但是这样的操作还不是很符合逻辑的。
最好的方法时再抽象出一个Director类,此类的意思是导演类,用来控制生成car的不同的模型,在这里生成了BMWCar的A、B型号,BenzCar的A型号。其实如果型号不多的话,可以把Director设计成一个单例设计模式的,但是在这里考虑到拓展,如果类型太多的话,可以衍生出一些子类,如BMWDirector等等。紧接着Client也要进行修改,会发现在main方法中变得非常的简洁。
修改的CarModel
1 package com.zqz.dp.builder.model; 2 import java.util.ArrayList; 3 import java.util.List; 4 /** 5 * @author Qin 6 * 生产car的模型,所有的才都按照此模板进行设计 7 */ 8 public abstract class CarModel { 9 /** 10 * 定义一个list<String>容器,用来装car跑动时候是否执行,执行的顺序。 11 * 封装好,提供getter、setter 12 */ 13 private List<String> orders=new ArrayList<String>(); 14 public List<String> getOrders() { 15 return orders; 16 } 17 public void setOrders(List<String> orders) { 18 this.orders = orders; 19 } 20 /** 21 * 首先car要可以启动起来 22 */ 23 protected abstract void start(); 24 /** 25 * 除了可以开动,car也要可以停止下来 26 */ 27 protected abstract void stop(); 28 /** 29 * car还要可以鸣笛 30 */ 31 public abstract void alarm(); 32 /** 33 * car还要有引擎 34 */ 35 protected abstract void engineBoom(); 36 /** 37 * 定义完规定之后,car要开始进行建立模型,下面就是进行具体的操作。设置为final是为了防止子类破坏进行覆写 38 */ 39 public final void run() { 40 for(String order:orders){ //forEach循环,取出每一个order 41 if(order!=null){ //值不为空 42 if(order.equalsIgnoreCase("start")){ //如果start匹配成功 43 this.start(); // car开始启动 44 }else if(order.equalsIgnoreCase("engine boom")){ //如果engineBoom匹配成功 45 this.engineBoom(); // 引擎启动 46 }else if(order.equalsIgnoreCase("alarm")){ //如果alarm匹配成功 47 this.alarm(); // 开始鸣笛 48 }else if(order.equalsIgnoreCase("stop")){ //如果shop匹配成功 49 this.stop(); // car停下来 50 } 51 } 52 } 53 } 54 }
BMWCar
1 package com.zqz.dp.builder.car; 2 import com.zqz.dp.builder.model.CarModel; 3 /** 4 * @author Qin 5 * 第一辆按照CarModel建立的car1 6 */ 7 public class BMWCar extends CarModel { 8 @Override 9 public void start() { 10 System.out.println("bmw启动"); 11 } 12 @Override 13 public void stop() { 14 System.out.println("bmw停下来"); 15 } 16 @Override 17 public void alarm() { 18 System.out.println("bmw鸣笛"); 19 } 20 @Override 21 public void engineBoom() { 22 System.out.println("bmw启动引擎"); 23 } 24 }
1 package com.zqz.dp.builder.car; 2 import com.zqz.dp.builder.model.CarModel; 3 /** 4 * @author Qin 5 * 第一辆按照CarModel建立的car1 6 */ 7 public class BMWCar extends CarModel { 8 @Override 9 public void start() { 10 System.out.println("bmw启动"); 11 } 12 @Override 13 public void stop() { 14 System.out.println("bmw停下来"); 15 } 16 @Override 17 public void alarm() { 18 System.out.println("bmw鸣笛"); 19 } 20 @Override 21 public void engineBoom() { 22 System.out.println("bmw启动引擎"); 23 } 24 }
BenzCar
1 package com.zqz.dp.builder.car; 2 import com.zqz.dp.builder.model.CarModel; 3 /** 4 * @author Qin 第二辆按照CarModel建立的car2 5 */ 6 public class BenzCar extends CarModel { 7 @Override 8 public void start() { 9 System.out.println("benz启动"); 10 } 11 @Override 12 public void stop() { 13 System.out.println("benz停下来"); 14 } 15 @Override 16 public void alarm() { 17 System.out.println("benz鸣笛"); 18 } 19 @Override 20 public void engineBoom() { 21 System.out.println("benz启动引擎"); 22 } 23 }
CarBuilder
1 package com.zqz.dp.builder.builder; 2 import java.util.List; 3 import com.zqz.dp.builder.modelbuilder.CarModel; 4 /** 5 * @author Qin 6 * car的模型,即创建的特性与顺序 7 */ 8 public abstract class CarBuilder { 9 /** 10 * car的模型,存放car的特性,根据不同的顺序是先不同的操作。 11 * @param orders :car的特性 12 * 抽象类,交给子类实现 13 */ 14 public abstract void setOrders(List<String> orders); 15 /** 16 * 得到建立car的model 17 * @return carModel,具体是什么样的model交给子类 18 */ 19 public abstract CarModel getCarModel(); 20 }
BMWBuilder
1 package com.zqz.dp.builder.builder; 2 import java.util.List; 3 import com.zqz.dp.builder.car.BMWCar; 4 import com.zqz.dp.builder.model.CarModel; 5 /** 6 * @author Qin 7 * bmwCar的建造器,设置好关系 8 */ 9 public class BMWBuilder extends CarBuilder { 10 private CarModel bmwCar=new BMWCar(); //父类实例指向子类,创建BMWCar模型 11 @Override 12 public void setOrders(List<String> orders) { 13 this.bmwCar.setOrders(orders); //把存放特性的list集合设置到BMWCar 14 } 15 @Override 16 public CarModel getCarModel() { 17 return this.bmwCar; //取得CarModel,即取得car的建立的模型,这里的实例是BMW的模型 18 } 19 }
BenzBuilder
1 package com.zqz.dp.builder.builder; 2 import java.util.List; 3 import com.zqz.dp.builder.car.BenzCar; 4 import com.zqz.dp.builder.model.CarModel; 5 /** 6 * @author Qin 7 * benzCar的建造器,设置好关系 8 */ 9 public class BenzBuilder extends CarBuilder { 10 private CarModel benzCar=new BenzCar();//父类实例指向子类,创建BenzCar模型 11 @Override 12 public void setOrders(List<String> orders) { 13 this.benzCar.setOrders(orders); //把存放特性的list集合设置到BenzCar 14 } 15 @Override 16 public CarModel getCarModel() { 17 return this.benzCar; //取得CarModel,即取得car的建立的模型,这里的实例是Benz的模型 18 } 19 }
Director
1 package com.zqz.dp.builder.director; 2 import java.util.ArrayList; 3 import java.util.List; 4 import com.zqz.dp.builder.builder.BMWBuilder; 5 import com.zqz.dp.builder.builder.BenzBuilder; 6 import com.zqz.dp.builder.builder.CarBuilder; 7 import com.zqz.dp.builder.model.CarModel; 8 /** 9 * @author Qin 10 * 导演类,主要就是用来创建不同的car的建立模型的 11 * 比如BMWCar有3中类型,即有3中builder 12 */ 13 public class Director { 14 /** 15 * list容器,用来存放car的特性及顺序 16 */ 17 private List<String> orders=new ArrayList<String>(); 18 /** 19 * BMWCar的建造模型,即建造器 20 */ 21 private CarBuilder bmwBuilder=new BMWBuilder(); 22 /** 23 * BenzCar的建造模型,即建造器 24 */ 25 private CarBuilder benzBuilder=new BenzBuilder(); 26 /** 27 * 第一辆bmwCar的建造模型,设置好特性和顺序 28 * @return CarModel模板,主要返回的是具体的car 29 */ 30 public CarModel getABMWCar(){ 31 this.orders.clear(); //先清空list集合 32 this.orders.add("start"); //先启动 33 this.orders.add("engine boom"); //启动引擎 34 this.orders.add("stop"); //停止 35 this.bmwBuilder.setOrders(orders); //设置list容器到建造器中 36 return this.bmwBuilder.getCarModel(); //通过建造器取得对应的car实例 37 } 38 /** 39 * 第二辆bmwCar的建造模型,设置好特性和顺序 40 * @return CarModel模板,主要返回的是具体的car 41 */ 42 public CarModel getBBMWCar(){ 43 this.orders.clear(); //先清空list集合 44 this.orders.add("start"); //先启动 45 this.orders.add("alarm"); //鸣笛 46 this.orders.add("stop"); //停止 47 this.bmwBuilder.setOrders(orders); //设置list容器到建造器中 48 return this.bmwBuilder.getCarModel(); //通过建造器取得对应的car实例 49 } 50 /** 51 * 第一辆benzCar的建造模型,设置好特性和顺序 52 * @return CarModel模板,主要返回的是具体的car 53 */ 54 public CarModel getABenzCar(){ 55 this.orders.clear(); //先清空list集合 56 this.orders.add("start"); //先启动 57 this.orders.add("stop"); //停止 58 this.benzBuilder.setOrders(orders); //设置list容器到建造器中 59 return this.benzBuilder.getCarModel(); //通过建造器取得对应的car实例 60 } 61 }
Client
1 package com.zqz.dp.builder.client; 2 import com.zqz.dp.builder.director.Director; 3 /** 4 * @author Qin 5 * 场景类,主要是按照模板CarModel生成不同的car,并开动起来 6 */ 7 public class Client { 8 public static void main(String[] args) { 9 Director dir=new Director(); //导演类,决定car的特性及顺序 10 System.out.println("-----------建立第一辆car-------------"); 11 dir.getABMWCar().run(); //通过模板A建立第一辆bmwCar,并让car跑一会 12 System.out.println("-----------建立第二辆car-------------"); 13 dir.getABenzCar().run();//通过模板A建立第一辆benzCar,并让car跑一会 14 } 15 }
在建造者设计模式中,有如下四个角色:
1、 Product产品类,如上面的BMWCar和BenzCar。考虑到拓展性还有CarModel
2、 Builder抽象建造者,如CarBuilder
3、 Builder的实现类,如BMWBuilder、BenzBuilder
4、 Director导演类,负责告诉Builder如果进行建造
建造者的优点:
1、 封装性:客户端Client不需要知道产品类BMWCar和BenzCar的细节,不需要知道建造器Builder是如何实现的,只知道Director中有方法可以调用放回基本CarModel。
2、 建造者独立,容易扩展:BMWBuilder、BenzBuilder是相互独立的,便于拓展。
3、 便于控制细节:在拓展中不对其他模块产生影响。
建造者的使用场景:
1、 相同的方法,不同的执行顺序,产生不同的事件结果。
2、 多个部件或者零件,都可以装配到一个对象中,但产生运行的结果又不一样上面的CarModel。
3、 产品类非常复杂,或者产品类的不同的调用顺序产生不同的结果。
注意事项:和工厂方法的区别:
建造者关注的是零件类型跟执行的顺序。记住一点:建造者模式最主要的功能是基本方法的调用顺序,也就是其实基本方法已经实现了。而工厂中最主要的就是创建方法。