设计模式之建造者设计模式

写在前面

不知道,你在工作中有没有使用过lombok,如果你使用过,不知道你有没有使用过其中的@Builder注解,其就会帮我们生成建造者设计模式相关的代码,本文就一起来看下吧!

1:介绍

1.1:什么时候使用建造者设计模式

当一个对象的属性很多,并且在不同的场景下对象创建时需要初始化的属性不同时,可以考虑使用该设计模式,否则就需要创建大量的构造函数,造成代码的臃肿和难以维护,并且对于使用者来说,到底选择哪个构造函数来初始化也会比较麻烦。

1.2:UML类图

工厂方法设计模式,包含如下元素:

1:产品
    待创建的对象
2:抽象构造者
    定义创建产品需要设置的属性
3:具体构造者
    具体产品的构造者,继承抽象构造者,负责设置具体的信息
4:导演
    使用构造者来创建产品

如下图:

设计模式之建造者设计模式_第1张图片

2:实例

源码 。

2.1:场景

按照不同的需求创建手机对象。

2.2:程序

  • 创建产品类
@Data // 可自动生成get、set、toString方法
public class Phone {

    /**
     * 品牌
     */
    private String brand;

    /**
     * 操作系统
     */
    private String os;

    /**
     * 内存大小, Unit: GB
     */
    private Integer ramSize;

    /**
     * 售价
     */
    private Double price;
}
  • 创建抽象构造器
/**
 * 手机抽象建造者的接口
 */
public interface PhoneBuilder {
    /**
     * 设置品牌
     */
    void setBrand();

    /**
     * 设置操作系统
     */
    void setOs();

    /**
     * 设置内存大小
     */
    void setRamSize();

    /**
     * 设置售价
     */
    void setPrice();

    /**
     * 获取 Phone 实例
     * @return
     */
    Phone getPhone();
}
  • 创建两个具体的构造器
    分别创建预定好的苹果手机,和小米手机:
/**
 * 苹果手机建造者
 */
public class IPhoneBuilder implements PhoneBuilder{

    private Phone phone;

    public IPhoneBuilder() {
        phone = new Phone();
    }

    @Override
    public void setBrand() {
        phone.setBrand("Apple");
    }

    @Override
    public void setOs() {
        phone.setOs("IOS");
    }

    @Override
    public void setRamSize() {
        phone.setRamSize(2);
    }

    @Override
    public void setPrice() {
        phone.setPrice(6666.66);
    }

    @Override
    public Phone getPhone() {
        return phone;
    }
}

/**
 * 小米手机建造者
 */
public class MiPhoneBuilder implements PhoneBuilder {

    private Phone phone;

    public MiPhoneBuilder() {
        phone = new Phone();
    }

    @Override
    public void setBrand() {
        phone.setBrand("Xiao Mi");
    }

    @Override
    public void setOs() {
        phone.setOs("Android");
    }

    @Override
    public void setRamSize() {
        phone.setRamSize(8);
    }

    @Override
    public void setPrice() {
        phone.setPrice(1999.99);
    }

    @Override
    public Phone getPhone() {
        return phone;
    }
}
  • 创建导演类
/**
 * 导演: 负责指定手机建造流程
 */
public class PhoneDirector {

    /**
     * 导演指挥建造者完成手机的建造工作
     * @param phoneBuilder
     */
    public void construct(PhoneBuilder phoneBuilder) {
        phoneBuilder.setBrand();
        phoneBuilder.setOs();
        phoneBuilder.setRamSize();
        phoneBuilder.setPrice();
    }
}
  • 测试
@Test
public void origin() {
    // 1. 创建一个导演
    PhoneDirector phoneDirector = new PhoneDirector();

    /***** 2. 建造 iPhone 手机 *****/
    
    // 2a. 创建一个 iPhone建造者
    IPhoneBuilder iPhoneBuilder = new IPhoneBuilder();
    // 2b. 导演指导 iPhone建造者 来建造一个iPhone的实例
    phoneDirector.construct(iPhoneBuilder);
    // 2c. 从 iPhone建造者 中获取实例
    Phone iPhone = iPhoneBuilder.getPhone();
    System.out.println(iPhone);

    /***** 3. 建造 Xiao Mi Phone 手机 *****/
    
    // 3a. 创建一个 MiPhone建造者
    MiPhoneBuilder miPhoneBuilder = new MiPhoneBuilder();
    // 3b. 导演指导 MiPhone建造者 来建造一个MiPhone的实例
    phoneDirector.construct(miPhoneBuilder);
    // 3c. 从 MiPhone建造者 中获取实例
    Phone miPhone = miPhoneBuilder.getPhone();
    System.out.println(miPhone);

}

运行:

Phone(brand=Apple, os=IOS, ramSize=2, price=6666.66)
Phone(brand=Xiao Mi, os=Android, ramSize=8, price=1999.99)

Process finished with exit code 0

需要注意到,这里客户端虽然不需要去设置对象的各种属性信息了,但是仅仅适用于要设置的属性都是确定的,并且要设置的属性值也是确定的场景,如果是要设置哪些属性是不确定,要设置的属性值也是不确定的话,这种方式明显就不使用了,怎么做呢?可以创建对应的构造函数,直接使用构造函数创建,但是可能需要创建非常多的构造函数,会让代码变的臃肿且难以维护。也可以调用无参构造函数,然后分别调用对应的setXxx方法,但是这样程序会变的复杂,且效率低下,因此就有了建造者设计模式的简化版本,这种方式创建一个内部的静态Builder类,之后通过链式调用的方式来设置属性,最终调用build,build内会调用对象的全部参数的构造函数,从而完成对象创建,使用简化版本的建造者设计模式修改Phone类如下:

/**
 * 手机
 */
@ToString
public class Phone {

    /**
     * 品牌
     */
    private String brand;

    /**
     * 操作系统
     */
    private String os;

    /**
     * 内存大小, Unit: GB
     */
    private Integer ramSize;

    /**
     * 售价
     */
    private Double price;

    /**
     * 提供一个静态方法以方便创建一个Phone建造者实例
     * @return
     */
    public static Phone.PhoneBuilder builder() {
        return new Phone.PhoneBuilder();
    }

    /**
     * 提供一个Phone的全参构造器以供建造者Builder来建造Phone实例
     * @param brand
     * @param os
     * @param ramSize
     * @param price
     */
    public Phone(String brand, String os, Integer ramSize, Double price) {
        this.brand = brand;
        this.os = os;
        this.ramSize = ramSize;
        this.price = price;
    }


    /**
     * 静态内部类: Phone Builder 建造者
     */
    public static class PhoneBuilder {

        private String brand;

        private String os;

        private Integer ramSize;

        private Double price;

        /**
         * Builder 建造者构造器
         */
        public PhoneBuilder() {
        }

        public Phone.PhoneBuilder brand(String brand) {
            this.brand = brand;
            return this;
        }

        public Phone.PhoneBuilder os(String os) {
            this.os = os;
            return this;
        }

        public Phone.PhoneBuilder ramSize(Integer ramSize) {
            this.ramSize = ramSize;
            return this;
        }

        public Phone.PhoneBuilder price(Double price) {
            this.price = price;
            return this;
        }

        /**
         * 建造者通过 Phone的全参构造器 来构造 Phone 实例
         * @return
         */
        public Phone build() {
            return new Phone(brand, os, ramSize, price);
        }
    }
}

测试:

@Test
public void simplify() {
    dongshi.daddy.builder.simplify.Phone P40Pro
            = dongshi.daddy.builder.simplify.Phone.builder()  // 通过产品的静态方法获取建造者
            .brand("华为")         // "客户"(调用方)充当了Director这个角色
            .os("鸿蒙")
            .ramSize(12)
            .price(9999.99)
            .build();             // 获得建造完成的产品

    System.out.println(P40Pro);
}

输出:

Phone(brand=华为, os=鸿蒙, ramSize=12, price=9999.99)

Process finished with exit code 0

这种简化版本的构造者设计模式其实就是我们在工作中经常用的lombok 的@Builder注解,如下:

@Builder
@ToString
public class PhoneWithLombok {

    /**
     * 品牌
     */
    private String brand;

    /**
     * 操作系统
     */
    private String os;

    /**
     * 内存大小, Unit: GB
     */
    private Integer ramSize;

    /**
     * 售价
     */
    private Double price;
}

程序是不是简洁多了,工作中用起来吧!

测试:

@Test
public void simplifyWithLombok() {
    PhoneWithLombok P40Pro
            = PhoneWithLombok.builder()  // 通过产品的静态方法获取建造者
            .brand("华为lombok")         // "客户"(调用方)充当了Director这个角色
            .os("lombok 操作系统")
            .ramSize(12)
            .price(9999.99)
            .build();             // 获得建造完成的产品

    System.out.println(P40Pro);
}

输出:

PhoneWithLombok(brand=华为lombok, os=lombok 操作系统, ramSize=12, price=9999.99)

Process finished with exit code 0

写在后面

参考文章列表

GoF设计模式(五):Builder Pattern 建造者模式 。

你可能感兴趣的:(设计模式,设计模式,builder设计模式,建造者设计模式)