前面三个都是比较简单的工厂模式,主要用于构造比较简单的对象。但无论是在现实世界中还是在软件系统中,都存在一些复杂的对象,它们拥有多个组成部分,如汽车,它包括车轮、方向盘、发送机等各种部件。而对于大多数用户而言,无须知道这些部件的装配细节,也几乎不会使用单独某个部件,而是使用一辆完整的汽车。这个时候,面对复杂的对象可以通过建造者模式对其进行设计与描述,建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。
?
一、构造者模式动机
还是老方法,先看一个例子——描述KFC如何创建套餐:套餐是一个复杂对象,它一般包含主食(如汉堡、鸡肉卷等)和饮料(如果汁、可乐等)等组成部分,不同的套餐有不同的组成部分,而KFC的服务员可以根据顾客的要求,一步一步装配这些组成部分,构造一份完整的套餐,然后返回给顾客。
?
在上述例子中,如果需要构建套餐这个对象,就必须先要构造主食类和饮料对象,但是主食类和饮料又不确定具体是什么类型,如果把主食和饮料看成抽象工厂中的产品族似乎是可行的,但实际上,这里与产品族的概念有不一样,因为前面的例子只需要生产某一个产品,比方说海尔虽然也具有两个对象,但是这两个对象却不是必须同时出现的,而这里的套餐少了主食或者饮料就不能构成一个完整的套餐了,这就是需要注意的地方,所以可见构造者模式做的事情就是生产一个复杂的对象,而这个复杂的对象之所以复杂就是因为可能需要其他对象共同创建后才可以。
?
二、构造者模式定义
建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
?
建造者模式是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节。建造者模式属于对象创建型模式。根据中文翻译的不同,建造者模式又可以称为生成器模式。
?
三、构造者模式结构
构造者模式包含如下角色:
Builder:抽象建造者
ConcreteBuilder:具体建造者
Director:指挥者
Product:产品角色
?
?
四、模式分析
1.建造者模式的结构中还引入了一个指挥者类Director,该类的作用主要有两个:一方面它隔离了客户与生产过程;另一方面它负责控制产品的生成过程。指挥者针对抽象建造者编程,客户端只需要知道具体建造者的类型,即可通过指挥者类调用建造者的相关方法,返回一个完整的产品对象;
2.在客户端代码中,无须关心产品对象的具体组装过程,只需确定具体建造者的类型即可,建造者模式将复杂对象的构建与对象的表现分离开来,这样使得同样的构建过程可以创建出不同的表现。?
?
五、实例分析
拿刚刚那个KFC套餐例子来说,我们的复杂对象就是套餐, 套餐包括主食和饮料,每部分都是套餐这个复杂产品的一个子产品,而指挥者就是我们的KFC服务员了。所以我们需要构造几个具体的子产品,这里为了方便,我们可以将产品都简化成String对象,而把重心放到理解构造者模式的结构上。首先创建这个复杂的套餐对象。
1.套餐对象
?
class="SetMeal.java" name="code">public class SetMeal { private String food; private String drink; public String getFood() { return food; } public void setFood(String food) { this.food = food; } public String getDrink() { return drink; } public void setDrink(String drink) { this.drink = drink; } }?2.用于构造套餐对象的构造者抽象类
?
?
public abstract class SetMealBuilder { protected SetMeal mSetMeal; public SetMealBuilder() { mSetMeal = new SetMeal(); } public abstract SetMealBuilder buildFood(); public abstract SetMealBuilder buildDrink(); public SetMeal getSetMeal(){ return this.mSetMeal; } }?3.创建具体的子构造器类——汉堡可乐套餐
?
?
public class SetMealHamburgerBuilder extends SetMealBuilder{ @Override public SetMealBuilder buildFood() { mSetMeal.setFood("汉堡包"); return this; } @Override public SetMealBuilder buildDrink() { mSetMeal.setDrink("可乐"); return this; } }?4.创建指挥者类——KFC服务员
?
?
public class KFCWaiter { private SetMealBuilder mSetMealBuilder; public void setSetMealBuilder(SetMealBuilder setMealBuilder) { mSetMealBuilder = setMealBuilder; } public SetMeal construct() { return mSetMealBuilder.getSetMeal(); } }?5.创建客户端进行测试
?
?
public class Test { public static void main(String[] args) { // 1.声明一个抽象的构造者对象 SetMealBuilder setMealBuilder; // 2.创建一个具体的构造者对象,并向上转型给父类 setMealBuilder = new SetMealHamburgerBuilder(); // 3.调用构造对象的方法 setMealBuilder.buildFood().buildDrink(); // 4.创建一个指挥者对象 KFCWaiter kfcWaiter = new KFCWaiter(); // 5.将构造者对象传入指挥者对象 kfcWaiter.setSetMealBuilder(setMealBuilder); // 6.调用指挥者创建套餐对象 SetMeal meal = kfcWaiter.construct(); // 7.输出套餐对象的值 System.out.println(String.format("food:%s,drink:%s", meal.getFood(), meal.getDrink())); } }可以发现,此时我们只需要改setMealBuilder = new SetMealHamburgerBuilder();这一行代码,将具体的构造者传入改成如果使用java的反射机制和XML文档的结合,还可以这么写: setMealBuilder = (SetMealBuilder )XMLUtil.getBean(); //getBean()的返回类型 这样一来只需要修改XML中的名字,连java文件都不需要改动,很好的符号了“开闭原则”。 其实,如果实际过程中如果具体的对象需要进行不同的组合的时候,可以只有一个具体的构造者对象即可,然后在具体的构造子类的过程进行传值处理,这样能减少类的开销,维护起来更简单,即将buildFood()改成buildFood(String)这样的话,具体生产什么样的汉堡就可以完全有用户定义了。drink也是一样的。 六、构造者模式的缺点 1.建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。 2.如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
?
?谢谢您的关注和阅读,文章不当之处还请您不吝赐教~~~?
?