JAVA设计模式总结—建造者模式

建造者模式

模式动机与定义

​ 首先建造者模式的动机是为了创建复杂对象,简化传统的创建方法,提高创建的效率和可读性

​ 像图中的这个例子,用户的需求是驾驶一辆汽车,但是对于用户来说是不需要了解汽车装配厂汽车的具体装配过程,用户只是使用汽车整个整体。

​ 所以在这个例子中,用户只需要知道我要类型为汽车的对象就够了,而不需要知道这个复杂对象到底是怎么组成的,我理解的就是, 建造者模式就是提供一个接口,实现接受用户一个汽车类型的参数,然后通过内部建造,返回一个汽车对象。

模式结构与分析

模式定义

  • 建造者模式(Builder Pattern):将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

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

构建过程:汽车的组装过程
不同的表示: 构建不同的汽车,比如有轿车,电动汽车,运动型汽车

比如,汽车有很多组成部分,但是用户不需要每个组成部分都要指定,用户可以通过指定汽车的一些特点,比如电动车,自动挡这些特征,就可以创建一辆车的对象, 但是具体车是怎么组装的,什么先后顺序,其他没有指定的部分的特征,比如车的高度就不需要知道。

  • 将客户端与包含多个部件的复杂对象的创建过程分离,客户端无须知道复杂对象的内部组成部分与装配方式,只需要知道所需建造者的类型即可
  • 关注如何逐步创建一个复杂的对象,不同的建造者定义了不同的创建过程

模式结构

  • Product: 最终要生成的对象,例如 Computer实例。
  • Builder: 构建者的抽象基类(有时会使用接口代替)。其定义了构建Product的抽象步骤,其实体类需要实现这些步骤。其会包含一个用来返回最终产品的方法Product getProduct()
  • ConcreteBuilder: Builder的实现类。
  • Director: 决定如何构建最终产品的算法. 其会包含一个负责组装的方法void Construct(Builder builder), 在这个方法中通过调用builder的方法,就可以设置builder,等设置完成后,就可以通过builder的 getProduct() 方法获得最终的产品。

时序图

模式实例与解析

​ 比如有一个老板现在需要造一个高楼,这个老板在建造者模式就是一个客户类(client),调用Director的建造方法,然后建造者模式中的指挥者类(Director)就相当于是一个包工头,他把建造的工作分配给底下的工人,而不同的工人可以建造的房屋类型不同,就比如有些工人只会建造普通的屋子,有些工人可以建造高楼这些工人就是具体的建造者(ConcreteBuilder),指挥者就通过客户端的需求给具体的工人分配工作,建造不同的房屋,房屋就是建造者模式里的产品类(product)

参考类图

​ 相比之前的标准建造者类图,该类图多了一个Client客户类,客户类依赖指挥者类和具体的建造者类,具体的建造者和product是依赖关系,具体的建造者继承抽象建造者,指挥者和抽象建造是聚合关系。

参考代码

Product:产品,对应例子中的房子

public class House {
    // 地基
    private String baise;
    // 墙面
    private String wall;
    // 屋顶
    private String roofed;
    @Override
    public String toString() {
        return "House{" +
                "baise='" + baise + '\'' +
                ", wall='" + wall + '\'' +
                ", roofed='" + roofed + '\'' +
                '}';
    }
    /**
     * get set方法
     */
}

Builder: 抽象的建造者,对应例子中的工人

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

ConcreteBuilder: 具体的建造者,对应例子中的具体工人

// 盖普通房子的工人
public class CommonHouse extends HouseBuilder{
    @Override
    public void buildBasic() {
        System.out.println("普通房子打地基5m");
        house.setBaise("5m");
    }
    @Override
    public void buildWalls() {
        System.out.println("普通房子砌墙10cm");
        house.setWall("10cm");
    }
    @Override
    public void buildRoofed() {
        System.out.println("普通房子屋顶");
        house.setRoofed("common");
    }
}
// 盖高楼的工人
public class HighBuilding extends HouseBuilder{
    @Override
    public void buildBasic() {
        System.out.println("高房打地基10m");
        house.setBaise("10m");
    }
    @Override
    public void buildWalls() {
        System.out.println("高楼砌墙20cm");
        house.setWall("20cm");
    }
    @Override
    public void buildRoofed() {
        System.out.println("高楼屋顶");
        house.setRoofed("high");
    }
}

Director: 指挥者类,这里对应例子中的包工头

// 指挥者,这里去指定制作流程,返回产品
public class HouseDirector {
    HouseBuilder houseBuilder = null;
    // 构造器传入houseBuilder
    public HouseDirector(HouseBuilder houseBuilder){
        this.houseBuilder = houseBuilder;
    }
    // 通过set传入houseBuilder
    public void setHouseBuilder(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }
    // 指挥HouseBuilder如何建造房子,建造房子的流程
    public House constructHouse() {
        houseBuilder.buildBasic();
        houseBuilder.buildWalls();
        houseBuilder.buildRoofed();
        return houseBuilder.buildHouse();
    }
}

Client: 客户类,这里对应例子中的老板

public class Client {
    public static void main(String[] args) {
        // 盖普通房子
        CommonHouse commonHouse = new CommonHouse();
        // 准备盖普通房子的指挥者
        HouseDirector houseDirector = new HouseDirector(commonHouse);
        // 完成盖房子,返回产品
        House house = houseDirector.constructHouse();
    }
}

运行结果:

普通房子打地基5m
普通房子砌墙10cm
普通房子屋顶
House{baise='5m', wall='10cm', roofed='common'}

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

StringBuilder

下面的例子是StringBuilder的简单应用

public class Builder {
    public static void main(String[] args) {
        StringBuilder stringBuilder = new StringBuilder("hello,");
        stringBuilder.append("world");
        System.out.println(stringBuilder);
    }
}

现在来看一下StringBuilder的定义

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, Comparable<StringBuilder>, CharSequence{
    ...
    public StringBuilder append(StringBuffer sb) {
        super.append(sb);
        return this;
    }
    ...
}

可以看到StringBuilder在调用append方法后,是使用了super的方法进行调用,StringBuilder在此充当了指挥者角色,同时充当了具体的建造者,建造方法的实现是由AbstractStringBuilder完成,而StringBuilder继承了AbstractStringBuilder,所以接下来我们再来看看AbstractStringBuilder的定义

abstract class AbstractStringBuilder implements Appendable, CharSequence {
   ...
     public AbstractStringBuilder append(StringBuffer sb) {
        return this.append((AbstractStringBuilder)sb);
    }
   ...
}

AbstractStringBuilder 实现了 Appendable接口方法,这里的AbstractStringBuilder 已经是建造者,只是不能实例化

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

Appendable接口定义了多个append方法(抽象方法),即Appendable为抽象建造者,定义了抽象方法

模式效果与应用

建造者模式优点:

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

  • 每一个具体建造者都相对独立,与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,扩展方便,符合开闭原则

  • 可以更加精细地控制产品的创建过程

建造者模式缺点:

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,不适合使用建造者模式,因此其使用范围受到一定的限制
  • 如果产品的内部变化复杂,可能会需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加了系统的理解难度和运行成本

在以下情况下可以使用建造者模式:

  • 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员变量

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

  • 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中

  • 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品

posted @ 2021-10-31 17:05  linkcxt  阅读(290)  评论(0)    收藏  举报