本文章仅用于本人学习笔记记录
微信:A20991212A(如本文档内容侵权了您的权益,请您通过微信联系到我)
建造者模式介绍
建造者模式所完成的内容就是通过将多个简单对象通过一步步的组装构建出一个复杂对象的过程。
例如你玩王者荣耀的时的初始化界面;有三条路、有树木、有野怪、有守卫塔等等,甚至依赖于你的网络情况会控制清晰度。而当你换一个场景进行其他不同模式的选择时,同样会建设道路、树木、野怪等等,但是他们的摆放和大小都有不同。这里就可以用到建造者模式来初始化游戏元素。
而这样的根据相同的物料,不同的组装所产生出的具体的内容,就是建造者模式的最终意图,也就是;将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
简单例子

这里我们模拟装修公司对于设计出一些套餐装修服务的场景。
很多装修公司都会给出自家的套餐服务,一般有;欧式豪华、轻奢田园、现代简约等等,而这些套餐的后面是不同的商品的组合。例如;一级&二级吊顶、多乐士涂料、圣象地板、马可波罗地砖等等,按照不同的套餐的价格选取不同的品牌组合,最终再按照装修面积给出一个整体的报价。
这里我们就模拟装修公司想推出一些套餐装修服务,按照不同的价格设定品牌选择组合,以达到使用建造者模式的过程。
场景模拟工程
itstack-demo-design-3-00
└── src
└── main
└── java
└── org.itstack.demo.design
├── ceilling
│ ├── LevelOneCeiling.java
│ └── LevelTwoCeiling.java
├── coat
│ ├── DuluxCoat.java
│ └── LiBangCoat.java
│ └── LevelTwoCeiling.java
├── floor
│ ├── DerFloor.java
│ └── ShengXiangFloor.java
├── tile
│ ├── DongPengTile.java
│ └── MarcoPoloTile.java
└── Matter.java在模拟工程中提供了装修中所需要的物料;ceilling(吊顶)、coat(涂料)、floor(地板)、tile(地砖),这么四项内容。(实际的装修物料要比这个多的多)
场景简述
物料接口
public interface Matter {
String scene(); // 场景;地板、地砖、涂料、吊顶
String brand(); // 品牌
String model(); // 型号
BigDecimal price(); // 价格
String desc(); // 描述
}物料接口提供了基本的信息,以保证所有的装修材料都可以按照统一标准进行获取。
吊顶(ceiling)
public class LevelOneCeiling implements Matter {
public String scene() {
return "吊顶";
}
public String brand() {
return "装修公司自带";
}
public String model() {
return "一级顶";
}
public BigDecimal price() {
return new BigDecimal(260);
}
public String desc() {
return "造型只做低一级,只有一个层次的吊顶,一般离顶120-150mm";
}
}public class LevelTwoCeiling implements Matter {
public String scene() {
return "吊顶";
}
public String brand() {
return "装修公司自带";
}
public String model() {
return "二级顶";
}
public BigDecimal price() {
return new BigDecimal(850);
}
public String desc() {
return "两个层次的吊顶,二级吊顶高度一般就往下吊20cm,要是层高很高,也可增加每级的厚度";
}
}涂料(coat)
public class DuluxCoat implements Matter {
public String scene() {
return "涂料";
}
public String brand() {
return "多乐士(Dulux)";
}
public String model() {
return "第二代";
}
public BigDecimal price() {
return new BigDecimal(719);
}
public String desc() {
return "多乐士是阿克苏诺贝尔旗下的著名建筑装饰油漆品牌,产品畅销于全球100个国家,每年全球有5000万户家庭使用多乐士油漆。";
}
}
public class LiBangCoat implements Matter {
public String scene() {
return "涂料";
}
public String brand() {
return "立邦";
}
public String model() {
return "默认级别";
}
public BigDecimal price() {
return new BigDecimal(650);
}
public String desc() {
return "立邦始终以开发绿色产品、注重高科技、高品质为目标,以技术力量不断推进科研和开发,满足消费者需求。";
}
}
地板(floor)
public class DerFloor implements Matter {
public String scene() {
return "地板";
}
public String brand() {
return "德尔(Der)";
}
public String model() {
return "A+";
}
public BigDecimal price() {
return new BigDecimal(119);
}
public String desc() {
return "DER德尔集团是全球领先的专业木地板制造商,北京2008年奥运会家装和公装地板供应商";
}
}
public class ShengXiangFloor implements Matter {
public String scene() {
return "地板";
}
public String brand() {
return "圣象";
}
public String model() {
return "一级";
}
public BigDecimal price() {
return new BigDecimal(318);
}
public String desc() {
return "圣象地板是中国地板行业著名品牌。圣象地板拥有中国驰名商标、中国名牌、国家免检、中国环境标志认证等多项荣誉。";
}
}
地砖(tile)
public class DongPengTile implements Matter {
public String scene() {
return "地砖";
}
public String brand() {
return "东鹏瓷砖";
}
public String model() {
return "10001";
}
public BigDecimal price() {
return new BigDecimal(102);
}
public String desc() {
return "东鹏瓷砖以品质铸就品牌,科技推动品牌,口碑传播品牌为宗旨,2014年品牌价值132.35亿元,位列建陶行业榜首。";
}
}
public class MarcoPoloTile implements Matter {
public String scene() {
return "地砖";
}
public String brand() {
return "马可波罗(MARCO POLO)";
}
public String model() {
return "缺省";
}
public BigDecimal price() {
return new BigDecimal(140);
}
public String desc() {
return "“马可波罗”品牌诞生于1996年,作为国内最早品牌化的建陶品牌,以“文化陶瓷”占领市场,享有“仿古砖至尊”的美誉。";
}
}
以上就是本次装修公司所提供的装修配置单,接下我们会通过案例去使用不同的物料组合出不同的套餐服务。
用一坨坨代码实现
工程结构
itstack-demo-design-3-01
└── src
└── main
└── java
└── org.itstack.demo.design
└── DecorationPackageController.java
一个类几千行的代码你是否见过,嚯?那今天就让你见识一下有这样潜质的类!
ifelse实现需求
public class DecorationPackageController {
public String getMatterList(BigDecimal area, Integer level) {
List<Matter> list = new ArrayList<Matter>(); // 装修清单
BigDecimal price = BigDecimal.ZERO; // 装修价格
// 豪华欧式
if (1 == level) {
LevelTwoCeiling levelTwoCeiling = new LevelTwoCeiling(); // 吊顶,二级顶
DuluxCoat duluxCoat = new DuluxCoat(); // 涂料,多乐士
ShengXiangFloor shengXiangFloor = new ShengXiangFloor(); // 地板,圣象
list.add(levelTwoCeiling);
list.add(duluxCoat);
list.add(shengXiangFloor);
price = price.add(area.multiply(new BigDecimal("0.2")).multiply(levelTwoCeiling.price()));
price = price.add(area.multiply(new BigDecimal("1.4")).multiply(duluxCoat.price()));
price = price.add(area.multiply(shengXiangFloor.price()));
}
// 轻奢田园
if (2 == level) {
LevelTwoCeiling levelTwoCeiling = new LevelTwoCeiling(); // 吊顶,二级顶
LiBangCoat liBangCoat = new LiBangCoat(); // 涂料,立邦
MarcoPoloTile marcoPoloTile = new MarcoPoloTile(); // 地砖,马可波罗
list.add(levelTwoCeiling);
list.add(liBangCoat);
list.add(marcoPoloTile);
price = price.add(area.multiply(new BigDecimal("0.2")).multiply(levelTwoCeiling.price()));
price = price.add(area.multiply(new BigDecimal("1.4")).multiply(liBangCoat.price()));
price = price.add(area.multiply(marcoPoloTile.price()));
}
// 现代简约
if (3 == level) {
LevelOneCeiling levelOneCeiling = new LevelOneCeiling(); // 吊顶,二级顶
LiBangCoat liBangCoat = new LiBangCoat(); // 涂料,立邦
DongPengTile dongPengTile = new DongPengTile(); // 地砖,东鹏
list.add(levelOneCeiling);
list.add(liBangCoat);
list.add(dongPengTile);
price = price.add(area.multiply(new BigDecimal("0.2")).multiply(levelOneCeiling.price()));
price = price.add(area.multiply(new BigDecimal("1.4")).multiply(liBangCoat.price()));
price = price.add(area.multiply(dongPengTile.price()));
}
StringBuilder detail = new StringBuilder("\r\n-------------------------------------------------------\r\n" +
"装修清单" + "\r\n" +
"套餐等级:" + level + "\r\n" +
"套餐价格:" + price.setScale(2, BigDecimal.ROUND_HALF_UP) + " 元\r\n" +
"房屋面积:" + area.doubleValue() + " 平米\r\n" +
"材料清单:\r\n");
for (Matter matter: list) {
detail.append(matter.scene()).append(":").append(matter.brand()).append("、").append(matter.model()).append("、平米价格:").append(matter.price()).append(" 元。\n");
}
return detail.toString();
}
}
首先这段代码所要解决的问题就是接收入参;装修面积(area)、装修等级(level),根据不同类型的装修等级选择不同的材料。
其次在实现过程中可以看到每一段if块里,都包含着不通的材料(吊顶,二级顶、涂料,立邦、地砖,马可波罗),最终生成装修清单和装修成本。
最后提供获取装修详细信息的方法,返回给调用方,用于知道装修清单。
建造者模式重构代码
建造者模式主要解决的问题是在软件系统中,有时候面临着”一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的过程构成;由于需求的变化,这个复杂对象的各个部分经常面临着重大的变化,但是将它们组合在一起的过程却相对稳定。
这里我们会把构建的过程交给创建者类,而创建者通过使用我们的构建工具包,去构建出不同的装修套餐。
工程结构
itstack-demo-design-3-02
└── src
├── main
│ └── java
│ └── org.itstack.demo.design
│ ├── Builder.java
│ ├── DecorationPackageMenu.java
│ └── IMenu.java
└── test
└── java
└── org.itstack.demo.design.test
└── ApiTest.java

工程中有三个核心类和一个测试类,核心类是建造者模式的具体实现。与ifelse实现方式相比,多出来了两个二外的类。具体功能如下;
- Builder,建造者类具体的各种组装由此类实现。
- DecorationPackageMenu,是IMenu接口的实现类,主要是承载建造过程中的填充器。相当于这是一套承载物料和创建者中间衔接的内容。
代码实现
定义装修包接口
public interface IMenu {
IMenu appendCeiling(Matter matter); // 吊顶
IMenu appendCoat(Matter matter); // 涂料
IMenu appendFloor(Matter matter); // 地板
IMenu appendTile(Matter matter); // 地砖
String getDetail(); // 明细
}
接口类中定义了填充各项物料的方法;吊顶、涂料、地板、地砖,以及最终提供获取全部明细的方法。
装修包实现
public class DecorationPackageMenu implements IMenu {
private List<Matter> list = new ArrayList<Matter>(); // 装修清单
private BigDecimal price = BigDecimal.ZERO; // 装修价格
private BigDecimal area; // 面积
private String grade; // 装修等级;豪华欧式、轻奢田园、现代简约
private DecorationPackageMenu() {
}
public DecorationPackageMenu(Double area, String grade) {
this.area = new BigDecimal(area);
this.grade = grade;
}
public IMenu appendCeiling(Matter matter) {
list.add(matter);
price = price.add(area.multiply(new BigDecimal("0.2")).multiply(matter.price()));
return this;
}
public IMenu appendCoat(Matter matter) {
list.add(matter);
price = price.add(area.multiply(new BigDecimal("1.4")).multiply(matter.price()));
return this;
}
public IMenu appendFloor(Matter matter) {
list.add(matter);
price = price.add(area.multiply(matter.price()));
return this;
}
public IMenu appendTile(Matter matter) {
list.add(matter);
price = price.add(area.multiply(matter.price()));
return this;
}
public String getDetail() {
StringBuilder detail = new StringBuilder("\r\n-------------------------------------------------------\r\n" +
"装修清单" + "\r\n" +
"套餐等级:" + grade + "\r\n" +
"套餐价格:" + price.setScale(2, BigDecimal.ROUND_HALF_UP) + " 元\r\n" +
"房屋面积:" + area.doubleValue() + " 平米\r\n" +
"材料清单:\r\n");
for (Matter matter: list) {
detail.append(matter.scene()).append(":").append(matter.brand()).append("、").append(matter.model()).append("、平米价格:").append(matter.price()).append(" 元。\n");
}
return detail.toString();
}
}
装修包的实现中每一个方法都会了 this,也就可以非常方便的用于连续填充各项物料。
同时在填充时也会根据物料计算平米数下的报价,吊顶和涂料按照平米数适量乘以常熟计算。
最后同样提供了统一的获取装修清单的明细方法。
建造者方法
public class Builder {
public IMenu levelOne(Double area) {
return new DecorationPackageMenu(area, "豪华欧式")
.appendCeiling(new LevelTwoCeiling()) // 吊顶,二级顶
.appendCoat(new DuluxCoat()) // 涂料,多乐士
.appendFloor(new ShengXiangFloor()); // 地板,圣象
}
public IMenu levelTwo(Double area){
return new DecorationPackageMenu(area, "轻奢田园")
.appendCeiling(new LevelTwoCeiling()) // 吊顶,二级顶
.appendCoat(new LiBangCoat()) // 涂料,立邦
.appendTile(new MarcoPoloTile()); // 地砖,马可波罗
}
public IMenu levelThree(Double area){
return new DecorationPackageMenu(area, "现代简约")
.appendCeiling(new LevelOneCeiling()) // 吊顶,二级顶
.appendCoat(new LiBangCoat()) // 涂料,立邦
.appendTile(new DongPengTile()); // 地砖,东鹏
}
}
建造者的使用中就已经非常容易了,统一的建造方式,通过不同物料填充出不同的装修风格;豪华欧式、轻奢田园、现代简约,如果将来业务扩展也可以将这部分内容配置到数据库自动生成。但整体的思想还可以使用创建者模式进行搭建。
测试验证
@Test
public void test_Builder(){
Builder builder = new Builder();
// 豪华欧式
System.out.println(builder.levelOne(132.52D).getDetail());
// 轻奢田园
System.out.println(builder.levelTwo(98.25D).getDetail());
// 现代简约
System.out.println(builder.levelThree(85.43D).getDetail());
}
总结
- 通过上面对建造者模式的使用,已经可以摸索出一点心得。那就是什么时候会选择这样的设计模式,当:一些基本物料不会变,而其组合经常变化的时候,就可以选择这样的设计模式来构建代码。
- 此设计模式满足了单一职责原则以及可复用的技术、建造者独立、易扩展、便于控制细节风险。但同时当出现特别多的物料以及很多的组合后,类的不断扩展也会造成难以维护的问题。但这种设计结构模型可以把重复的内容抽象到数据库中,按照需要配置。这样就可以减少代码中大量的重复。
- 建造者模式所完成的内容就是通过将多个简单对象通过一步步的组装构建出一个复杂对象的过程。
- 就是操作不断的返回自身对象,然后返回的自身对象又不断的操作。