前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >设计模式 - 建造者模式

设计模式 - 建造者模式

作者头像
子乾建建-Jeff
发布2020-06-29 15:21:09
3010
发布2020-06-29 15:21:09
举报
文章被收录于专栏:iBroProiBroPro

「行情不行,就得多努力」

建建:这场疫情啊,让我明白了,有一个房子,能让你稳定住所;有一辆车,能让你出行无忧。

子乾:听这话意思是要买房买车了呢。

建建:要不资助点?

子乾:那明天你跟我一起出门挣点。

建建:去哪,在哪个位置?

子乾:以前啊,就在那大商场门口就行,一个碗,一根棍,一个铺盖趴一天,天天收入好几百。现在行情不行了呀。

建建:还是给我爸打个电话吧,明天去提车。

子乾:那建总要宝马还是大奔,是保时捷还是劳斯莱斯?

建建:行吧,那我就要一辆“环环相扣”的吧。

那么问题来了,面对众多的车牌,从在厂生产,到 4S 店销售,再到客户手中,这样一个复杂“车”对象,购买和生产的来龙去脉怎么表示在代码里面?

那这就需要 「建造者模式」了。

什么是 建造者模式?(别名生成器模式)

Builder Pattern: Separate the construction of a complex object from its representation so that the same construction process can create different representations.

看不懂看下面:

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

它是对象创建型模式

先来看一下类图:

角色:

指挥者 Director

抽象建造者 Builder

具体建造者 SubBuilder1、SubBuilder2 ...

目标产品 Product

如果你想买一辆车,Product 是车,Builder 是抽象的车制造厂,SubBuilder1 、SubBuilder2 可以是宝马制造厂、红旗制造厂、特斯拉制造厂等等... Director 则可以是汽车销售员。

你只需要告诉销售员想要的车牌,背后经过一系列生产组装(不需你管),一辆完整的车就送到了你面前。

再比如,现在饭店都有套餐,你去了想吃一份套餐,Product 是一份套餐,Builder 是抽象的套餐制作机,SubBuilder1、SubBuilder2 可以是具体的某一种套餐的制作机,麻辣香锅焖面套餐带米饭雪碧、三荤三素带米饭可乐、五荤五素带米饭等等... Director 则可以是饭店服务员。

你只需要告诉服务员想要的套餐名字,大厨们一顿操作(无需你管),一份完整的套餐就送到了你面前。

我们以饭店实例做一下拆解:

类图:

目标产品 Meal制作类 SubMealBuilderA 或者 SubMealBuilderB 完成制作,而为了“开闭原则”,他们有共同的制作类父类 MealBuilder 类,这在客户端的调用过程中就用到了“里氏代换”原则。

服务员 Waiter 则通过制作类的父类,完成调用。实际 Waiter 中使用的具体制作过程函数,肯定是制作类子类的。因为具体实现都在子类呀。

客户和服务员打交道就可以了,只需要告诉服务员想要的具体产品。

下面通过代码来解析一下这个过程:

首先,要有一个目标产品类 Meal

它包含该产品的具体组成,这份套餐中包含食物(麻辣香锅焖面套餐带米饭、三荤三素带米饭...)、饮料(可乐、雪碧、脉动...)

代码语言:javascript
复制
package com.sample.buildpattern;

// 目标产品类
public class Meal {

    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;
    }
}

然后,定义一个制作类父类 MealBuilder

该类依赖于目标产品类,因为该系列的类负责制作完成一份完整的目标产品。

因此这里面要包含食物、饮料的制作过程,因为目标产品 Meal 定义为了 protected,所以还需要一个类将生成的目标产品外用,即 getMeal()。

代码语言:javascript
复制
package com.sample.buildpattern;

public abstract class MealBuilder {
    protected Meal meal = new Meal();

    public abstract void buildFood();
    public abstract void buildDrink();
    public abstract Meal getMeal();
}

要定义 MealBuilder 具体的子类,每个子类负责制作不同的套餐

SubMealBuilderA 负责制作麻辣香锅焖面套餐带米饭可乐套餐,SubMealBuilderB 负责制作三荤三素带米饭可乐套餐。

SubMealBuilderA:

代码语言:javascript
复制
package com.sample.buildpattern;

public class SubMealBuilderA extends MealBuilder{

    @Override
    public void buildFood() {
        System.out.println("麻辣香锅焖面套餐加米饭");
    }

    @Override
    public void buildDrink() {
        System.out.println("雪碧");
    }
}

SubMealBuilderB:

代码语言:javascript
复制


public class SubMealBuilderB extends MealBuilder {

    @Override
    public void buildFood() {
        System.out.println("三荤三素加米饭");
    }

    @Override
    public void buildDrink() {
        System.out.println("可乐");
    }
}

定义指挥者服务员类 Waiter。

它负责调用(告诉)制作人员,客户需要哪一款套餐。

因此该类需要聚合制作类父类,手里握着制作类们的父亲,儿子们不敢不听话,想使用哪个儿子制作产品就可以调用哪一个。

制作类的父类为 Waiter 类私有属性,通过 set 方法为其赋值,并且在 construct 方法中,获得制作类制作的产品。

代码语言:javascript
复制
package com.sample.buildpattern;

public class Waiter {
    private MealBuilder mealBuilder;

    public void setMealBuilder(MealBuilder mealBuilder){
        this.mealBuilder = mealBuilder;
    }

    public Meal construct(){
        mealBuilder.buildFood();
        mealBuilder.buildDrink();

        return mealBuilder.getMeal();
    }
}

完成客户端类,客户端用户直接和服务员类 Waiter 打交道,告诉服务员需要什么套餐,服务员进行安排后,用户拿到目标套餐。

用户想要麻辣香锅焖面套餐加米饭可乐,因此指定该套餐,服务员收到后传达,制作人员一顿操作,用户拿到 meal,可以输出看是不是想要的套餐。

代码语言:javascript
复制
package com.sample.buildpattern;

public class Client {

    public static void main(String[] args){
        // 指定套餐
        MealBuilder mealBuilder = new SubMealBuilderA();

        //服务员传达
        Waiter waiter = new Waiter();
        waiter.setMealBuilder(mealBuilder);

        //返回食品
        Meal meal = waiter.construct();
        meal.getDrink();
        meal.getFood();

    }
}

结果:完美~

以上就是该模式的一个细致拆分分享。

通过以上分析,隐隐约约可以感受到该模式的一些优缺点。类挺多,开闭原则好像也符合,对吧。

下面我们总结一下它的优缺点:

优点:

▏客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使相同的创建过程可以创建不同的产品对象;

▏建造者类符合开闭原则,每个独立,很方便的替换、新增、删除;

▏可以更加精细的控制产品的创建过程。

缺点:

▏建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,不适合使用建造者模式;

▏产品内部变化复杂,可能需要创建很多具体的建造者,系统变的庞大臃肿复杂。

建造者模式适用的环境:

☆ 需要生成的产品对象有复杂的内部结构,比如多个成员变量,而且还是引用类型变量;

☆ 需要生成的产品对象的属性相互依赖,需要指定生成顺序;

☆ 对象的创建过程独立于创建该对象的类。创建过程在指挥者类中,不在建造者类,也不在客户端类;

☆ 复杂对象的创建和使用满足隔离要求。

诶,有没有一个小疑惑。

建造者模式是:告诉它,想要什么,就可以给你返回什么。

而抽象工厂模式也是,想要什么,告诉它,就可以给你返回什么。

它们俩有哪些区别呢?

从字面理解,工厂就是生产产品,生产某一样产品。建造者模式是构建,构建一件产品。

▌建造者模式返回的是一个组装好的完整产品,而抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成一个产品族。回顾这个:设计模式 - 抽象工厂模式

▌抽象工厂模式中,客户端实例化工厂类,调用工厂方法,获得目标产品。而建造者模式中,客户端可以不直接调用建造者的相关方法,而是通过指挥者来引导如何生成对象,包括对象的组装过程和建造步骤,它侧重于一步步构造一个复杂对象,返回一个完整复杂对象。

▌若将抽象工厂模式认为是汽车配件生产工厂,生产一个产品族的产品,轮胎、方向盘、发动机等。那么建造者模式就是汽车组装工厂,通过对部件的组装返回一辆完整汽车,宝马、奔驰、保时捷。

亲爱的读者朋友,不知不觉今天的分享就又结束了。你有什么想和我说的吗?

感谢陪伴,感谢阅读。

设计模式相关 demo 代码,在这个码云链接:

https://gitee.com/JeffBro/DesignPattern23

表情包来源于网络,侵删。

「子乾建建为作者笔名」

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 iBroPro 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档