【浅谈设计模式】(5):建造者模式--组装复杂的实例

1,082 阅读7分钟

微信公众号:潇雷

当努力到一定程度,幸运自会与你不期而遇。

一、初识建造者模式

1.1 概述

今天,我们来学习创建者模式的最后一种:建造者模式(builder)。

先上百度百科:

建造者模式是设计模式的一种,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

怎么理解呢?

这个定义有三个关键点:

  • 复杂对象
  • 构建和表示分离
  • 同样的构建可以创建不同的表示

首先,Builder模式适合复杂对象,这个复杂对象的特点是什么呢?即这个对象的构建由多个部分共同构建而成,并且这个对象的构建比较困难的时候。因此,构建和表示分离开的含义就是,不同的构建,相同的装配,这也就是构建和装配的解耦。同样的构建可以创建不同的表示,即客户端执行同样的步骤,也可以得到不一样的表示结果。例如,我们在某团上买菜,我们挑好后,只需执行下单这个动作,对于顾客来说,这个步骤是固定的,而外卖员则会根据顾客的要求,一步步的装配这些组成部分。

1.2 适用场景

  • 当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用建造者模式。
  • 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。
  • Builder模式不适合创建差异性很大的产品类
  • 产品的构建和最终的表示是独立的。

二、建造者模式的实现

2.1 结构

Builder模式包含如下角色:

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

2.2 例子

既然是建造者模式,就拿建楼来举例子。可以分为别墅(villa)和瓦房(RoofedHouse)。

建造一栋楼是一个复杂的过程,假设需要钢筋(rebar)、水泥(cement)、砖头(brick)三种材料。

2.2.1 定义产品类

(1)定义house实体类

public class House {
    //钢筋
    private String rebar;
    //水泥
    private String cement;
    //砖头
    private String brick;

    public String getRebar() {
        return rebar;
    }

    public void setRebar(String rebar) {
        this.rebar = rebar;
    }

    public String getCement() {
        return cement;
    }

    public void setCement(String cement) {
        this.cement = cement;
    }

    public String getBrick() {
        return brick;
    }

    public void setBrick(String brick) {
        this.brick = brick;
    }

    @Override
    public String toString() {
        return "House{" +
                "rebar='" + rebar + '\'' +
                ", cement='" + cement + '\'' +
                ", brick='" + brick + '\'' +
                '}';
    }
}
2.2.2 定义抽象建造者类
public abstract class HouseModel {
    protected House house = new House();

    public abstract void buildRebar();
    public abstract void buildCement();
    public abstract void buildBrick();

    public abstract House createHouse();
}
2.2.3 定义具体的建造者类
public class VillaHouse extends HouseModel{

    @Override
    public void buildRebar() {
        house.setRebar("别墅钢筋");
    }

    @Override
    public void buildCement() {
        house.setCement("高级水泥");
    }

    @Override
    public void buildBrick() {
        house.setBrick("国家级红砖");
    }

    @Override
    public House createHouse() {
        return house;
    }
}
public class RoofedHouse extends HouseModel{
    @Override
    public void buildRebar() {
        house.setRebar("生锈了的钢筋");
    }

    @Override
    public void buildCement() {
        house.setCement("没粘性的水泥");
    }

    @Override
    public void buildBrick() {
        house.setBrick("土砖头");
    }

    @Override
    public House createHouse() {
        return house;
    }
}
2.2.4 定义指挥者类
public class Director {
    private HouseModel model;
    public Director(HouseModel houseModel){
        this.model=houseModel;
    }
    public House construct(){
        model.buildBrick();
        model.buildRebar();
        model.buildCement();
        return model.createHouse();
    }
}
2.2.5 测试
public class Client {
    public static void main(String[] args) {
        showHouse(new VillaHouse());
        showHouse(new RoofedHouse());
    }
    private static void showHouse(HouseModel houseModel){
        Director director = new Director(houseModel);
        House house = director.construct();
        System.out.println(house.toString());
    }
}

打印结果:

House{rebar='别墅钢筋', cement='高级水泥', brick='国家级红砖'}
House{rebar='生锈了的钢筋', cement='没粘性的水泥', brick='土砖头'}

Director 指挥者类在建造者模式中具有很重要的作用,它用于指导具体的构建者如何构建产品,并可以控制调用次序,并向调用者返回一个完整的产品类。

2.3 优缺点

2.3.1 优点
  • 建造者模式的封装性很好。使用建造者模式可以有效的封装变化,对整体而言可以取得比较好的稳定性。
  • 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
  • 建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,符合开闭原则。
2.3.2 缺点
  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式。

三、建造者模式的其他应用

3.1 多参数代码重构

建造者模式还可以对多参数的代码进行重构。可以自由组合参数,建造者模式的设计一般来说还是比较复杂和灵活的。

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

    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainboard='" + mainboard + '\'' +
                '}';
    }
}

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

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

    private Phone(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 Phone build() {
            return new Phone(this);}
    }
    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainboard='" + mainboard + '\'' +
                '}';
    }
}

public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone.Builder()
                .cpu("intel")
                .mainboard("华硕")
                .memory("金士顿")
                .screen("三星")
                .build();
        System.out.println(phone);
    }
}

3.2 建造者模式在JDK中运用

印象中,一个熟悉的面试题就是stringBuffer 和 StringBuilder 的区别是什么。区别就是StringBuilder是线程不安全的,而StringBuffer是线程安全的。而这个StringBuilder 采用的就是建造者模式。

Appendable 定义了抽象方法,为抽象建造者类

public interface Appendable {

    Appendable append(CharSequence csq) throws IOException;

    Appendable append(CharSequence csq, int start, int end) throws IOException;

    Appendable append(char c) throws IOException;
}

AbstractStringBuilder 实现了Appendable接口方法所有的append()方法

abstract class AbstractStringBuilder implements Appendable, CharSequence {
}

StringBuilder 充当了指挥者角色和具体的建造者角色。返回this对象本身充当产品类。

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{

    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
}

四、小结

本篇文章讲解了建造者模式的概念和简单实现,只能做个简单的入门,真正的建造者模式在源码中的应用还是非常复杂的,只能说有机会再去深入,入门,这篇文章就够了,这也是浅谈设计模式想要达到的目的。下面对这几天学的创建型模式做个简单总结。

建造者模式与工厂模式的区别是建造者模式注重部件构建的过程,一步步地精确构建出一个复杂的对象;而工厂方法注重的整体对象的创建方式。

创建型模式分类:

  • 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。
  • 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。
  • 工厂方法(FactoryMethod)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。
  • 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。
  • 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。