设计模式(六):建造者模式

Published on 2024-03-05 18:00 in 分类: 博客 with 狂盗一枝梅
分类: 博客

一、建造者模式定义

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

说的通俗一点,就是使用生成器模式能够把构建对象的过程分离出来,同样的过程不同的执行方式,可以生成出来不同的对象。

1、适用性

在以下情况使用生成器模式:

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时;
  • 当构造过程必须允许被构造的对象有不同的表示时。

2、四种角色

生成器模式中有四种角色:抽象建造者(Builder)、具体建造者(ConcreteBuilder)、指挥者(Director)、产品(Product),其类图关系如下所示:

生成器模式类图.drawio

产品角色(Product)

表示被构造的复杂对象。

抽象建造者(Builder)

为创建一个Product对象的各个部件指定抽象接口,它相当于制定了一个“蓝图”,按照蓝图上的步骤可以生成对象,但是它不关心建造的细节,细节由具体建造者实现。

具体建造者(ConcreteBuilder)

实现Builder的接口以构造和装配该产品的各个部件。

指挥者(Director)

负责控制产品对象的生产过程,它执行具体Builder的方法,并最终生产出一个Product。

3、生成过程

客户端创建Director和ConcreteBuilder对象,将ConcreteBuilder传给Director,Director调用Builder的方法创建Product对象。

二、建造者模式举例

来看一个维基百科的例子:

/** "Product" */
 class Pizza {
   private String dough = "";
   private String sauce = "";
   private String topping = "";
 
   public void setDough (String dough)     { this.dough = dough; }
   public void setSauce (String sauce)     { this.sauce = sauce; }
   public void setTopping (String topping) { this.topping = topping; }
 }
 
 
 ''/** "Abstract Builder" */''
 abstract class PizzaBuilder {
   protected Pizza pizza;
 
   public Pizza getPizza() { return pizza; }
   public void createNewPizzaProduct() { pizza = new Pizza(); }
 
   public abstract void buildDough();
   public abstract void buildSauce();
   public abstract void buildTopping();
 }
 
 /** "ConcreteBuilder" */
 class HawaiianPizzaBuilder extends PizzaBuilder {
   public void buildDough()   { pizza.setDough("cross"); }
   public void buildSauce()   { pizza.setSauce("mild"); }
   public void buildTopping() { pizza.setTopping("ham+pineapple"); }
 }
 
 /** "ConcreteBuilder" */
 class SpicyPizzaBuilder extends PizzaBuilder {
   public void buildDough()   { pizza.setDough("pan baked"); }
   public void buildSauce()   { pizza.setSauce("hot"); }
   public void buildTopping() { pizza.setTopping("pepperoni+salami"); }
 }
 
 
 ''/** "Director" */''
 class Waiter {
   private PizzaBuilder pizzaBuilder;
 
   public void setPizzaBuilder (PizzaBuilder pb) { pizzaBuilder = pb; }
   public Pizza getPizza() { return pizzaBuilder.getPizza(); }
 
   public void constructPizza() {
     pizzaBuilder.createNewPizzaProduct();
     pizzaBuilder.buildDough();
     pizzaBuilder.buildSauce();
     pizzaBuilder.buildTopping();
   }
 }
 
 /** A customer ordering a pizza. */
 class BuilderExample {
   public static void main(String[] args) {
     Waiter waiter = new Waiter();
     PizzaBuilder hawaiianPizzabuilder = new HawaiianPizzaBuilder();
 
     waiter.setPizzaBuilder ( hawaiianPizzabuilder );
     waiter.constructPizza();
 
     Pizza pizza = waiter.getPizza();
   }
 }

三、框架中的建造者模式

在实际实践过程中,建造者模式可能并不是上面案例中那么严格的有四种角色,调用关系也可能会被简化。对于大佬们来说,接下来我们研究的可能只是信手拈来的一段代码,他们只是参考了建造者模式的思想而已,严格按照建造者模式写法有的时候可能会让事情变得复杂化。

1、Java核心类:StringBuilder类

大家都知道,StringBuilder类是Java中的一个工具类,它提供了可变字符串对象,用于进行字符串的动态拼接和修改,它的常用写法如下所示:

String sb = new StringBuilder("a").append("b").append("c").append("e").toString();

Ok,可以看到,先构造了StringBuilder对象,然后调用了多个append方法,最后调用toString方法获取到sb字符串对象,我们来分析下如果使用了建造者模式,那在这里,四种角色分别是谁?

不管怎么说,先看看StringBuilder类定义

image-20240305165847719

StringBuilder类继承了AbstractStringBuilder类,而AbstractStringBuilder类又实现了Appendable接口

image-20240305170051577

我们调用的append方法原来是从这个接口而来。

那我们就明白了。

Appendable接口是Builder,StringBuilder是ConcreteBuilder,StringBuilder的toString方法返回值String是Product,而使用StringBuilder的类则是Director。

什么叫做使用StringBuilder的类是Director?举个例子

public class Service1{
    
    public String buildProduct(){
        String sb = new StringBuilder("a").append("b").append("c").append("e").toString();
        return sb;
    }
}

在这里Service1就是Director。

完整的类图如下所示:

生成器模式类图-StringBuilder.drawio

2、lombok的@Builder注解

在lombok中,我们经常这么写

@Data
@Builder
public class User {
    private String name;
}

省去了一堆的getter/setter方法,而且还能链式调用创建对象实例

User build = User.builder().name("张三").build();

@Builder注解是个语法糖,它在编译的时候会被编译成如下形式

public class User {
    private String name;

    User(final String name) {
        this.name = name;
    }
    
    /**
    * 省略getter/setter/equals/hashCode/toString等方法
    */

    public static User.UserBuilder builder() {
        return new User.UserBuilder();
    }

    public static class UserBuilder {
        private String name;

        UserBuilder() {
        }

        public User.UserBuilder name(final String name) {
            this.name = name;
            return this;
        }

        public User build() {
            return new User(this.name);
        }

        public String toString() {
            return "User.UserBuilder(name=" + this.name + ")";
        }
    }
}

使用@Builder注解后,Lombok将为被注解的类生成以下内容:

  1. 一个静态内部类作为建造者(Builder):生成一个内部静态类,该类具有与被注解的类相同的属性,并提供链式调用的方法来逐步设置这些属性,最后使用build方法创建对象。
  2. 生成一个builder静态方法,该方法返回生成的静态内部类的实例。

来分析下在上面构造过程中,建造者模式的四个角色分别是谁:

首先,可以确定的是,当前类就是Director,也就是说,谁运行了了User build = User.builder().name("张三").build();这段代码,谁是Director;User无疑,就是Product了,比较关键的是谁是Builder和ConcreteBuilder。

在上面的反编译代码中,我们发现UserBuilder这个静态内部类并没有父类,它也没有实现任何接口,但是它提供了获取Product的方法build,从使用者的角度上来看,它还提供了赋值相关属性的所有方法(实际上是UserBuilder的成员变量属性),它按道理就是个Builder,然而它不是个抽象类。。。。也就是说,UserBuilder这个类,它实际上既是Builder也是ConcreteBuilder。

完整的相关类图如下所示:

生成器模式类图-lombok建造者模式.drawio



END.


#设计模式
目录