likes
comments
collection
share

【设计模式 】| 建造者源码学习与实践

作者站长头像
站长
· 阅读数 4

前言

为什么要用建造者模式?在我们看来他和工厂模式的目的是一样的,就是为了获取对象。下面我们进一步来了解建造者模式是什么,以及他在我们业务开发中的使用场景。

纲要

【设计模式 】| 建造者源码学习与实践

什么是建造者模式?

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

优缺点

【设计模式 】| 建造者源码学习与实践

四大主要角色

【设计模式 】| 建造者源码学习与实践

为什么要用建造者模式?

从两点来考虑

  1. 分阶段、分步骤的方法更适合多次运算结果类创建场景
  2. 不需要关心特定类型的建造者的具体算法实现

封装的变化

  1. 建造器的数量与具体实现
  2. 建造器内部创建多个属性
  3. 建造器的步骤

常用场景

【设计模式 】| 建造者源码学习与实践

实现Bean对象的构建

//建造者的抽象基类(接口)
public interface Builder<T> {

    T  build();
}
//最终构建的对象
public class User {

    private boolean isRef;

    private Object name;

    private String cardId;

    public Object getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
            "name='" + name + '\'' +
            ", cardId='" + cardId + '\'' +
            '}';
    }

    private User(BuilderImpl builder){
        this.name = builder.name;
        this.cardId = builder.cardId;
    }

    private static BuilderImpl builder(){
        return new BuilderImpl();
    }

    // Builder 类的具体实现类 && 指挥者
    private static class BuilderImpl implements Builder<User> {

        private Object name;

        private String cardId;

        private boolean isRef;

        private BuilderImpl name(Object name){
            this.name = name;
            return this;
        }

        private BuilderImpl cardId(String cardId){
            this.cardId = cardId;
            return this;
        }

        private BuilderImpl isRef(boolean isRef){
            this.isRef = isRef;
            return this;
        }

        //指挥者,得到一个新的User对象
        @Override
        public User build(){
            if (!this.isRef){
                if (this.name == null || this.cardId == null){
                    throw new NullPointerException();
                }
            }else {
                if (!(this.name instanceof String)){
                    throw new IllegalArgumentException("name必须为String类型");
                }
            }
            return new User(this);
        }
    }



    public static void main(String[] args) {
        User u = User.builder().isRef(true).name(213).cardId("cardId").build();
        System.out.println(u);
    }
}

上面的实现逻辑,就是Lombok注释中,@Builder的大概实现,我们可以通过set方法设置建造者的变量值,自由组合来完成一个新的对象,

在结尾的build()方法中集中进行数据的校验。

源码学习案例

  1. JDK 类库中的 Appendable 接口

  2. StringBuilder

建造者的抽象基类(接口)

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

Builder 类的具体实现类

abstract class AbstractStringBuilder implements Appendable, CharSequence {

    AbstractStringBuilder() {
    }

    AbstractStringBuilder(int capacity) {
        value = new char[capacity];
    }

    // Documentation in subclasses because of synchro difference
    @Override
    public AbstractStringBuilder append(CharSequence s) {
        if (s == null)
            return appendNull();
        if (s instanceof String)
            return this.append((String)s);
        if (s instanceof AbstractStringBuilder)
            return this.append((AbstractStringBuilder)s);

        return this.append(s, 0, s.length());
    }

  
    @Override
    public AbstractStringBuilder append(CharSequence s, int start, int end) {
        if (s == null)
            s = "null";
        if ((start < 0) || (start > end) || (end > s.length()))
            throw new IndexOutOfBoundsException(
                "start " + start + ", end " + end + ", s.length() "
                + s.length());
        int len = end - start;
        ensureCapacityInternal(count + len);
        for (int i = start, j = count; i < end; i++, j++)
            value[j] = s.charAt(i);
        count += len;
        return this;
    }

    
    @Override
    public AbstractStringBuilder append(char c) {
        ensureCapacityInternal(count + 1);
        value[count++] = c;
        return this;
    }
}

指挥者与具体的建造者

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

    public StringBuilder() {
        super(16);
    }

    public StringBuilder(int capacity) {
        super(capacity);
    }

    public StringBuilder(String str) {
        super(str.length() + 16);
        append(str);
    }

    @Override
    public StringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

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

通过上面的代码可以看出他们之间的关系

  1. Appendable作为基类,提供三个方法
  2. AbstractStringBuilder实现了三个方法
  3. 在StringBuilder中继承了AbstractStringBuilder类,重写父类的方法,履行指导者的作用,调用append方法,返回一个StringBuilder对象

与工厂模式的区别

工厂模式:根据用户选择,来制作不同的食物,汉堡、面条、包子。

建造者模式:根据用户选择,来制作,加什么材料的汉堡。

  1. 工厂是生产某个配件,而建造者是整合配件
  2. 建造者注重步骤,按照步骤组装完整
  3. 工厂注重于创建不同对象

总结

建造者模式在我们业务开发中还是经常使用的, 他帮助我们自由组合创建对象,提高了灵活性,但是增加了代码量。