创建一个对象,必定要设置该对象的属性,简单的对象还好,但一个复杂的对象具有很多复杂的属性,每次我们创建一个复杂对象后还需要设置它大量的属性,这显然浪费了时间。为了解决这个问题,我们可以考虑给该对象的属性设置一个有效的默认值,这样就有了一套默认的模板,我们创建对象时就只需要创建这个模板就行。但是,我们想要要多套默认的模板该怎么办呢?这个时候就可以通过设计模式来实现。
建造者模式主要解决一个复杂对象的创建工作,将其构建过程与表示过程进行分离,使其在相同的构建过程中可以创建出不同的模板。
工厂模式和建造者模式都能帮助我们创建一个完整的对象,但工厂模式管理多个类,为多个类创建对象。而建造者模式只管理一个类,更重视对一个类的对象进行构建。
以汉堡店的套餐为例,汉堡店中有多个套餐,每个套餐中有多个产品。现在我要创建一个套餐对象,但我不想一遍遍的设置该对象名称是什么,有哪些东西,价格是多少。于是我为套餐类添加一个建造者类来管理套餐对象。
package 设计模式.建造者模式;
public class 食物类 {
private String 名称;
private Double 价格;
public void 设置名称(String 名称) {
this.名称 = 名称;
}
public void 设置价格(Double 价格) {
this.价格 = 价格;
}
public String 获取名称() {
return 名称;
}
public Double 获取价格() {
return 价格;
}
}
package 设计模式.建造者模式;
import java.util.ArrayList;
import java.util.List;
public class 套餐类 {
private String 套餐名;
private List<食物类> 食物列表 = new ArrayList<>();
private Double 价格;
public void 设置套餐名(String 套餐名){
this.套餐名 = 套餐名;
}
public void 添加食物(食物类 食物){
食物列表.add(食物);
}
public void 展示套餐(){
System.out.println(套餐名 + ":");
价格 = 0d;
for (食物类 食物 : 食物列表) {
System.out.println(" " + 食物.获取名称() + "\t单价:" + 食物.获取价格());
价格 += 食物.获取价格();
}
System.out.println("套餐价格:" + 价格);
}
}
package 设计模式.建造者模式;
public class 建造者类 {
public 套餐类 构造小霸王套餐(){
套餐类 小霸王 = new 套餐类();
小霸王.设置套餐名("小霸王套餐");
// 创建食物——霸王鸡腿堡
食物类 食物 = new 食物类();
食物.设置名称("霸王鸡腿堡");
食物.设置价格(12d);
// 为套餐添加食物
小霸王.添加食物(食物);
// 创建食物——薯条
食物 = new 食物类();
食物.设置名称("薯条");
食物.设置价格(10d);
// 为套餐添加食物
小霸王.添加食物(食物);
// 创建食物——可乐
食物 = new 食物类();
食物.设置名称("中杯可乐");
食物.设置价格(4.5);
// 为套餐添加食物
小霸王.添加食物(食物);
return 小霸王;
}
public 套餐类 构造大霸王套餐(){
套餐类 大霸王 = new 套餐类();
大霸王.设置套餐名("大霸王套餐");
// 创建食物——霸王鸡腿堡
食物类 食物 = new 食物类();
食物.设置名称("双层霸王鸡腿堡");
食物.设置价格(16d);
// 为套餐添加食物
大霸王.添加食物(食物);
// 创建食物——薯条
食物 = new 食物类();
食物.设置名称("薯条");
食物.设置价格(10d);
// 创建食物——上校鸡块
食物 = new 食物类();
食物.设置名称("上校鸡块");
食物.设置价格(10d);
// 为套餐添加食物
大霸王.添加食物(食物);
// 创建食物——可乐
食物 = new 食物类();
食物.设置名称("大杯可乐");
食物.设置价格(4.5);
// 为套餐添加食物
大霸王.添加食物(食物);
return 大霸王;
}
}
package 设计模式.建造者模式;
public class 测试类 {
public static void main(String[] args) {
套餐类 套餐 = new 建造者类().构造小霸王套餐();
套餐.展示套餐();
System.out.println();
套餐 = new 建造者类().构造大霸王套餐();
套餐.展示套餐();
}
}
通过上面的方法,我的确实现了构造与展示分离(所有的构造操作都在建造者类中完成,调用者无需关心构造的过程)。但此模式的结构还能优化,当前我们每添加一种套餐,就需要在建造者类中创建大量的食物对象,而这些食物对象(部分)价格和名称都是不变的,我们改变的只是套餐这个整体罢了,于是我想可以让建造者来管理部分的创建,也就是添加各个食物,再创建一个指挥者类来管理整体套餐的创建,我们现在只用和指挥者沟通即可完成套餐的创建。
方法和之前的一样,没有改动
package 设计模式.建造者模式.优化;
import 设计模式.建造者模式.套餐类;
import 设计模式.建造者模式.食物类;
public class 套餐建造者类 {
private 套餐类 套餐;
private 套餐建造者类(){
// 可以直接在此方法中新建套餐,用new方法创建建造者。但我喜欢使用静态方法来创建,实现全部链式调用
}
/**
* 初始化操作
* @return
*/
public static 套餐建造者类 准备套餐(){
套餐建造者类 建造者 = new 套餐建造者类();
建造者.套餐 = new 套餐类();
return 建造者;
}
public 套餐建造者类 设置套餐名(String 套餐名){
套餐.设置套餐名(套餐名);
return this;
}
private void 添加食物(String 食物名, Double 价格){
食物类 食物 = new 食物类();
食物.设置名称(食物名);
食物.设置价格(价格);
套餐.添加食物(食物);
}
public 套餐建造者类 添加霸王鸡腿堡(){
添加食物("霸王鸡腿堡",12d);
return this;
}
public 套餐建造者类 添加双层霸王鸡腿堡(){
添加食物("双层霸王鸡腿堡",16d);
return this;
}
public 套餐建造者类 添加薯条(){
添加食物("薯条",10d);
return this;
}
public 套餐建造者类 添加上校鸡块(){
添加食物("上校鸡块",10d);
return this;
}
public 套餐建造者类 添加可乐(String 大小){
// 模拟更复杂的创建方法
String 名称 = 大小 + "可乐";
Double 价格 = 4d;
价格 = 大小.equals("小杯") ? 2d : 价格;
价格 = 大小.equals("大杯") ? 5d : 价格;
添加食物(名称, 价格);
return this;
}
public 套餐类 建造完成(){
return 套餐;
}
}
package 设计模式.建造者模式.优化;
import 设计模式.建造者模式.套餐类;
public class 指挥者类 {
public 套餐类 构造小霸王套餐(){
return 套餐建造者类
.准备套餐()
.设置套餐名("小霸王套餐")
.添加霸王鸡腿堡()
.添加薯条()
.添加可乐("小杯")
.建造完成();
}
public 套餐类 构造大霸王套餐(){
return 套餐建造者类.准备套餐()
.设置套餐名("大霸王套餐")
.添加双层霸王鸡腿堡()
.添加上校鸡块()
.添加可乐("中杯")
.建造完成();
}
public 套餐类 构造至尊大霸王套餐(){
return 套餐建造者类.准备套餐()
.设置套餐名("至尊大霸王套餐")
.添加双层霸王鸡腿堡()
.添加上校鸡块()
.添加薯条()
.添加可乐("大杯")
.建造完成();
}
}
package 设计模式.建造者模式;
import 设计模式.建造者模式.优化.指挥者类;
public class 测试类 {
public static void main(String[] args) {
套餐类 套餐 = new 指挥者类().构造小霸王套餐();
套餐.展示套餐();
System.out.println();
套餐 = new 指挥者类().构造大霸王套餐();
套餐.展示套餐();
System.out.println();
套餐 = new 指挥者类().构造至尊大霸王套餐();
套餐.展示套餐();
}
}
使用链式调用方法可以非常方便的完成一个对象的创建。
package 设计模式.建造者模式.链式调用;
public class Computer {
private String cpu;
private String videoCard;
private String memory;
private String storageSpaces;
private Computer(Builder builder){
cpu = builder.cpu;
videoCard = builder.videoCard;
memory = builder.memory;
storageSpaces = builder.storageSpaces;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", videoCard='" + videoCard + '\'' +
", memory='" + memory + '\'' +
", storageSpaces='" + storageSpaces + '\'' +
'}';
}
public static class Builder{
private String cpu;
private String videoCard;
private String memory;
private String storageSpaces;
public Builder setCpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder setVideoCard(String videoCard) {
this.videoCard = videoCard;
return this;
}
public Builder setMemory(String memory) {
this.memory = memory;
return this;
}
public Builder setStorageSpaces(String storageSpaces) {
this.storageSpaces = storageSpaces;
return this;
}
public Computer build(){
return new Computer(this);
}
}
}
package 设计模式.建造者模式.链式调用;
public class Test {
public static void main(String[] args) {
Computer computer = new Computer.Builder().setCpu("CORE i7-9750")
.setMemory("16GB")
.setVideoCard("GTX1660 Ti 6G")
.setStorageSpaces("1TB SSD + 1TB HDD")
.build();
System.out.println(computer);
}
}
}
}
}