您的位置:首页 > 其它

软件设计模式学习(七)建造者模式

2020-03-08 19:53 87 查看

建造者模式

建造者模式是最复杂的创建型模式,它将客户端与包含多个组成部分的复杂对象的创建过程分离,客户端无须知道复杂对象的内部组成部分与装配部分,只需知道建造者的类型即可。


模式动机

建造者模式用于创建一个包含对个组成部分的复杂对象,可以返回一个完整的产品对象给用户。用户无须知道创建过程和内部细节,只需直接使用创建好的完整对象即可。比如汽车拥有车轮、方向盘、发送机等各种部件,用户几乎不会单独使用某个部件,而是使用一辆完整的汽车。软件开发中也存在类似汽车一样的复杂对象,它们拥有一系列成员属性,而且可能存在一些限制条件。


模式定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一步一步创建一个复杂对象,允许用户通过指定复杂对象的类型和内容构建它们,用户不需要知道内部具体构建细节。


模式结构

  1. Builder(抽象建造者)

    为创建Product对象的各个部件指定抽象接口,方法buildPartX()用于创建复杂对象的各个部件;另一个方法getResult()用于返回复杂对象。

  2. ConcreteBuilder(具体建造者)

    具体建造者实现Builder接口,实现各部件的构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。

  3. Product(产品角色)

    产品角色是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。

  4. Director(指挥者)

    负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端只需与指挥者进行交互,在客户端确定建造者的类型,并实例化具体建造者对象(也可通过配置文件和反射机制),然后通过指挥者类的构造函数或者set方法将该对象传入指挥者类中。


建造者模式实例之KFC套餐

  1. 实例说明

    套餐是一个复杂对象,一般包含主食(如汉堡、鸡肉卷等)和饮料(如果汁、可乐)等组成部分。不同套餐有不同的组成部分,KFC服务员根据顾客需求,一步一步装填这些组成部分,构造一份完整的套餐,然后返回给顾客。

  2. 实例代码及解释

    产品类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;
    }
    }
  3. 抽象建造者类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;
    }
    }
  4. 具体建造者类SubMealBuilderA(A套餐建造者类)

    SubMealBuilderA是具体建造者类,用于创建A套餐,它是抽象建造者类的子类,实现了抽象建造者类中声明的部件的组装方法。

    public class SubMealBuilderA extends MealBuilder {
    
    @Override
    public void buildFood() {
    
    meal.setFood("一个鸡腿堡");
    }
    
    @Override
    public void buildDrink() {
    meal.setDrink("一杯可乐");
    }
    }
  5. 具体建造者类SubMealBuilderB(B套餐建造者类)

    public class SubMealBuilderB extends MealBuilder {
    
    @Override
    public void buildFood() {
    
    meal.setFood("一个鸡肉卷");
    }
    
    @Override
    public void buildDrink() {
    
    meal.setDrink("一杯果汁");
    }
    }
  6. 指挥者类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();
    }
    }
  7. 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;
    }
    }
  8. 配置文件

    <?xml version="1.0" encoding="UTF-8" ?>
    <configuration>
    <className>SubMealBuilderA</className>
    </configuration>
  9. 测试类

    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());
    }
    }
  10. 结果分析

    如果在配置文件将节点中内容设置为 SubMealBuilderA,则输出结果如下: 如果在配置文件将节点中内容设置为 SubMealBuilderB,则输出结果如下: 更换具体建造者无须修改源代码,只修改配置文件即可。如果需要增加新的具体建造者,只需增加一个新的具体建造者类继承抽象建造者类,再实现其中声明的抽象部件组装方法,修改配置文件,即可使用新的具体建造者构造新的类型的套餐,系统具有良好的灵活性和可扩展性,符合开闭原则的要求。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: