建造者模式
1、建造者模式介绍
建造者模式是一种创建型设计模式,用于将一个复杂对象的构造过程与其表示分离开来,使得同样的构造过程可以创建不同的表示。该模式通过建造者类和指导者类的协作,将复杂的构造过程简化,并且增加了灵活性和可维护性。
1.1 建造者模式结构图
Builder:是为创建一个Product对象的各个部件指定的抽象接口
ConcreteBuilder:是具体建造者,实现Builder接口,构造和装配各个部件。(实现具体的构建过程)
Product:产品角色。
Director:指挥者(指导者),是构建一个使用Builder接口的对象。
1.2 建造者模式基本代码
Product
类:
/** * @author Shier * CreateTime 2023/4/25 22:26 * 产品类 */ public class Product { ArrayList<String> parts = new ArrayList<>(); /** * 添加新的产品部件 * @param part */ public void add(String part){ parts.add(part); } /** * 列举所有的产品 */ public void show(){ for (String part : parts) { System.out.println(part); } } }
Builder 类
/** * @author Shier * CreateTime 2023/4/25 22:30 * 抽象建造者类 */ public abstract class Builder { /** * 建造部件A */ public abstract void buildPartA(); /** * 建造部件B */ public abstract void buildPartB(); /** * 获得产品建造后返回结果 * @return */ public abstract Product getResult(); }
ConcreteBuilderA 类:
/** * @author Shier * CreateTime 2023/4/25 22:33 * 具体建造者A */ public class ConcreteBuilderA extends Builder{ private Product product =new Product(); @Override public void buildPartA() { System.out.println("部件A"); } @Override public void buildPartB() { System.out.println("部件B"); } @Override public Product getResult() { return product; } }
ConcreteBuilderB类与ConcreteBuilderA 类一样的,只是B的产品是C和D,不在重复写。
Director类:
/** * @author Shier * CreateTime 2023/4/25 22:36 * 指导者类 */ public class Director { public void construct(Builder builder){ builder.buildPartA(); builder.buildPartB(); } }
客户端类:
/** * @author Shier * CreateTime 2023/4/25 22:37 * 客户端代码 */ public class Client { public static void main(String[] args) { Director director = new Director(); ConcreteBuilderA builderA = new ConcreteBuilderA(); ConcreteBuilderB builderB = new ConcreteBuilderB(); // 指导者用ConcreteBuilderA创建方法来建造产品 director.construct(builderA); // A建造者创建的产品是A和B Product resultA = builderA.getResult(); resultA.show(); // 指导者用ConcreteBuilderB创建方法来建造产品 director.construct(builderB);// B建造者创建的产品是C和D Product resultB = builderB.getResult(); resultB.show(); } }
建造者模式是在当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时适用的模式。
2、具体案例
画小人,要求是小人要有头、身体、两手、两脚就可以了。
2.1 不使用建造者模式-画小人
/** * @author Shier * CreateTime 2023/4/25 22:45 */ public class Test extends JFrame { public Test() { setSize(400, 400); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocationRelativeTo(null); setVisible(true); } public void paint(Graphics graphics) { //瘦小人 graphics.drawOval(150, 120, 30, 30); //头 graphics.drawRect(160, 150, 10, 50); //身体 graphics.drawLine(160, 150, 140, 200); //左手 graphics.drawLine(170, 150, 190, 200); //右手 graphics.drawLine(160, 200, 145, 250); //左脚 graphics.drawLine(170, 200, 185, 250); //右脚 胖小人 //graphics.drawOval(250, 120, 30, 30); //头 //graphics.drawOval(245, 150, 40, 50); //身体 //graphics.drawLine(250, 150, 230, 200); //左手 //graphics.drawLine(280, 150, 300, 200); //右手 //graphics.drawLine(260, 200, 245, 250); //左脚 //graphics.drawLine(270, 200, 285, 250); //右脚 } public static void main(String[] args) { new Test(); } }
如果在画一个胖小人,就是取消注释就可,但是有没有可能你复制别人的或者自己写的时候会漏写了代码,就有可能出现小人是缺胳膊少腿的情况。
就算将瘦小人和胖小人分别单独为一个类,但是这样还是避免不了缺胳膊少腿的情况呀。
所以说可以直接使用建造者模式,将两者普通同的部分抽取到抽象类当中
2.2 使用建造者模式-画小人
建造小人抽象类:
/** * @author Shier * CreateTime 2023/4/25 23:01 * 建造小人抽象类 */ public abstract class PersonBuilder { protected Graphics graphics; public PersonBuilder(Graphics graphics) { this.graphics = graphics; } // 头 public abstract void buildHead(); // 身体 public abstract void buildBody(); // 左手 public abstract void buildArmLeft(); // 右手 public abstract void buildArmRight(); // 右腿 public abstract void buildLegRight(); // 左腿 public abstract void buildLegLeft(); }
/** * @author Shier * CreateTime 2023/4/25 22:53 * 胖小人 */ public class PersonFatBuilder extends PersonBuilder { public PersonFatBuilder(Graphics graphics) { super(graphics); } /** * 头 */ @Override public void buildHead() { graphics.drawOval(250, 120, 30, 30); } /** * 身体 */ @Override public void buildBody() { graphics.drawOval(245, 150, 40, 50); } /** * 左手 */ @Override public void buildArmLeft() { graphics.drawLine(250, 150, 230, 200); } /** * 右手 */ @Override public void buildArmRight() { graphics.drawLine(280, 150, 300, 200); } /** * 右腿 */ @Override public void buildLegRight() { graphics.drawLine(270, 200, 285, 250); } /** * 左腿 */ @Override public void buildLegLeft() { graphics.drawLine(260, 200, 245, 250); } }
瘦小人类:
/** * @author Shier * CreateTime 2023/4/25 22:53 * 瘦小人 */ public class PersonThinBuilder extends PersonBuilder { public PersonThinBuilder(Graphics graphics) { super(graphics); } /** * 头 */ @Override public void buildHead() { graphics.drawOval(150, 120, 30, 30); } /** * 身体 */ @Override public void buildBody() { graphics.drawRect(160, 150, 10, 50); } /** * 左手 */ @Override public void buildArmLeft() { graphics.drawLine(160, 150, 140, 200); } /** * 右手 */ @Override public void buildArmRight() { graphics.drawLine(170, 150, 190, 200); } /** * 右腿 */ @Override public void buildLegRight() { graphics.drawLine(170, 200, 185, 250); } /** * 左腿 */ @Override public void buildLegLeft() { graphics.drawLine(160, 200, 145, 250); } }
建造人指挥类-指导建造怎么样的小人
指挥者(Director),用它来控制建造过程,也用它来隔离用户与建造过程的关联
/** * @author Shier * CreateTime 2023/4/25 23:08 */ public class PersonDirector { private PersonBuilder personBuilder; /** * 初始化时指定要画什么样的人 * @param personBuilder */ public PersonDirector(PersonBuilder personBuilder) { this.personBuilder = personBuilder; } public void CreatePerson(){ personBuilder.buildHead(); personBuilder.buildBody(); personBuilder.buildArmLeft(); personBuilder.buildArmRight(); personBuilder.buildLegRight(); personBuilder.buildLegLeft(); } }
测试类:
/** * @author Shier * CreateTime 2023/4/25 22:45 */ public class Test extends JFrame { public Test() { setSize(400, 400); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocationRelativeTo(null); setVisible(true); } public void paint(Graphics graphics) { // 建造瘦小人 PersonBuilder thinBuilder = new PersonThinBuilder(graphics); PersonDirector thinDirector = new PersonDirector(thinBuilder); thinDirector.CreatePerson(); // 建造胖小人 PersonFatBuilder fatBuilder = new PersonFatBuilder(graphics); PersonDirector fatDirector = new PersonDirector(fatBuilder); fatDirector.CreatePerson(); } public static void main(String[] args) { new Test(); } }
在抽象类中创建了普遍存在的功能,只要继承抽象类就可实现复杂的构建过程,复杂构建过程的实现都在PersonBuilderA/B中实现了,表示在指挥者类实现
如果还有新的需求就是直接继承PersonBuilder类,让客户端调用实现即可。但是如果我要画人的五官,手指等人的细节的地方,又该如何实现?
如果这些细节是每个具体的小人都需要构建的,那就应该要加进去,反之就没必要。其实建造者模式是逐步建造产品的,所以建造者的Builder类里的那些建造方法必须要足够普遍,以便为各种类型的具体建造者构造。
2.3 电脑组件案例(简易完整版建造者模式)
我们要生产一台电脑,它具有CPU、显卡、内存等组件,每个组件都有不同的品牌、型号、参数等属性。我们可以使用建造者模式来实现其构建:
首先,需要定义电脑类Computer,它具有CPU、显卡、内存等组件属性;
然后,定义ComputerBuilder抽象类,其中包含设置CPU、设置显卡、设置内存等抽象方法,用于实现电脑组装过程的各个步骤;
接着,定义具体的电脑建造者类A、B、C等,分别实现ComputerBuilder中的抽象方法,构建不同的电脑配置;
最后,定义Director类,该类包含了构建电脑的方法buildComputer,其中封装了调用ComputerBuilder中各个步骤的顺序和逻辑,通过注入不同的具体建造者类,可以构建出不同的电脑对象。
这样,使用建造者模式可以将电脑的构建逻辑封装在具体建造者中,客户端代码仅需通过Director调用buildComputer方法来指定需要的电脑构建方式,从而实现了将复杂对象的构建与表示分离。它可以将对象的构建过程抽象化,使得相同的构建过程可以创建不同的表示。相比于直接在构造函数中设置可选参数,建造者模式使用setter的方式进行初始化,非常的灵活
那么看懂的要求,再来看看具体的代码实现吧Computer类:也就是上面建造者模式的Product类
/** * @author Shier * CreateTime 2023/4/26 22:01 */ public class Computer { private String cpu; private String gpu; private String memory; public void setGpu(String gpu) { this.gpu = gpu; } public void setMemory(String memory) { this.memory = memory; } public void setCpu(String cpu) { this.cpu = cpu; } @Override public String toString() { return "Computer:{" + "cpu='" + cpu + '\'' + ", gpu='" + gpu + '\'' + ", memory=" + memory + '}'; } }
ComputerBuilder类:
/** * @author Shier * CreateTime 2023/4/26 22:02 * ComputerBuilder抽象类 */ public abstract class ComputerBuilder { protected Computer computer; public Computer getComputer() { return computer; } /** * 构建Computer类 */ public void createNewComputer(){ computer = new Computer(); } public abstract void buildCpu(); public abstract void buildGpu(); public abstract void buildMemory(); }
三个具体的建造者类:每一个都实现ComputerBuilder中的方法,并定义自己的电脑配置
ComputerBuilderA:
/** * @author Shier * CreateTime 2023/4/26 22:05 * ComputerBuilderA 具体建造者 */ public class ComputerBuilderA extends ComputerBuilder{ @Override public void buildCpu() { computer.setCpu("Intel i7-12500H"); } @Override public void buildGpu() { computer.setGpu("NVIDIA GeForce RTX 1650"); } @Override public void buildMemory() { computer.setMemory("16G"); } }
ComputerBuilderB:
/** * @author Shier * CreateTime 2023/4/26 22:05 * ComputerBuilderB 具体建造者 */ public class ComputerBuilderB extends ComputerBuilder{ @Override public void buildCpu() { computer.setCpu("Intel i9 4090X"); } @Override public void buildGpu() { computer.setGpu("NVIDIA GeForce RTX 4090"); } @Override public void buildMemory() { computer.setMemory("64G"); } }
ComputerBuilderC:
/** * @author Shier * CreateTime 2023/4/26 22:05 * ComputerBuilderC 具体建造者 */ public class ComputerBuilderC extends ComputerBuilder{ @Override public void buildCpu() { computer.setCpu("AMD Ryzen 9 5900X"); } @Override public void buildGpu() { computer.setGpu("NVIDIA GeForce RTX 3080"); } @Override public void buildMemory() { computer.setMemory("24G"); } }
Director类:
/** * @author Shier * CreateTime 2023/4/26 22:10 * 创建Director类,用于指挥具体建造者类构建出特定的对象电脑对象: */ public class Director { private ComputerBuilder computerBuilder; public void setComputerBuilder(ComputerBuilder computerBuilder){ this.computerBuilder = computerBuilder; } public Computer getComputer(){ return computerBuilder.getComputer(); } public void buildComputer(){ computerBuilder.createNewComputer(); computerBuilder.buildCpu(); computerBuilder.buildGpu(); computerBuilder.buildMemory(); } }
测试类:
/** * @author Shier * CreateTime 2023/4/26 22:13 * main方法中使用Director构建具体的电脑对象 */ public class ComputerTest { public static void main(String[] args) { // 创建Director类 Director director = new Director(); // 具体建造者A ComputerBuilderA builderA = new ComputerBuilderA(); director.setComputerBuilder(builderA); // 让DIrector类构建A的电脑配置 director.buildComputer(); // 在Director类中进行构建 Computer computerA = director.getComputer(); System.out.println("电脑A的配置:"); System.out.println(computerA.toString()); // 创建电脑建造者B ComputerBuilder builderB = new ComputerBuilderB(); director.setComputerBuilder(builderB); director.buildComputer(); Computer computerB = director.getComputer(); System.out.println("电脑B的配置:"); System.out.println(computerB.toString()); // 创建电脑建造者C ComputerBuilder builderC = new ComputerBuilderC(); director.setComputerBuilder(builderC); director.buildComputer(); Computer computerC = director.getComputer(); System.out.println("电脑C的配置:"); System.out.println(computerC.toString()); } }
最后输出结果:
这就是一个简单的建造者模式实现,通过不同的具体建造者类构建出了三个不同的电脑对象,并实现了将复杂对象的构建与表示分离。
3、总结
适用场景:
建造者模式主要用于创建一些复杂的对象,这些对象内部子对象的建造顺序通常是稳定的,但每个子对象本身的构建通常面临着复杂的变化。
建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。
建造者模式优点:
可以将一个复杂对象的构造过程与其表示分离开来,并且可以使得同样的构造过程可以创建不同的表示。
建造者模式可以让客户端不必知道产品构建的细节,从而降低了系统的耦合度。
可以使用建造者模式逐步构建一个复杂的对象,从而可以控制对象构建的过程及其顺序。
可以隔离复杂对象的创建和使用,并且可以提高代码的复用性。
建造者模式缺点:
建造者模式需要为每一个需要创建的产品定义一个具体的建造者类,从而会增加系统中类的个数。
由于建造者模式要求建造者必须按照指定的顺序进行构造对象,因此可能会对其灵活性产生一定的影响。
em.out.println(“电脑C的配置:”);
System.out.println(computerC.toString());
}
}
最后输出结果: [外链图片转存中...(img-i5ciGKgu-1685679055776)] > 这就是一个简单的建造者模式实现,通过不同的具体建造者类构建出了三个不同的电脑对象,并实现了将复杂对象的构建与表示分离。 ## 3、总结 适用场景: - 建造者模式主要用于创建一些复杂的对象,这些对象内部子对象的建造顺序通常是稳定的,但每个子对象本身的构建通常面临着复杂的变化。 - 建造者模式的好处就是使得建造代码与表示代码分离,由于建造者隐藏了该产品是如何组装的,所以若需要改变一个产品的内部表示,只需要再定义一个具体的建造者就可以了。 建造者模式优点: 1. 可以将一个复杂对象的构造过程与其表示分离开来,并且可以使得同样的构造过程可以创建不同的表示。 2. 建造者模式可以让客户端不必知道产品构建的细节,从而降低了系统的耦合度。 3. 可以使用建造者模式逐步构建一个复杂的对象,从而可以控制对象构建的过程及其顺序。 4. 可以隔离复杂对象的创建和使用,并且可以提高代码的复用性。 建造者模式缺点: 1. 建造者模式需要为每一个需要创建的产品定义一个具体的建造者类,从而会增加系统中类的个数。 2. 由于建造者模式要求建造者必须按照指定的顺序进行构造对象,因此可能会对其灵活性产生一定的影响。