初探设计模式——建造者模式

149 阅读4分钟

「这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战」。

1.定义:

将一个复杂对象的构建与表示分离,使得用户在不知道的对象的创建细节情况下就可以直接创建复杂的对象。例如用户需要一台电脑,他不需要知道这台电脑有哪些组件,组装过程是什么样的,他只需要知道最终会交给他一台完整的电脑就够了。复杂的对象一步一步的被建造完成,其中的实现方式只有建造者知道。

2.模式结构

3.时序图

4.代码分析

案例:去KFC点一份套餐,一般情况下一份套餐中有主食(汉堡、鸡肉卷),饮料(可乐、果汁),小食(鸡翅、鸡腿),作为顾客不用知道这是怎么做的,只需要给我一份完整套餐即可,先分析下从点餐到拿到套餐需要什么

  • 产品类:顾客点的套餐

  • 指挥者:配餐员,指挥配齐一份套餐

  • 抽象建造者:套餐需要哪些制作流程

  • 具体建造者:厨师,制作套餐中的每一样商品

/**
 * 套餐需要的东西(抽象建造者),定义产品构造需要的方法(定义一份套餐由什么组成)
 */
public static abstract class Builder {
    public abstract void buildFood();

    public abstract void buildDrinks();

    public abstract void buildSnack();

    public abstract PackageKFC returnKFC();
}

/**
 * 厨师(具体建造者),负责产品的构造并返回构造好的产品(汉堡、炸鸡等)
 */
public static class ConcreteBuilder extends Builder {
    PackageKFC aPackageKFC = new PackageKFC();

    @Override
    public void buildFood() {
        aPackageKFC.setFood("汉堡");
    }

    @Override
    public void buildDrinks() {
        aPackageKFC.setDrinks("可乐");
    }

    @Override
    public void buildSnack() {
        aPackageKFC.setSnack("炸鸡");
    }

    @Override
    public PackageKFC returnKFC() {
        return aPackageKFC;
    }
}

/**
 * 配餐员(指挥者),负责套餐的配置顺序
 */
public static class Direct {
    public void construct(Builder builder) {
        builder.buildFood();
        builder.buildDrinks();
        builder.buildSnack();
        builder.returnKFC();
    }
}

/**
 * KFC套餐,需要被构建的产品类
 */
public static class PackageKFC {
    private String food;
    private String drinks;
    private String snack;

    public void setFood(String food) {
        this.food = food;
    }

    public void setDrinks(String drinks) {
        this.drinks = drinks;
    }

    public void setSnack(String snack) {
        this.snack = snack;
    }

    @Override
    public String toString() {
        return "您的餐好了,有" + food + "," + drinks + "," + snack;
    }
}


/**
 * 具体使用
 */
public static void main(String[] args) {
    Direct direct = new Direct();
    Builder builder = new ConcreteBuilder();
    direct.construct(builder);
    PackageKFC packageKFC = builder.returnKFC();
    System.out.println(packageKFC.toString());
}

5.模式分析

抽象建造者中定义了抽象方法的同时还定义了返回的产品,同时还引入了指挥者,指挥者角色的存在首先将顾客和产品的建造过程进行了隔离,其次负责产品的配置过程,指挥者针对抽象建造者编程,这里客户端是不需要参与的,客户端只需要知道具体建造者类型就够了,客户端可通过具体的建造者建造出具有相同属性不同类型的产品,比如需要套餐A(汉堡,鸡翅,可乐),套餐B(鸡肉卷,鸡腿,果汁)等

6.适用环境

  • 需要创建的对象的结构是复杂的,并且有多个成员属性
  • 成员属性之间是相互依赖的,需要指定生成顺序
  • 对象的创建过程独立于创建该对象的类,引入了指挥者将创建过程封装在指挥者类中而不在建造者类中
  • 隔离复杂对象的创建和使用,使得相同的创建过程可以创建出不同的类

7.扩展

  • 省略抽象建造者:如果可以确定只有一个具体的建造者时可以将抽象建造者省略;
  • 省略指挥者:如果可以确定只有一个具体的建造者且已经省去了抽象建造者是可以将指挥者省略,让Builder角色扮演指挥者和建造者双重角色。

由此可知,上述案例的代码可以简化为这样:

public static class KFCFactory {
    private String food;
    private String drinks;
    private String snack;

    public KFCFactory(String food, String drinks, String snack) {
        this.food = food;
        this.drinks = drinks;
        this.snack = snack;
    }

    public static class Builder {
        private String food;
        private String drinks;
        private String snack;

        public Builder buildFood(String food) {
            this.food = food;
            return this;
        }

        public Builder buildDrinks(String drinks) {
            this.drinks = drinks;
            return this;
        }

        public Builder buildSnack(String snack) {
            this.snack = snack;
            return this;
        }

        public KFCFactory build() {
            return new KFCFactory(this.food, this.drinks, this.snack);
        }
    }

    @Override
    public String toString() {
        return "您的餐好了,有" + food + "," + drinks + "," + snack;
    }
}


/**
 * 具体使用
 */
public static void main(String[] args) {
 	KFCFactory kfcFactoryA = new KFCFactory
                .Builder()
                .buildFood("汉堡")
                .buildDrinks("可乐")
                .buildSnack("鸡翅")
                .build();

    System.out.println(kfcFactoryA.toString());

    KFCFactory kfcFactoryB = new KFCFactory
        .Builder()
        .buildFood("鸡肉卷")
        .buildDrinks("果汁")
        .buildSnack("土豆泥")
        .build();

    System.out.println(kfcFactoryB.toString());
}