设计模式之建造者模式

文章详细介绍了建造者模式的概念、应用场景及优缺点,并通过电脑配置的例子展示了从简单到复杂的改进过程,包括如何通过建造者接口稳定建造过程,引入指挥者类以隐藏建造细节,以及如何处理大量属性的构造问题。最后对比了建造者模式与工厂模式的区别。
摘要由CSDN通过智能技术生成

1、建造者模式

建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。

这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

一个 Builder 类会一步一步构造最终的对象。该 Builder 类是独立于其他对象的。

现在有一个需求:用户买电脑

@Data
@Accessors(chain = true)
class Compute {
    private String cpu;
    private String gpu;
    private String memory;
    private String hd;
}

-------------------------------------------时空线-----------------------------------------------
public class BuilderTest {
    public static void main(String[] args) {
        Compute compute = new Compute()
                .setCpu("i7-10700")
                .setGpu("3080 Ti")
                .setMemory("32G")
                .setHd("1T");
        System.out.println(compute);
    }
}

看一下这样设计的问题是什么:

  • 对象实例化的时候就必须要为每一个属性赋值,比较麻烦
  • 相当于你去配电脑,卖家把所有组件都给你摆出来,让你自己亲自动手组装
  • 这样有可能会不按正确步骤组装对象,或缺少了某一个组件
  • 违反了迪米特法则

第一次改进:

作者额外添加一个功能:直接封装装配电脑的工作。需要专门创建一个ComputeBuilder类,来专门负责组装电脑的过程

@Data
@Accessors(chain = true)
class Compute {
    private String cpu;
    private String gpu;
    private String memory;
    private String hd;
}

// 电脑构建者类,并且必须关联一个产品
class ComputeBuilder {
    private Compute compute = new Compute();

    // 构建方法
    public Compute build() {
        return compute.setCpu("i7-10700")
                .setGpu("3080 Ti")
                .setMemory("32G")
                .setHd("1T");
    }
}
-------------------------------------------时空线-----------------------------------------------------
public class BuilderApp {
    public static void main(String[] args) {
        // 创建一个建造者
        ComputeBuilder cb = new ComputeBuilder();
        // 创建电脑
		// 学习 (cpu好 + 内存 )
		Computer c = cb.build();
		System.out.println(c);
		
		// 玩游戏(显卡好)
		Computer c2 = cb.build();
		System.out.println(c2);
		
		// 娱乐、办公
		Computer c3 = cb.build();
		System.out.println(c3);
    }
}

这样设计的好处是:

  • 由建造者隐藏了创建对象的复杂过程

但缺点是什么?

  • 封装的太狠! 无论客户端是什么需求,客户端没办法选择配置! 都是由服务器端定死的配置!!

我们需要的效果是:

1、针对于不同需求,有不同的建造者。
2、客户端仍然不知道建造电脑的过程。

第二次改进:

@Data
@Accessors(chain = true)
class Computer {
	private String cpu;
	private String gpu;
	private String memory;
	private String hd;
}

class AdvancedComputerBuilder {
	private Computer c = new Computer();

	// 构建方法
	public Computer build() {
		return c.setCpu("i7-10700")
				.setGpu("4090 Ti")
				.setMemory("32G")
				.setHd("2T");
	}
}

class MiddleComputerBuilder {
	private Computer c = new Computer();

	public Computer build() {
		return c.setCpu("i7-10700")
				.setGpu("3080 Ti")
				.setMemory("16G")
				.setHd("1T");
	}
}

class LowComputerBuilder {
	private Computer c = new Computer();

	public Computer build() {
		return c.setCpu("i7-10700")
				.setGpu("1650 Ti")
				.setMemory("16G")
				.setHd("512G");
	}
}
-----------------------------------------------------------------------------------------------------------
public class BuilderApp {
	public static void main(String[] args) {
		AdvancedComputerBuilder acb = new AdvancedComputerBuilder();
		MiddleComputerBuilder mcb = new MiddleComputerBuilder();
		LowComputerBuilder lcb = new LowComputerBuilder();
		// 游戏
		Computer c3 = acb.build();
		System.out.println(c3);
        
		// 学习
		Computer c = mcb.build();
		System.out.println(c);
		
		// 办公、娱乐
		Computer c2 = lcb.build();
		System.out.println(c2);
				
	}
}

虽然满足了上述需求,但还是有问题

1、无论是高 中 低哪一种配置的电脑,配置的过程都是一样的,只不过细节不一样。 既然有重复,那就有了坏味道。
2、这些重复的安装步骤,不稳定,万一哪一个建造者中漏了某一个步骤,那么生产出来的产品就是不合格的,但是编译器却不会报错

改进一下:

1、稳定建造电脑的过程, 定义一个建造者接口
2、让所有具体建造者实现这个接口,那么所有的具体建造者,就必须实现这个接口中的所有方法,就不会遗漏某一个步骤。

第三次改进:

package day01.designPattern;

import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
class Computer {
    private String cpu;
    private String gpu;
    private String memory;
    private String hd;
}
interface IcomputeBuilder {
    IcomputeBuilder cpu();

    IcomputeBuilder gpu();

    IcomputeBuilder memory();

    IcomputeBuilder hd();

    Computer builder();
}

class AdvancedComputerBuilder implements IcomputeBuilder{
    private Computer compute = new Computer();

    @Override
    public IcomputeBuilder cpu() {
        compute.setCpu("i7-10700");
        return this;
    }

    @Override
    public IcomputeBuilder gpu() {
        compute.setGpu("4090 Ti");
        return this;
    }

    @Override
    public IcomputeBuilder memory() {
        compute.setMemory("32G");
        return this;
    }

    @Override
    public IcomputeBuilder hd() {
        compute.setHd("2T");
        return this;
    }

    // 构建方法
    public Computer builder() {
        return compute;
    }
}

class MiddleComputerBuilder implements IcomputeBuilder{
    private Computer compute = new Computer();

    @Override
    public IcomputeBuilder cpu() {
        compute.setCpu("i7-10700");
        return this;
    }

    @Override
    public IcomputeBuilder gpu() {
        compute.setGpu("3080 Ti");
        return this;
    }

    @Override
    public IcomputeBuilder memory() {
        compute.setMemory("16G");
        return this;
    }

    @Override
    public IcomputeBuilder hd() {
        compute.setHd("1T");
        return this;
    }

    // 构建方法
    public Computer builder() {
        return compute;
    }
}

class LowComputerBuilder implements IcomputeBuilder{
    private Computer compute = new Computer();

    @Override
    public IcomputeBuilder cpu() {
        compute.setCpu("i5-10700");
        return this;
    }

    @Override
    public IcomputeBuilder gpu() {
        compute.setGpu("1650 Ti");
        return this;
    }

    @Override
    public IcomputeBuilder memory() {
        compute.setMemory("8G");
        return this;
    }

    @Override
    public IcomputeBuilder hd() {
        compute.setHd("1T");
        return this;
    }

    // 构建方法
    public Computer builder() {
        return compute;
    }
}
----------------------------------------------------------------------------------------------------------
public class BuilderApp {
    public static void main(String[] args) {

        AdvancedComputerBuilder acb = new AdvancedComputerBuilder();
        acb.cpu().gpu().memory().hd();
        Computer c = acb.builder();
        System.out.println(c);


        MiddleComputerBuilder mcb = new MiddleComputerBuilder();
        mcb.cpu().gpu().memory().hd();
        Computer c2 = mcb.builder();
        System.out.println(c2);

        LowComputerBuilder lcb = new LowComputerBuilder();
        lcb.cpu().gpu().memory().hd();
        Computer c3 = lcb.builder();
        System.out.println(c3);
    }
    /**
     * Computer(cpu=i7-10700, gpu=4090 Ti, memory=32G, hd=2T)
     * Computer(cpu=i7-10700, gpu=3080 Ti, memory=16G, hd=1T)
     * Computer(cpu=i5-10700, gpu=1650 Ti, memory=8G, hd=1T)
     */
}

我们看这样进行构建的优点:

建造者类中的建造过程是稳定的,不会漏掉某一步。这样当客户端想扩展建造者时,也不会漏掉某一步

缺点:

1、如果有多个建造者,代码依然会有重复
2、现在又变成了客户端自己配置电脑,又违反了迪米特法则。
(这相当于,你去电脑城配电脑,虽然不用你亲自组装电脑,但是你必须指挥那个装机boy,下一步该干啥,下一步该干啥,虽然隐藏细节,但是还是要指挥)

而我们想要的是连指挥过程也要隐藏起来

第四次改进:

@Data
@Accessors(chain = true)
class Computer {
    private String cpu;
    private String gpu;
    private String memory;
    private String hd;
}
interface IComputeBuilder {
    IComputeBuilder cpu();

    IComputeBuilder gpu();

    IComputeBuilder memory();

    IComputeBuilder hd();

    Computer builder();
}
// 隐藏指挥命令的细节
class Director {
    public Computer build(IComputeBuilder computerBuilder) {
        // 指挥builder进行组装
        return computerBuilder.cpu().gpu().memory().hd().builder();
    }
}
class AdvancedComputerBuilder implements IComputeBuilder{
    private Computer compute = new Computer();

    @Override
    public IComputeBuilder cpu() {
        compute.setCpu("i7-10700");
        return this;
    }

    @Override
    public IComputeBuilder gpu() {
        compute.setGpu("4090 Ti");
        return this;
    }

    @Override
    public IComputeBuilder memory() {
        compute.setMemory("32G");
        return this;
    }

    @Override
    public IComputeBuilder hd() {
        compute.setHd("2T");
        return this;
    }

    // 构建方法
    public Computer builder() {
        return compute;
    }
}

class MiddleComputerBuilder implements IComputeBuilder{
    private Computer compute = new Computer();

    @Override
    public IComputeBuilder cpu() {
        compute.setCpu("i7-10700");
        return this;
    }

    @Override
    public IComputeBuilder gpu() {
        compute.setGpu("3080 Ti");
        return this;
    }

    @Override
    public IComputeBuilder memory() {
        compute.setMemory("16G");
        return this;
    }

    @Override
    public IComputeBuilder hd() {
        compute.setHd("1T");
        return this;
    }

    // 构建方法
    public Computer builder() {
        return compute;
    }
}

class LowComputerBuilder implements IComputeBuilder{
    private Computer compute = new Computer();

    @Override
    public IComputeBuilder cpu() {
        compute.setCpu("i5-10700");
        return this;
    }

    @Override
    public IComputeBuilder gpu() {
        compute.setGpu("1650 Ti");
        return this;
    }

    @Override
    public IComputeBuilder memory() {
        compute.setMemory("8G");
        return this;
    }

    @Override
    public IComputeBuilder hd() {
        compute.setHd("1T");
        return this;
    }

    // 构建方法
    public Computer builder() {
        return compute;
    }
}
-------------------------------------------------------------------------------------------------------
public class BuilderApp {
    public static void main(String[] args) {

        // 创建指挥者
        Director director = new Director();
        //游戏
        AdvancedComputerBuilder acb = new AdvancedComputerBuilder();// 创建一个建造者
        Computer c = director.build(acb);// 由指挥者进行指挥
        System.out.println(c);
        //开发
        MiddleComputerBuilder mcb = new MiddleComputerBuilder();
        Computer c2 = director.build(mcb);
        System.out.println(c2);
        //日常办公
        LowComputerBuilder lcb = new LowComputerBuilder();
        Computer c3 = director.build(acb);
        System.out.println(c3);
    }
    /**
     * Computer(cpu=i7-10700, gpu=4090 Ti, memory=32G, hd=2T)
     * Computer(cpu=i7-10700, gpu=3080 Ti, memory=16G, hd=1T)
     * Computer(cpu=i5-10700, gpu=1650 Ti, memory=8G, hd=1T)
     */
}

至此,建造者设计模式的第一种形态,推演完毕!

优点是:

  • 建造者负责建造,指挥者负责指挥,创建对象的过程是稳定的(IComputerBuilder接口负责稳定),创建对象的过程也不会有重复代码(指挥者完成)
  • 当需要拓展新的产品时,不需要修改原来的代码,只需要实现构建者接口,然后交给指挥者完成即可,这里就将构造和流程分别由构造者和指挥者进行解耦
  • 建造者作为中间层,进行解耦

缺点是,客户端无法灵活控制组件的细节,比如,客户端无法直接指定cpu或者硬盘的型号。

所以才有了建造者模式的第二种形态

当一个类的属性特别多的时候,构造器的参数也就特别多,尤其是构造器的多个参数类型还都一样,那么客户端代码就很容易出错

// 当一个类的属性特别多的时候,构造器的参数也就特别多,尤其是构造器的多个参数类型还都一样。
// 那么客户端代码就很容易出错。
@Data
@Accessors(chain = true)
class Computer {
    private String cpu;
    private String harddisk;
    private String memery;
    private String gpu;
    private String power;
    private String motherboard; // 主板
    private String cdrom;
    private String fan;
    private String netcard;

    public Computer() {
    }
    private Computer(ComputerBuilder cb) {
        this.cpu = cb.cpu;
        this.harddisk = cb.harddisk;
        this.memery = cb.memery;
        this.gpu = cb.gpu;
        this.power = cb.power;
        this.netcard = cb.netcard;
        this.motherboard = cb.motherboard;
        this.fan = cb.fan;
        this.cdrom = cb.cdrom;
    }

    // 以上的构造器,使用起来确实很麻烦! 也许有小伙伴会这样问,我们可以对构造器进行重载,把参数减少, 事实证明,这也很2....

    // 最好的解决方法是使用建造者设计模式的第二种形态
    // 1. 建造者必须是产品的一个静态内部类
    // 2. 建造者的属性完全与产品的所有属性一致
    // 3. 为建造者的每一个属性都添加一个setter方法,且该setter方法必须返回建造者对象本身

    public static class ComputerBuilder {
        private String cpu;
        private String harddisk;
        private String memery;
        private String gpu;
        private String power;
        private String motherboard; // 主板
        private String cdrom;
        private String fan;
        private String netcard;
        public ComputerBuilder setCpu(String cpu) {
            this.cpu = cpu;
            return this;
        }
        public ComputerBuilder setHarddisk(String harddisk) {
            this.harddisk = harddisk;
            return this;
        }
        public ComputerBuilder setMemery(String memery) {
            this.memery = memery;
            return this;
        }
        public ComputerBuilder setGpu(String gpu) {
            this.gpu = gpu;
            return this;
        }
        public ComputerBuilder setPower(String power) {
            this.power = power;
            return this;
        }
        public ComputerBuilder setMotherboard(String motherboard) {
            this.motherboard = motherboard;
            return this;
        }
        public ComputerBuilder setCdrom(String cdrom) {
            this.cdrom = cdrom;
            return this;
        }
        public ComputerBuilder setFan(String fan) {
            this.fan = fan;
            return this;
        }
        public ComputerBuilder setNetcard(String netcard) {
            this.netcard = netcard;
            return this;
        }
        // 构建方法
        public Computer build() {
            System.out.println("组装" + cpu + "," + gpu + "," + memery);
            return new Computer(this);
        }
    }
}


public class Demo02 {
    public static void main(String[] args) {
        Computer.ComputerBuilder cb = new Computer.ComputerBuilder();
        Computer c = cb.setMotherboard("华硕主板").setCpu("酷睿8代").setFan("cpu自带的风扇").setHarddisk("500g").setMemery("32G").build();
        System.out.println(c);
    }
    /**
     * 组装酷睿8代,null,32G
     * Computer(cpu=酷睿8代, harddisk=500g, memery=32G, gpu=null, power=null, motherboard=华硕主板, cdrom=null, fan=cpu自带的风扇, netcard=null)
     */
}

// 建造者设计模式的第二种形态: 参考effective java 第二条2 :当类的属性过多时,就使用建造者设计模式的第二种形态

2、UML类图

在这里插入图片描述

3、建造者模式的优缺点

  • 优点:
    • 创建对象的过程稳定不变,因为有ComputerBuilder接口来稳定过程
    • 创建对象的过程只写了一次,没有重复的代码(指挥者完成)
    • 当需要扩展指挥者的时候,不用修改之前的代码,这符合了开闭原则
  • 缺点:
    • 产品必须有共同点,范围有限制。
    • 如内部变化复杂,会有很多的建造类。

4、建造者模式与工厂模式的区别

  • 工厂模式只负责创建实例,new出对象即可,并不关心里面的属性和构建的过程;
  • 建造者模式更加关注构建的过程,通过一些规范的流程、标准,构建合格的产品
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

King Gigi.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值