一、概念
建造者模式的定义是:将一个复杂对象的构造与它的表示分离,使同样的构建过程可以创建不同的表示,这样的设计模式被称为建造者模式
建造者模式,顾名思义的就是类似建房子,有一个固定的流程。
建造者模式实现了依赖倒转原则,抽象不应该依赖细节,细节应该依赖于抽象。
二、使用场景
当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用建造者模式。
建造者模式的角色定义,在建造者模式中存在以下4个角色:
- Builder:为创建一个产品对象的各个部件指定抽象接口,构建操作
- ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口
- Director:构造一个使用Builder接口的对象
- Product:表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程, 包含定义组成部件的类,包括将这些部件装配成最终产品的接口
Director使用Builder创建Product
三、代码示例
场景:水果店推出水果套餐,老顾客优惠10元,节假日套餐优惠15元
建造者模式,输入一系列参数,建造一个想要的结果
问题:
1、套餐包含各类水果的组合,较复杂(套餐后续会越来越复杂)。
2、套餐的创建步骤基本固定,设置价格/设置折扣/得到结算价。
3、会不断推出新的套餐。
1、所有套餐都有这四个步骤。
2、套餐会不断增加丰富。
3、订单在结算套餐时,算法与具体套餐无关。
4、会员套餐与假日套餐的配置,交给专业人员(老员工)来做。
builder类,生产套餐接口(为创建一个产品对象的各个部件指定抽象接口)
/**
* 创建一个Builder类,实际的builder类负责创建套餐product对象。
* 也是工厂,生产套餐
*/
public interface Builder {
void setApple(int price, int num);
void setBanana(int price, int num);
void setOrange(int price, int num);
/**
* 返回创建的套餐
* @return
*/
FruitMeal build();
}
product类,套餐模板类-相当于模具
/**
* 创建一个水果套餐Meal类,套餐模板
* 封装项目明细、折扣、结算金额
* 这个是固定的,会变动的是价格
*/
@Data
public class FruitMeal {
/**
* 苹果--价格
*/
private Apple apple;
/**
* 香蕉价格
*/
private Banana banana;
/**
* 桔子价格
*/
private Orange orange;
/**
* 红包
*/
private int discount;
/**
* 总价
*/
private int totalPrice;
/**
* 展示明细
*/
public void showTotalPrice() {
System.out.println("苹果:" +"数量-"+apple.getNum() +",单价-"+apple.getPrice()+" 小结:"+apple.getNum()*apple.getPrice());
System.out.println("香蕉:" +"数量-"+banana.getNum() +",单价-"+banana.getPrice()+" 小结:"+banana.getNum()*banana.getPrice());
System.out.println("桔子:" +"数量-"+orange.getNum() +",单价-"+orange.getPrice()+" 小结:"+orange.getNum()*orange.getPrice());
System.out.println("红包:" + discount);
}
/**
* 结算
*/
public void settleAccounts() {
if (null != apple) {
totalPrice += apple.getPrice() * apple.getNum();
}
if (null != orange) {
totalPrice += orange.getPrice() * orange.getNum();
}
if (null != banana) {
totalPrice += banana.getPrice() * banana.getNum();
}
// 折扣
if (totalPrice > 0) {
totalPrice -= discount;
}
}
}
建造节假日套餐,实现Builder类
/**
* 节假日套餐
* 接收一系列价格参数
* 输出一个结算套餐
*/
public class HolidayBuilder implements Builder {
private FruitMeal fruitMeal = new FruitMeal();
@Override
public void setApple(int price, int num) {
fruitMeal.setApple(new Apple(price,num));
}
@Override
public void setBanana(int price, int num) {
fruitMeal.setBanana(new Banana(price,num));
}
@Override
public void setOrange(int price, int num) {
fruitMeal.setOrange(new Orange(price,num));
}
/**
* 获取套餐
* @return
*/
@Override
public FruitMeal build() {
// 设置优惠红包
fruitMeal.setDiscount(15);
// 结算
fruitMeal.settleAccounts();
return fruitMeal;
}
}
建造老客套餐,实现Builder接口
/**
* 老客套餐
* @author guxilong
*/
public class OldCustomerBuilder implements Builder {
// 水果套餐
private FruitMeal fruitMeal = new FruitMeal();
@Override
public void setApple(int price, int num) {
fruitMeal.setApple(new Apple(price,num));
}
@Override
public void setBanana(int price, int num) {
fruitMeal.setBanana(new Banana(price,num));
}
@Override
public void setOrange(int price, int num) {
fruitMeal.setOrange(new Orange(price,num));
}
@Override
public FruitMeal build() {
//折扣
fruitMeal.setDiscount(10);
// 计算总价
fruitMeal.settleAccounts();
return fruitMeal;
}
}
收银台-导演类
/**
* 收银台---导演类
*/
public class Director {
/**
* 选择老客套餐, 以下代码模板,轻易是不变的, 设置价格和数量
* 一般是 spring注入方法,
*/
public static FruitMeal selectOld() {
Builder builder = new OldCustomerBuilder();
builder.setApple(120, 2);
builder.setBanana(80, 2);
builder.setOrange(50, 1);
// 构建1个套餐对象
return builder.build();
}
/**
* 选择节假日套餐, 以下代码模板,轻易是不变的, 设置价格和数量
* 一般是 spring注入方法,
*/
public static FruitMeal selectHoliday() {
Builder builder = new HolidayBuilder();
builder.setApple(120, 2);
builder.setBanana(80, 2);
builder.setOrange(50, 1);
// 构建1个套餐对象
return builder.build();
}
}
public class BuildTest {
/**
* 导演类通过build创建创建套餐,并计算价格
*/
public static void main(String[] args) {
// 选则节假日套餐
FruitMeal fruitMeal = Director.selectHoliday();
fruitMeal.showTotalPrice();
System.out.println("节假日套餐总花费: " + fruitMeal.getTotalPrice());
// 选则节假日套餐
FruitMeal fruitMeal1 = Director.selectOld();
fruitMeal1.showTotalPrice();
System.out.println("老客套餐总花费: " + fruitMeal1.getTotalPrice());
}
}