项目背景
最近项目经理小王因为手里没有合适的项目,但是他个人还是一个非常上进的人。于是就自己租了一个小推车,晚上没事的时候出来摊煎饼赚点外快。但是他发现出入账,统计物料,流水计算是个挺麻烦的东西。毕竟资深程序员,只好写一个系统来干这些事情了。
需求分析
小王同学煎饼分为两大类,一种细粮煎饼,一种粗粮煎饼。配料有鸡蛋,烤肠,馃子,脆皮。用户在点了主体煎饼之后,随意的添加后面的配料,不限数量,不限组合。小王同学在经过市场调研之后发现大家普遍购买的有三种,所以就搞了三个套餐。
套餐A | 细粮 + 2个鸡蛋 + 1个馃子 |
套餐B | 粗粮 + 2个鸡蛋 + 1个脆皮 |
套餐C | 细粮 + 1个鸡蛋 + 1个烤肠 |
当然,每种配料的价格不同,所以价格也不一样,系统最终可以统计每单需要什么配料,多少钱即可
构思
因为这次没有甲方来着急上线,所以小王同学就想了一下怎么去实现这个功能。小王同学打算设计一个超类Jianbing,然后粗粮和细粮两个类继承这个超类。在超类中定义各种配料为一个标志位(int)0为没有,2为2个。然后有一个总计cost()方法来计算,具体实现就是判断每一个配料的标志位是否均有值,明细就把所有有值的配料列出来,就可以搞定这个事。
但是如果这个时候引进一种新的配料比如卫龙。那就需要把超类重新调整一遍,这不符合设计模式的依赖倒置原则(见一位大牛的见解,快速入口), 而且再改动的时候肯定就又风险导致系统出现新的buff。
几经思考之后,就有了下面的解决方案
代码实现
/**
* 煎饼摊超类
*/
public abstract class Booth {
public String description = "";
private int price = 0;;
public void setDescription(String description) {
this.description = description;
}
/**
* 订单明细
*/
public String getDescription() {
return description + "-" + this.getPrice() + "¥";
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
/**
* 花费
*/
public abstract int cost();
}
/**
* 煎饼超类
*/
public class JianBing extends Booth{
@Override
public int cost() {
return super.getPrice();
}
}
public class CuLiang extends JianBing{
public CuLiang() {
super.setDescription("粗粮煎饼");
super.setPrice(5);
}
}
public class XiLiang extends JianBing{
public XiLiang() {
super.setDescription("细粮煎饼");
super.setPrice(5);
}
}
/**
* 配料父类
*/
public class Materials extends Booth{
private Booth obj;
public Materials(Booth booth) {
this.obj = booth;
}
@Override
public int cost() {
return super.getPrice() + obj.cost();
}
@Override
public String getDescription() {
return super.description + "-" + super.getPrice() + "¥\r\n " + obj.getDescription();
}
}
public class Guozi extends Materials{
public Guozi(Booth booth) {
super(booth);
super.setDescription("馃子");
super.setPrice(2);
}
}
public class Egg extends Materials{
public Egg(Booth booth) {
super(booth);
super.setDescription("鸡蛋");
super.setPrice(1);
}
}
public class Roast extends Materials{
public Roast(Booth booth) {
super(booth);
super.setDescription("烤肠");
super.setPrice(1);
}
}
/**
* 套餐A
*/
public class TariffA extends Booth{
Booth order;
public TariffA() {
order = new XiLiang();
order = new Guozi(order);
order = new Egg(order);
order = new Egg(order);
}
@Override
public int cost() {
return order.cost();
}
@Override
public String getDescription() {
return order.getDescription();
}
}
public static void main(String[] args) {
Booth order;
order = new CuLiang();
System.out.println("===========订单1===========");
System.out.println("合计:" + order.cost());
System.out.println("订单详情:" + order.getDescription());
order = new XiLiang();
order = new CuiPI(order);
order = new Egg(order);
order = new Roast(order);
System.out.println("===========订单2===========");
System.out.println("合计:" + order.cost());
System.out.println("订单详情:" + order.getDescription());
order = new TariffA();
System.out.println("===========订单3(套餐A)===========");
System.out.println("合计:" + order.cost());
System.out.println("订单详情:" + order.getDescription());
}
// 运行结果
===========订单1===========
合计:5
订单详情:粗粮煎饼-5¥
===========订单2===========
合计:9
订单详情:烤肠-1¥
鸡蛋-1¥
脆皮-2¥
细粮煎饼-5¥
===========订单3(套餐A)===========
合计:9
订单详情:鸡蛋-1¥
鸡蛋-1¥
馃子-2¥
细粮煎饼-5¥
这样就解决了之前构思中提到的问题,还能解决后期物料添加,根本不会影响主体代码,只需要添加自己的名称和单价即可解决。这就引出关于装饰者设计模式的定义:在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
装饰者模式结构
抽象构件(Component)角色 | 给出一个抽象接口,以规范准备接收附加责任的对象 |
具体构件(Concrete Component)角色 | 定义一个将要接收附加责任的类 |
装饰(Decorator)角色 | 持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口 |
具体装饰(Concrete Decorator)角色 | 负责给构件对象添加上附加的责任 |