文章目录
0.更新日志
- 2019-4-2 第一次完成此文章V1.0.0
- 2019-4-2 修改文章第7节思路PS内容的错字,文章版本更新至V1.0.1
- 2019-4-10 更新标题,增加了“java版”字样,文章版本更新至V1.0.2
1.建造者模式概述
假设用户需要一辆车。而一辆车由方向盘,轮胎,发动机等部件组成。
如何将这些部件组装成一辆完整的汽车并返回给用户? 使用建造者模式。
- 建造者模式可以将部件本身和它们的组装过程分开,关注如何一步步创建一个包含多个组成部分的复杂对象,用户只需要指定复杂对象的类型即可得到该对象,而无须知道其内部的具体构造细节。
2.建造者模式定义
定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
要点:
- 将客户端与包含多个部件的复杂对象的创建过程分离,客户端无须知道复杂对象的内部组成部分与装配方式,只需要知道所需建造者的类型即可
- 关注如何逐步创建一个复杂的对象,不同的建造者定义了不同的创建过程
结构:
怎么理解上一张图片?(可以直接看入门例子再回来尝试理解这个例子)
建造者模式包含以下4个角色:
Builder(抽象建造者)
ConcreteBuilder(具体建造者)
Product(产品)
Director(指挥者)
按照上面造车的例子来说
- Director类相当于汽车工程师,定义一个指挥方法create()(即上图的Contruct()方法)。
- Product类应该为汽车类,包含有成员:轮胎类,方向盘类,发动机类。
- Builder类是抽象接口,定义造产品的方法:build轮胎(),build方向盘(),build发动机(),并且有一个返回产品的方法 get宝马()(即getResult()方法)。
- 具体建造者类(按照工厂模式可以理解为生产工厂,这里也可以认为是生产工人):宝马类Builder。这个类实现Builder接口(即给出具体怎么创建那些部件)。
- Director类里面在指挥方法create()内控制组装顺序,即是先 build轮胎 还是先 build发动机 ,最后调用get宝马()方法获得一台宝马,还要把这台宝马return给用户。
3.建造者模式入门例子
博主 易灬小侠:java建造者模式–给一个你一看就懂的建造者模式 https://blog.csdn.net/u010102390/article/details/80179754
这已经是非常浅显易懂的例子了,有助于初学者初步建立建造者模式的思想。
看完上述博客里的例子后能够不在提示的情况下复现上述例子就算成功理解建造者思想了。
4.建造者模式的几种情况
-
多个产品类的情况
多个具体Builder类实现Builder接口
-
只需要一个建造者的情况
可以不写接口,直接实现具体Builder类
-
省略指挥者(Director)的情况
直接在具体Builder类中定义并实现指挥方法
5.建造者模式优缺点以及适用场景
- 优点
- 客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
- 每一个具体建造者都相对独立,与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,扩展方便,符合开闭原则。
- 可以更加精细地控制产品的创建过程。
- 缺点
-
建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,不适合使用建造者模式,因此其使用范围受到一定的限制。
-
如果产品的内部变化复杂,可能会需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加了系统的理解难度和运行成本。
- 适用场景
- 需要生成的产品对象有复杂的内部结构,这些产品对象通常包含多个成员变量
- 需要生成的产品对象的属性相互依赖,需要指定其生成顺序。
- 对象的创建过程独立于创建该对象的类。在建造者模式中通过引入了指挥者类,将创建过程封装在指挥者类中,而不在建造者类和客户类中。
- 隔离复杂对象的创建和使用,并使得相同的创建过程可以创建不同的产品。
6.建造者模式与工厂模式的异同
- 两者区别(不同点):
- 建造者模式关心细节,工厂模式关心整体。
- 产品的复杂度不同。工厂方法模式创建的产品一般都是单一性质产品,都是一个模样,而建造者模式创建的则是一个复合产品,它由各个部件复合而成。
- 两者相同点:
- 两个模式都是用于创建产品的。
- 都符合开闭原则。
7.建造者模式应用例子
问题:现有电脑产品可以根据客户需求选用不用的CPU、RAM、主板形成游戏用电脑和办公用电脑,请试用建造者模式描述该场景。
目录结构如下:(PS:马赛克的地方是多加了一个产品进去,无需注意)
思路:(PS:如果有什么不懂的地方,请看上述的建造者模式UML图理解)
- 根据UML图,我们可以先创建产品类两个GameComputer类和OfficeComputer类,然后抽出它们共同之处创建一个父类Computer类,让两个子类继承父类。
public abstract class Computer {
public CPU cpu;
public RAM ram;
public ZB zb;//主板
//创建完三个成员变量后在来给出get和set方法!!!
public CPU getCpu() {
return cpu;
}
public void setCpu(String type) {
this.cpu.setType(type);
System.out.println(type);
}
public RAM getRam() {
return ram;
}
public void setRam(String type) {
this.ram.setType(type);
System.out.println(type);
}
public ZB getZb() {
return zb;
}
public void setZb(String type) {
this.zb.setType(type);
System.out.println(type);
}
public void showComputer() {};//展示产品配置用的
}
CPU类、RAM类、ZB类
public class CPU {
private String type;//类型,比如i7-6200U,i5-6200U
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public void showCPU() {
System.out.println(type);
}
}
public class RAM {
private String type;//类型
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public void showRAM() {
System.out.println(type);
}
}
public class ZB {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public void showZBType() {
System.out.println(type);
}
}
两个Computer子类继承父类
public class GameComputer extends Computer{//游戏电脑
public GameComputer() {
super.cpu=new CPU();
super.ram=new RAM();
super.zb=new ZB();
}
@Override
public void showComputer() {
System.out.println("GameComputer CPU:"+super.cpu.getType()+",RAM:"
+super.ram.getType()+",ZB:"+super.zb.getType());
}
}
public class OfficeComputer extends Computer{//办公电脑
public OfficeComputer() {
super.cpu=new CPU();
super.ram=new RAM();
super.zb=new ZB();
}
@Override
public void showComputer() {
System.out.println("OfficeComputer CPU:"+super.cpu.getType()+",RAM:"
+super.ram.getType()+",ZB:"+super.zb.getType());
}
}
- 搞清楚产品的关系后,我们来实现Builder类。先创建一个Builder接口。
public interface Builder {
public void buildCPU();//具体生产CPU
public void buildRAM();
public void buildZB();
public Computer buildComputer();//返回一个完成的产品,不可缺少
}
- 然后我们来一个产品对应一个Builder类(像不像工厂模式的一个工厂生产一个产品),创建两个具体Builder类:GCBuilder类和OCBuilder类。
public class GCBuilder implements Builder{//游戏电脑建造者
private Computer gc = new GameComputer();
@Override
public void buildCPU() {
gc.setCpu("i9-7980XE");
}
@Override
public void buildRAM() {
gc.setRam("金士顿掠夺者3000");
}
@Override
public void buildZB() {
gc.setZb("华硕Prime X470 Pro");
}
@Override
public Computer buildComputer() {
System.out.println("gameComputer created!");
return gc;
}
}
public class OCBuilder implements Builder{
private Computer oc=new OfficeComputer();
@Override
public void buildCPU() {
oc.setCpu("速龙X4 840");
}
@Override
public void buildRAM() {
oc.setRam("复仇者2400");
}
@Override
public void buildZB() {
oc.setZb("技嘉B450");
}
@Override
public Computer buildComputer() {
System.out.println("officeComputer created!");
return oc;
}
}
- 构建指挥者Director类,指挥电脑组装顺序。
public class Director {//根据客户端传入的参数的不同来确定执行哪个方法
//游戏电脑组装
public Computer createComputer(GCBuilder gcb) {//组装顺序1.主板,2.CPU,3.RAM
gcb.buildZB();
gcb.buildCPU();
gcb.buildRAM();
return gcb.buildComputer();//返回一台组装完成的电脑
}
//办公电脑组装
public Computer createComputer(OCBuilder ocb) {
ocb.buildZB();
ocb.buildCPU();
ocb.buildRAM();
return ocb.buildComputer();
}
}
- 测试一下
public class Main {
public static void main(String[] args) {
Director director = new Director();
GCBuilder gcb = new GCBuilder();
OCBuilder ocb = new OCBuilder();
director.createComputer(gcb).showComputer();//展示电脑配置信息
director.createComputer(ocb).showComputer();
//也可以在这创建一个具体电脑类接收如:OfficeComputer oc = (OfficeComputer) director.createComputer(ocb);
//或者用Computer类接收也可以
}
}
结果:
8.思考
- 如果增加一种中档电脑上面代码该如何改变?
(1)首先创建一个中档电脑类继承Computer类
(2)创建一个具体建造者类MCBuilder类实现Builder接口
(3)指挥者Director类增加一个方法public Computer createComputer(MCBuilder mcb) - 怎样将(抽象)工厂模式和建造者模式结合使用?(具体代码就不给出来了,篇幅太长)
要回答这个问题可以先看看我之前写的抽象工厂模式例子代码
https://blog.csdn.net/qq_36445854/article/details/88700477
(1)上面的建造者应用例子可以看到组装电脑可以用具体Builder类来组装。那么CPU类、RAM类、ZB(主板)类这种部件就可以用(抽象)工厂模式来生产,因为CPU类可以作为父类衍生出子类:CPU1类、CPU2类、CPU3类等不同的产品等级结构。
(2)这样就可以发现,建造者模式关心电脑的组成,具体的部件生产让(抽象)工厂模式去关心就行了(具体Builder类build部件方法改成从工厂获得再放入电脑类成员变量里就行了)。
9.参考博客
博主 易灬小侠:java建造者模式–给一个你一看就懂的建造者模式 https://blog.csdn.net/u010102390/article/details/80179754
博主 Memset:【设计模式】 模式PK:工厂模式VS建造者模式
https://www.cnblogs.com/ChinaHook/p/7471470.html