Skip to content
On this page

建造者模式

概述

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

image-20220919205058127

  • 分离了部件的构造(由Builder来负责)和装配(由Director负责)。从而可以构造出复杂的对象。从而可以构造出复杂的对象,这个模式适用于:某个对象的构建过程构建复杂的情况
  • 由于实现了构建和装配的解耦。不同的构建器、相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象,也就是实现了构建算法
  • 建造者模式可以将部件和其组装过程分开,一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无需知道其内部的具体构造细节

结构

建造者(Builder)模式包含如下角色:

  • 抽象建造者类(Builder):这个接口规定要实现复杂对象的那些部分的创建,并不设计具体的部件对象的创建
  • 具体建造者类(Concrete Builder):实现Builder接口,完成复杂产品的各个部件的具体创建方法,在构造过程完成后,提供产品的实例
  • 产品类(Product):要创建的复杂对象
  • 指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只是复杂保证对象的各部分完整的创建或按着某种顺序创建

image-20220919213110914

案例

创建共享单车

生产自行车是一个复杂的过程,它包含了车架、车座等组件的生产。而车间又有碳钎维、铝合金等材质,车座有橡胶、真皮等材质。对于自行车的生产就可以使用建造者模式

Bike的产品,包含车架、车座等组件;Builder是抽象建造者,MobileBuilder和OfoBuilder是具体的建造者;Director是指挥者,类图如下:

image-20220919213618976

代码

产品类(Bike)

java
package com.huangjiliang.design.heima.create.builder;

import lombok.Data;

/**
 * 具体产品
 */
@Data
public class Bike {

    private String seat;

    private String frame;

}

抽象建造者(BikeBuilder)

java
package com.huangjiliang.design.heima.create.builder;

/**
 * 自行车抽象构建者
 */
public abstract class BikeBuilder {

    protected Bike bike = new Bike();

    public abstract void buildSeat();

    public abstract void buildFrame();

    public abstract Bike createBike();

}

摩拜具体建造者(MobikeBikeBuilder)

java
package com.huangjiliang.design.heima.create.builder;

/**
 * 摩拜单车具体构建者
 */
public class MobikeBikeBuilder extends BikeBuilder {
    @Override
    public void buildSeat() {
        bike.setSeat("真皮车座");
    }

    @Override
    public void buildFrame() {
        bike.setFrame("铝合金车架");
    }

    @Override
    public Bike createBike() {
        return bike;
    }
}

ofo具体建造者(OfoBikeBuilder)

java
package com.huangjiliang.design.heima.create.builder;

/**
 * Ofo自行车具体构建者
 */
public class OfoBikeBuilder extends BikeBuilder {
    @Override
    public void buildSeat() {
        bike.setSeat("橡胶车座");
    }

    @Override
    public void buildFrame() {
        bike.setFrame("碳钎维车架");
    }

    @Override
    public Bike createBike() {
        return bike;
    }
}

指挥者(BikeDirector)

java
package com.huangjiliang.design.heima.create.builder;

/**
 * 自行车构建指挥者
 */
public class BikeDirector {

    private BikeBuilder builder;

    public BikeDirector(BikeBuilder builder) {
        this.builder = builder;
    }

    public Bike construct() {
        builder.buildFrame();
        builder.buildSeat();
        return builder.createBike();
    }
}

客户端(Client)

java
package com.huangjiliang.design.heima.create.builder;

/**
 * 测试类
 */
public class Client {

    public static void main(String[] args) {
        showBike(new MobikeBikeBuilder());
        showBike(new OfoBikeBuilder());
    }

    private static void showBike(BikeBuilder builder) {
        BikeDirector director = new BikeDirector(builder);
        Bike bike = director.construct();
        System.out.println(bike.getFrame());
        System.out.println(bike.getSeat());
    }
}

上面实例是Builder模式的常规用法,指挥者类Director在建造者模式中具有很重要的作用,它用于指挥具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,但是有些情况下需要简化系统结构,可以把指挥者类和抽象类进行结合

java
// 抽象 builder 类
public abstract class BikeBuilder {

    protected Bike bike = new Bike();

    public abstract void buildFrame();
    public abstract void buildSeat();
    public abstract Bike createBike();
    // 把原本放在指挥者类的功能移到建造者类中,可以省区创建指挥者类。但是增加了建造者类的功能负担
    public Bike construct() {
        this.buildFrame();
        this.BuildSeat();
        return this.createBike();
    }
}

这样做确实简化了系统结构,但同时也加重了抽象建造者类的职责,也不是太符合单一职责原则,如果construct()方法过于复杂,建议还是封装到Director中

优缺点

优点

  • 建造者模式的封装性很好,使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此将主要的业务逻辑封装在指挥者类中对整体而言取得比较好的稳定性
  • 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品
  • 可以更加精细的控制产品的创建过程。将复杂的产品的创建分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
  • 建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本不用修改之前已经测试通过的代码,因此就不会对原有功能引入风险。符合开闭原则

缺点

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制
  • 如果产品变动,增加部件功能,需要修改建造者相关类,所以要求产品时比较稳定的

使用场景

建造者模式创建的是复杂对象,其产品的各个部分经常面临着剧烈变化,但将他们组合在一起的算法相对稳定,所以它通常在一下场合中使用:

  • 创建的对象较为复杂,由多个部件构成,各部件面临着复杂的变化,但构建间的建造者顺序是稳定的
  • 创建复杂对象的算法独立于该对象的组成部分以及他们的装配方式,即产品的构建过程和最终的表示是独立的

扩展模式

建造者模式除了上面的用例外,在开发中还有一个常用的使用方式,就是当一个类构造器需要传入很多参数时,如果创建这个类的实例,代码可读性会非常差,而且很容易引起错误,此时就可以利用建造者模式进行重构

代码

原始版本

java
package com.huangjiliang.design.heima.create.builder.extend;

public class Phone {

    private String cpu;
    private String screen;
    private String memory;
    private String mainboard;

    public Phone(String cpu, String screen, String memory, String mainboard) {
        this.cpu = cpu;
        this.screen = screen;
        this.memory = memory;
        this.mainboard = mainboard;
    }

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public String getScreen() {
        return screen;
    }

    public void setScreen(String screen) {
        this.screen = screen;
    }

    public String getMemory() {
        return memory;
    }

    public void setMemory(String memory) {
        this.memory = memory;
    }

    public String getMainboard() {
        return mainboard;
    }

    public void setMainboard(String mainboard) {
        this.mainboard = mainboard;
    }

}
java
package com.huangjiliang.design.heima.create.builder.extend;

public class PhoneClient {

    // 创建Phone
    public static void main(String[] args) {
        Phone phone = new Phone("intel","三星屏幕","金士顿","华硕");
        System.out.println(phone);
    }

}

改进版本

java
package com.huangjiliang.design.heima.create.builder.extend;

/**
 * 因phone构造器传如很多参数,容易犯错
 * 为此使用建造者模式重构
 */
public class PhonePlus {

    private String cpu;
    private String screen;
    private String memory;
    private String mainboard;

    public PhonePlus() {}

    private PhonePlus(Builder builder) {
        cpu = builder.cpu;
        screen = builder.screen;
        memory = builder.memory;
        mainboard = builder.mainboard;
    }

    public static final class Builder {
        private String cpu;
        private String screen;
        private String memory;
        private String mainboard;

        public Builder() {}

        public Builder cpu(String val) {
            cpu = val;
            return this;
        }
        public Builder screen(String val) {
            screen = val;
            return this;
        }
        public Builder memory(String val) {
            memory = val;
            return this;
        }
        public Builder mainboard(String val) {
            mainboard = val;
            return this;
        }

        public PhonePlus build() {
            return new PhonePlus(this);
        }
    }

}
java
package com.huangjiliang.design.heima.create.builder.extend;

public class PhonePlusClient {

    public static void main(String[] args) {
        // 实现链式变成
        PhonePlus phonePlus = new PhonePlus.Builder().cpu("intel").mainboard("华硕").memory("金士顿").screen("三星").build();
    }
}

重构后的代码在使用起来更方便,某种程度上上也可以提高开发效率。

创建者模式对比

工厂方法模式VS建造者模式

1、工厂方法模式注重的是整体对象的创建方法;而建造者模式注重的是部件构建的过程,意在一步一步精确构造创建出一个复杂的对象

2、举例:如要制造一个超人,如果使用工厂方法模式,直接产生出来的就是一个力大无穷‘能够飞翔、内裤外穿的超人;而如果使用建造者模式,则需要组装手、脚、头、躯干等部分,然后再把内裤外穿,这样一个超人才会诞生

抽象工厂方法VS建造者模式

1、抽象工厂模式实现对产品家族的创建,一个产品家族是这样的一系列的产品;具有不同分类维度的产品组合,采用抽象工厂模式则不需要关心构建过程,只是关心什么产品由什么工厂产生即可

2、建造者模式则是按照指定的蓝图建造产品,它的主要目的是通过组装零配件产生一个新的产品

如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车