建造者模式是最复杂的创建型模式,它将客户端与包含多个组成部分的复杂对象的创建过程分离,客户端无须知道复杂对象的内部组成部分与装配部分,只需知道建造者的类型即可。
模式动机
建造者模式用于创建一个包含对个组成部分的复杂对象,可以返回一个完整的产品对象给用户。用户无须知道创建过程和内部细节,只需直接使用创建好的完整对象即可。比如汽车拥有车轮、方向盘、发送机等各种部件,用户几乎不会单独使用某个部件,而是使用一辆完整的汽车。软件开发中也存在类似汽车一样的复杂对象,它们拥有一系列成员属性,而且可能存在一些限制条件。
模式定义
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一步一步创建一个复杂对象,允许用户通过指定复杂对象的类型和内容构建它们,用户不需要知道内部具体构建细节。
模式结构
-
Builder(抽象建造者)
为创建Product对象的各个部件指定抽象接口,方法buildPartX()用于创建复杂对象的各个部件;另一个方法getResult()用于返回复杂对象。
-
ConcreteBuilder(具体建造者)
具体建造者实现Builder接口,实现各部件的构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。
-
Product(产品角色)
产品角色是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。
-
Director(指挥者)
负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端只需与指挥者进行交互,在客户端确定建造者的类型,并实例化具体建造者对象(也可通过配置文件和反射机制),然后通过指挥者类的构造函数或者set方法将该对象传入指挥者类中。
建造者模式实例之KFC套餐
-
实例说明
套餐是一个复杂对象,一般包含主食(如汉堡、鸡肉卷等)和饮料(如果汁、可乐)等组成部分。不同套餐有不同的组成部分,KFC服务员根据顾客需求,一步一步装填这些组成部分,构造一份完整的套餐,然后返回给顾客。
-
实例代码及解释
-
产品类Meal
套餐Meal是复杂产品对象,它包括两个成员属性food和drink,其中food表示主食,drink表示饮料,Meal中还包括成员属性的Getter方法和Setter方法。
public class Meal { //部件 private String food; private String drink; public String getDrink() { return drink; } public void setDrink(String drink) { this.drink = drink; } public String getFood() { return food; } public void setFood(String food) { this.food = food; } }
-
抽象建造者类MealBuilder(套餐建造者类)
MealBuilder是套餐建造者,它是一个抽象类,声明了抽象的部件组装方法buildFood()和buildDrink(),在MealBuilder中定义Meal类型的对象meal,提供工厂方法getMeal()用于返回meal对象。
public abstract class MealBuilder { protected Meal meal = new Meal(); public abstract void buildFood(); public abstract void buildDrink(); public Meal getMeal() { return meal; } }
-
具体建造者类SubMealBuilderA(A套餐建造者类)
SubMealBuilderA是具体建造者类,用于创建A套餐,它是抽象建造者类的子类,实现了抽象建造者类中声明的部件的组装方法。
public class SubMealBuilderA extends MealBuilder { @Override public void buildFood() { meal.setFood("一个鸡腿堡"); } @Override public void buildDrink() { meal.setDrink("一杯可乐"); } }
-
具体建造者类SubMealBuilderB(B套餐建造者类)
public class SubMealBuilderB extends MealBuilder { @Override public void buildFood() { meal.setFood("一个鸡肉卷"); } @Override public void buildDrink() { meal.setDrink("一杯果汁"); } }
-
指挥者类KFCWaiter(服务员类)
KFCWaiter类是指挥者类,在KFC套餐制作过程中相当于KFC服务员,客户端指定具体建造者类型,在其construct()方法中调用指定建造者对象的部件组装方法和工厂方法。
public class KFCWaiter { private MealBuilder mealBuilder; public void setMealBuilder(MealBuilder mealBuilder) { this.mealBuilder = mealBuilder; } public Meal construct() { mealBuilder.buildFood(); mealBuilder.buildDrink(); return mealBuilder.getMeal(); } }
-
XML操作工具类
public class XMLUtil { public static Object getBean() throws Exception { //创建解析器工厂 DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); //创建解析器 DocumentBuilder builder = builderFactory.newDocumentBuilder(); //得到document Document document = builder.parse("config.xml"); //获取包含品牌名称的文本节点 NodeList brandNameList = document.getElementsByTagName("className"); Node classNode = brandNameList.item(0).getFirstChild(); String className = classNode.getNodeValue().trim(); Class c = Class.forName("com.builderPattern." + className); Object o = c.newInstance(); return o; } }
-
配置文件
<?xml version="1.0" encoding="UTF-8" ?> <configuration> <className>SubMealBuilderA</className> </configuration>
-
测试类
public class Test { public static void main(String[] args) throws Exception { //动态确定套餐种类 MealBuilder mealBuilder = (MealBuilder) XMLUtil.getBean(); //服务员是指挥者 KFCWaiter waiter = new KFCWaiter(); //服务员准备套餐 waiter.setMealBuilder(mealBuilder); //客户获得套餐 Meal meal = waiter.construct(); System.out.println("套餐组成:"); System.out.println(meal.getDrink()); System.out.println(meal.getFood()); } }
-
结果分析
如果在配置文件将节点中内容设置为 SubMealBuilderA,则输出结果如下:
如果在配置文件将节点中内容设置为 SubMealBuilderB,则输出结果如下:
更换具体建造者无须修改源代码,只修改配置文件即可。如果需要增加新的具体建造者,只需增加一个新的具体建造者类继承抽象建造者类,再实现其中声明的抽象部件组装方法,修改配置文件,即可使用新的具体建造者构造新的类型的套餐,系统具有良好的灵活性和可扩展性,符合开闭原则的要求。
-