设计模式-建造者模式

本文涉及的产品
云原生大数据计算服务 MaxCompute,5000CU*H 100GB 3个月
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
简介: 设计模式-建造者模式

引例



需求:建造房子,建造过程包括打地基、砌墙和封顶。房子各式各样,包括平房和别墅,编程模拟实现。

工厂模式把细节封装起来,建造者模式可根据配置创建不同的对象

一般解法


思路:定义房子类,包括建房的方法,然后定义平房和别墅继承房子类,重写相关方法。

类图

代码

AbstractHouse(房子)

public abstract class AbstractHouse {
    public abstract void buildBasic();//打地基
    public abstract void buildWalls();//砌墙
    public abstract void buildRoof();//封顶
    public void build() { //建房
       buildBasic();  //三个步骤(注意顺序)
       buildWalls();
       buildRoof();
    }
}


Bungalow(平房)

public class Bungalow extends AbstractHouse{
    @Override
    public void buildBasic() {
        System.out.println("平房打地基");
    }
    @Override
    public void buildWalls() {
        System.out.println("平房砌墙");
    }
    @Override
    public void buildRoof() {
        System.out.println("平房封顶");
    }
}


Villa (别墅)

public class Villa extends AbstractHouse{
    @Override
    public void buildBasic() {
        System.out.println("别墅打地基");
    }
    @Override
    public void buildWalls() {
        System.out.println("别墅砌墙");
    }
    @Override
    public void buildRoof() {
        System.out.println("别墅封顶");
    }
}

客户端调用

public class Client {
    public static void main(String[] args) {
        Bungalow bungalow = new Bungalow();
        bungalow.build();
        Villa villa = new Villa();
        villa.build();
    }
}


运行结果

缺点

程序不好扩展和维护,把产品和创建产品过程封装在一起,耦合性高。

解耦产品和创建产品过程=》建造者模式

建造者模式


建造者模式(Builder Pattern)又叫生成器模式,是一种对象构建模式。它可以将复杂对象的建造过程抽象出来(抽象类别),使这个抽象过程的不同实现方法可以构造出不同表现(属性)的对象。

一步一步创建一个复杂的对象,允许用户只通过指定复杂对象的类型和内容就可以构建它们,而不需要知道内部的具体构建细节

类图

image.png

  • Product(产品): 具体的产品对象
  • Builder(抽象建造者): 创建一个产品对象的各个部件指定的接口/抽象类。
  • ConcreteBuilder(具体建造者): 实现接口,构建和装配各个部件。
  • Director(指挥者): 构建一个使用Builder接口的对象,负责控制产品对象的生产过程,隔离了客户与对象的生产过程

建造者模式解法


代码

House类(Product产品)

package java设计模式.Builder.improve;
//产品--->Produce
public class House {
    private String baise;
    private String wall;
    private String roof;

    public String getBaise() {
        return baise;
    }

    public void setBaise(String baise) {
        this.baise = baise;
    }

    public String getWall() {
        return wall;
    }

    public void setWall(String wall) {
        this.wall = wall;
    }

    public String getRoof() {
        return roof;
    }

    public void setRoof(String roof) {
        this.roof = roof;
    }
}

HouseBuilder类(Builder抽象建造者)

public abstract class HouseBuilder {
    protected House house = new House();
    //将建造的流程写好, 抽象的方法
    public abstract void buildBasic();
    public abstract void buildWalls();
    public abstract void buildRoof();
    //建造房子好, 将产品(房子) 返回
    public House buildHouse() {
        return house;
    }
}


Bungalow类(ConcreteBuilder具体建造者A)

public class Bungalow extends HouseBuilder {
    @Override
    public void buildBasic() {
        System.out.println("平房打地基");
    }
    @Override
    public void buildWalls() {
        System.out.println("平房砌墙");
    }
    @Override
    public void buildRoof() {
        System.out.println("平房封顶");
    }
}

Villa类(ConcreteBuilder具体建造者B)

public class Villa extends HouseBuilder {
    @Override
    public void buildBasic() {
        System.out.println("别墅打地基");
    }
    @Override
    public void buildWalls() {
        System.out.println("别墅砌墙");
    }
    @Override
    public void buildRoof() {
        System.out.println("别墅封顶");
    }
}

HouseDirector类(Director指挥者)

public class HouseDirector {
    //聚合 housebuilder
    HouseBuilder houseBuilder = null;
    //构造器传入 houseBuilder
    public HouseDirector(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }
    //通过setter 传入 houseBuilder
    public void setHouseBuilder(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }
    //如何处理建造房子的流程,交给指挥者
    public House constructHouse() {
        houseBuilder.buildBasic();
        houseBuilder.buildWalls();
        houseBuilder.buildRoof();
        return houseBuilder.buildHouse();
    }
}
客户端调用
public class Client {
    public static void main(String[] args) {
        //盖平房
        Bungalow commonHouse = new Bungalow();
        //准备创建房子的指挥者
        HouseDirector houseDirector = new HouseDirector(commonHouse);
        //完成盖房子,返回产品(普通房子)
        House house = houseDirector.constructHouse();
        System.out.println("--------------------------");
        //盖别墅
        Villa highBuilding = new Villa();
        //重置建造者
        houseDirector.setHouseBuilder(highBuilding);
        //完成盖房子,返回产品(高楼)
        houseDirector.constructHouse();
    }
}


运行结果

建造者模式在JDK的应用和源码分析

image.png

image.png

案例

理论总是难以理解的,现在通过案例分析问题,一步步了解使用建造者模式的好处

【案例】好好看一下这个案例

KFC套餐假如目前KFC里面有很多个套餐> 在套餐里面有必点,也有选点,然后每个单品又有大小之分> 必点:汉堡(hamburger),薯条(chips)> 选点:鸡腿(chicken),可乐(cola),披萨(pizza)

【用Java代码模拟场景】

我们如何构成这么多套餐实例呢?

我们不使用建造者模式也能构建代码,但是建造者模式会让代码看上去更装逼,代码到后期更结构化更容易维护和拓展

首先构建这个实体类`KFC`

public class KFC {
    //套餐必点
    private String hamburger;
    private String chips;
    
    //套餐选点
    private String chicken;
    private String cola;
    private String pizza;
}

我们的想法是不是折叠构造函数来创建实例,下面来尝试一下

public class KFC{
    //省略了上面的属性.....
    
    //必点套餐A
    public KFC(String hamburger,String chips){
        this(hamburger,chips,null,null,null);
    }
    A
    //套餐B
    public KFC(String hamburger,String chips,String chicken){
        this(hamburger,chips,chicken,null,null);
    }
    //套餐C
    public KFC(String hamburger,String chips,String chicken,String cola){
        this(hamburger,chips,chicken,cola,null);
    }
    
    //......还有好多种组合方式,你会发现使用折叠构造函数的方法十分复杂
    
    //全选
    public KFC(String hamburger,String chips,String chicken,String cola,String pizza){
        this.hamburger = hamburger;
        this.chips = chips;
        this.chicken = chicken;
        this.cola = cola;
        this.pizza = pizza;
    }
}

我们会发现使用折叠构造函数的方式很复杂,很恶心,代码看都不想看

那么有人会想,我可以使用`set`方法来创建,我只要一个必点构造就好了,那继续模拟咯

解释

public class KFC{
    //.....省略了属性
    //必点
    public KFC(String hamburger,String chips){
        this.hamburger = hamburger;
        this.chips = chips;
    }
    //set方法
    public void setChicken(String chicken) {
        this.chicken = chicken;
    }
    public void setCola(String cola) {
        this.cola = cola;
    }
    public void setPizza(String pizza) {
        this.pizza = pizza;
    }
    
    //实例化对象,你会发现这种方式就友好很多
     public static void main(String[] args) {
        KFC kfc = new KFC("大汉堡","大薯条");
        //加小份可乐
        kfc.setCola("小可乐");
        //加个鸡腿
        kfc.setChicken("大鸡腿");
        System.out.println(kfc);
    }
}

你会发现使用`set`方式就友好了很多

这个虽然友好了很多,但是也有点小毛病,就是你**set太随意了,我可能这个套餐里面没有这个单品,而使用set的人却不知道**,造成错误的套餐出现!。

为了解决上面的两种问题:一种设计模式解决一类问题,所以**建造者模式**就出现了



> 拿其中两个套餐举例>> 套餐A:汉堡,薯条,大鸡腿> 套餐B:汉堡,薯条,小鸡腿,小可乐,小披萨>> 其中薯条和汉堡可大可小,并且必须有,> 其它的都为固定大小,但是你可以选择有或没有


  • 产品(KFC)
public class KFC {
    //套餐必点
    private String hamburger;
    private String chips;
    
    //套餐选点
    private String chicken;
    private String cola;
    private String pizza;
    
        //必点
    public KFC(String hamburger,String chips){
        this.hamburger = hamburger;
        this.chips = chips;
    }
    //set方法
    public void setChicken(String chicken) {
        this.chicken = chicken;
    }
    public void setCola(String cola) {
        this.cola = cola;
    }
    public void setPizza(String pizza) {
        this.pizza = pizza;
    }
}
  • Builder

定义一个接口,表明需要建造什么,得到什么

public interface Builder {
    void setChicken();
    void setCola();
    void setPizza();
    KFC getKFC();
}
  • ConcreteBuilder:

此时应该注意,这个时候还没有生产套餐,只是定义套餐

套餐A


public class ConcreteBuilder1 implements Builder {
    private KFC kfc;
    //这一步非常重要
    public ConcreteBuilder1(String hamburger,String chips){
        kfc = new KFC(hamburger,chips);
    }
    @Override
    public void setChicken() {
        kfc.setChicken("大鸡腿");
    }
    @Override
    public void setCola() {
        kfc.setCola(null);
        System.out.println("套餐A里面没有可乐");
    }
    @Override
    public void setPizza() {
        kfc.setPizza(null);
        System.out.println("套餐A里面没有披萨");
    }
    @Override
    public KFC getKFC() {
        return kfc;
    }
}

套餐B

public class ConcreteBuilder2 implements Builder {
    private KFC kfc;
    //这一步非常重要
    public ConcreteBuilder2(String hamburger,String chips){
        kfc = new KFC(hamburger,chips);
    }
    @Override
    public void setChicken() {
        kfc.setChicken("小鸡腿");
    }
    @Override
    public void setCola() {
        kfc.setCola("小可乐");
    }
    @Override
    public void setPizza() {
        kfc.setPizza("小披萨");
    }
    @Override
    public KFC getKFC() {
        return kfc;
    }
}

Director:

真正的执行者,这里把他当作服务员,此时你像服务员点餐

public class Director {
    public KFC build(Builder builder){
        //套餐里面我只选了鸡腿和可乐
        builder.setChicken();
        builder.setCola();
        return builder.getKFC();
    }
}
public class BuilderTest {
    public static void main(String[] args) {
       //套餐A
        System.out.println("======套餐A======");
        Builder concreteBuilder1 = new ConcreteBuilder1("大汉堡", "小薯条");
        KFC kfc1 = new Director().build(concreteBuilder1);
        System.out.println(kfc1);
        //套餐B
        System.out.println("======套餐B======");
        Builder concreteBuilder2 = new ConcreteBuilder2("小汉堡", "小薯条");
        KFC kfc2 = new Director().build(concreteBuilder2);
        System.out.println(kfc2);
    }
}

输出

到了这里你还是会觉得有点麻烦,你会发现,单品可有可无的选择上面你十分的被动,代码看上去也很怪,如果你下次想全部单品先选上,再去选套餐的时候,你又要新建一个新的指导者。

```

我觉得普通的建造者模式不适合参数的可有可无的选择,**普通的建造者模式更侧重调控次序**,在有些情况下需要简化系统结构

简化版的建造者模式

这个时候简化版的建造者模式站出来了

**采用链式编程的方式**

这种模式更加灵活,更加符合定义

既然Director是变化的,并且其实在生活中我们自己本身就是Director,所以这个时候我们可以**把Director这个角色去掉**,因为我们自身就是指导者

* 产品(product)


public class KFC {
    //套餐必点
    private String hamburger;
    private String chips;
    //套餐选点
    private String chicken;
    private String cola;
    private String pizza;
    public KFC(String hamburger,String chips){
        this.hamburger = hamburger;
        this.hamburger = chips;
    }
    public void setChicken(String chicken) {
        this.chicken = chicken;
    }
    public void setCola(String cola) {
        this.cola = cola;
    }
    public void setPizza(String pizza) {
        this.pizza = pizza;
    }
  • 抽象建造者(builder)
public abstract class Builder {
        abstract Builder setChicken();
        abstract Builder setCola();
        abstract Builder setPizza();
        abstract KFC getKFC();
}
  • 具体建造者(ConcreteBuilder)
public class ConcreteBuilder extends Builder {
    KFC kfc;
    public ConcreteBuilder(String hamburger,String chips){
        kfc = new KFC(hamburger,chips);
    }
    @Override
    Builder setChicken() {
        kfc.setChicken("鸡腿");
        return this;
    }
    @Override
    Builder setCola() {
        kfc.setCola("可乐");
        return this;
    }
    @Override
    Builder setPizza() {
        kfc.setPizza("披萨");
        return this;
    }
    @Override
    KFC getKFC() {
        return kfc;
    }
}
  • 测试
public class BTest {
    public static void main(String[] args) {
       KFC kfc = new ConcreteBuilder("汉堡","薯条").setChicken().setCola().getKFC();
    }
}

如果不需要抽象建造者的角色来规定生产内容,那么代码到这里其实还有进一步的简化空间。

【关键代码】

**使用静态内部类的方式**

【进一步简化】

public class KFC {
    //套餐必点
    private String hamburger;
    private String chips;
    //套餐选点
    private String chicken;
    private String cola;
    private String pizza;
    
    //一定要有一个带有Builder参数的建造者
    private KFC(Builder builder) {
        this.hamburger = builder.hamburger;
        this.chips = builder.chips;
        this.chicken = builder.chicken;
        this.cola = builder.cola;
        this.pizza = builder.pizza;
    }
    //注意必须为静态内部类
    public static class Builder{
        //套餐必点
        private String hamburger;
        private String chips;
        //套餐选点
        private String chicken;
        private String cola;
        private String pizza;
        public Builder(String hamburger,String chips){
            this.hamburger = hamburger;
            this.chips = chips;
        }
        public Builder setChicken(){
            this.chicken = "小鸡腿";
            return this;
        }
        public Builder setCola(){
            this.cola = "小可乐";
            return this;
        }
        public Builder setPizza(){
            this.pizza = "小披萨";
            return this;
        }
        
        //生成一个产品
        public KFC getKFC(){
            return new KFC(this);
        }
    }
}

测试

public class BuilderTest {
   public static void main(String[] args) {
       KFC kfc = new KFC.Builder("大汉堡", "小薯条").setChicken().setCola().getKFC();
       System.out.println(kfc);
    }
}



  • 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰。
  • 使用不同的具体建造者即可得到不同的产品对象。
  • 增加新的具体建造者无须修改原有代码。
  • 若产品间的差异很大,则不适合使用建造者模式。
  • 抽象工厂模式VS建造者模式抽象工厂模式是对产品家族的创建,一个产品家族是一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。 而建造者模式则是要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。
相关实践学习
基于MaxCompute的热门话题分析
本实验围绕社交用户发布的文章做了详尽的分析,通过分析能得到用户群体年龄分布,性别分布,地理位置分布,以及热门话题的热度。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps 
相关文章
|
7月前
|
设计模式 Java
【设计模式系列笔记】建造者模式
建造者模式是一种创建型设计模式,用于将复杂对象的构建与其表示分离,使构建过程可定制。关键元素包括产品类(定义要构建的对象)、建造者接口(定义构建方法)、具体建造者类(实现构建过程)和指导者类(负责构建过程)。通过建造者模式,客户端可以灵活地创建具有不同表示的复杂对象,提高代码的可读性和可维护性,尤其适用于构建过程复杂且包含多个可选部分的情况。
134 1
|
7月前
|
设计模式 安全 Java
构建未来应用:Java设计模式 - 建造者模式(Builder)在现代编程中的应用
【4月更文挑战第7天】建造者模式是提升代码质量的关键,尤其在复杂环境中。它分步骤构建对象,将构建与表示分离,适用于UI构建、数据模型组装、配置文件解析和网络请求构造等场景。最佳实践包括明确构建步骤、提供默认值、支持链式调用和确保线程安全。然而,过多步骤、不一致状态和性能问题是使用时需注意的问题。掌握建造者模式对于现代编程至关重要。
87 3
|
3月前
|
设计模式 算法
设计模式--建造者模式 builder
这篇文章通过一个电脑购买的例子,详细解释了建造者模式的四个角色(产品类、抽象构建者、实体构建类和指导者类),并提供了相应的代码实现,阐述了建造者模式在设计复杂对象时的应用和优势。
设计模式--建造者模式 builder
|
19天前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
3月前
|
设计模式 算法 Java
Java设计模式-建造者模式(6)
Java设计模式-建造者模式(6)
|
4月前
|
设计模式 XML 存储
【四】设计模式~~~创建型模式~~~建造者模式(Java)
文章详细介绍了建造者模式(Builder Pattern),这是一种创建型设计模式,用于将复杂对象的构建与其表示分离,允许分步骤创建一个复杂的对象而无需指定其内部的具体构造细节。通过定义抽象建造者、具体建造者、指挥者和产品角色,建造者模式允许通过相同的构建过程创建不同的产品表示,提高了系统的灵活性和扩展性。
|
6月前
|
设计模式 算法
建造者模式-大话设计模式
建造者模式-大话设计模式
|
7月前
|
设计模式 uml
大话设计模式(3)——造物者一般的建造者模式
大话设计模式(3)——造物者一般的建造者模式
43 1
大话设计模式(3)——造物者一般的建造者模式
|
5月前
|
设计模式 JavaScript
js设计模式【详解】—— 建造者模式
js设计模式【详解】—— 建造者模式
57 0
|
6月前
|
设计模式
设计模式-05建造者模式(Builder Pattern)
设计模式-05建造者模式(Builder Pattern)