黄焖鸡米饭最热卖的外卖之一,国人都喜欢吃,吃过黄焖鸡米饭的应该都知道,除了黄焖鸡米饭主体外,还可以添加各种配菜,如土豆、香菇、鹌鹑蛋、青菜等。如果需要你来设计一套黄焖鸡米饭结账系统,你该如何设计呢?
前置条件:主体:黄焖鸡米饭 价格:16,配菜:土豆 价格:2、香菇 价格:2、鹌鹑蛋 价格:2、青菜 价格:1.5
这还不简单?看我的,你随手就来了下面这段代码。
public class HuangMenJiMiFan {
// 黄焖鸡价格
private double huangMenJiPrice = 16D;
// 土豆价格
private double potatoPrice = 2D;
// 鹌鹑蛋价格
private double eggPrice = 2D;
// 香菇价格
private double mushroomPrice = 2D;
// 青菜价格
private double vegPrice = 1.5D;
// 总价格
private double totalPrice = 0D;
// 订单描述
private StringBuilder desc = new StringBuilder("黄焖鸡米饭 ");
// 是否加土豆
private boolean hasPotato = false;
// 是否加鹌鹑蛋
private boolean hasEgg = false;
// 是否加香菇
private boolean hasMushroom = false;
// 是否加蔬菜
private boolean hasVeg = false;
public HuangMenJiMiFan(){
this.totalPrice = this.huangMenJiPrice;
}
public void setHasPotato(boolean hasPotato) {
this.hasPotato = hasPotato;
}
public void setHasEgg(boolean hasEgg) {
this.hasEgg = hasEgg;
}
public void setHasMushroom(boolean hasMushroom) {
this.hasMushroom = hasMushroom;
}
public void setHasVeg(boolean hasVeg) {
this.hasVeg = hasVeg;
}
public String getDesc(){
if (hasEgg){
this.desc.append("+ 一份鹌鹑蛋 ");
}
if (hasMushroom){
this.desc.append("+ 一份香菇 ");
}
if (hasPotato){
this.desc.append("+ 一份土豆 ");
}
if (hasVeg){
this.desc.append("+ 一份蔬菜 ");
}
return desc.toString();
}
public double cost(){
if (hasEgg){
this.totalPrice +=this.eggPrice;
}
if (hasMushroom){
this.totalPrice +=this.mushroomPrice;
}
if (hasPotato){
this.totalPrice +=this.potatoPrice;
}
if (hasVeg){
this.totalPrice +=this.vegPrice;
}
return totalPrice;
}
}
只要在点黄焖鸡米饭的时候,把添加的配菜设置成true
就好,这段代码确实解决了黄焖鸡米饭结算问题。但是我需要加两份土豆呢?我需要添加一种新配菜呢?或者我新增一个黄焖排骨呢?这时候实现起来就需要去改动原来的代码,这违背了设计模式的开放-关闭原则
开放-关闭原则:类应该对扩展开放,对修改关闭
上面的设计违背了开放-关闭原则
,为了避免这个问题,采用装饰者模式似乎是一种可行的解决办法。
装饰者模式:动态的给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
装饰者模式的通用类图如下:
从类图中,我们可以看出装饰者模式有四种角色:
- Component:核心抽象类,装饰者和被装饰者都需要继承这个抽象类
- ConcreteComponent:对装饰的对象,该类必须继承
Component
- Decorator:装饰者抽象类,抽象出具体装饰者需要装饰的接口
- ConcreteDecorator:具体的装饰者,该类必须继承
Decorator
类,并且里面有一个变量指向Component
抽象类
装饰者模式的核心概念我们都知道了,那就来实现一把,用装饰者模式来设计黄焖鸡米饭的结账系统。
Component
类的设计,仔细想想,不管黄焖鸡米饭还是配菜都会涉及到金额计算。所以我们把该方法抽象到Component
类。来设计我们黄焖鸡米饭结账系统的Component
类,我们取名叫做Food
,Food
类的具体设计如下:
/**
* 核心抽象类
*/
public abstract class Food {
String desc = "食物描述";
public String getDesc() {
return this.desc;
}
// 价格计算
public abstract double cost();
}
ConcreteComponent
类是我们具体的被装饰对象,我们这里的装饰对象是黄焖鸡米饭,我们来设计我们黄焖鸡米饭的被装饰对象Rice
类,Rice
类的具体实现如下:
/**
* 被装饰者-黄焖鸡米饭
*/
public class Rice extends Food{
public Rice(){
this.desc ="黄焖鸡米饭";
}
@Override
public double cost() {
// 黄焖鸡米饭的价格
return 16D;
}
}
Decorator
类是装饰者的抽象类,我们需要定义一个getDesc()
的抽象接口,因为在Food
类中,getDesc()
不是抽象的,在后面的具体装饰者中,需要重写getDesc()
类,所以我们需要将抽象在装饰者这一层。我们来设计黄焖鸡米饭结账系统的装饰者抽象类FoodDecorator
,FoodDecorator
类的具体设计如下:
public abstract class FoodDecorator extends Food {
// 获取描述
public abstract String getDesc();
}
ConcreteDecorator
类是具体的装饰者,我们有四个具体的装饰者,分别是土豆、香菇、鹌鹑蛋、青菜,具体的装饰者需要做的事情是计算出被装饰者装饰完装饰品后的总价格和更新商品的描述。四个具体装饰者的设计如下:
public class Egg extends FoodDecorator {
String desc = "鸡蛋";
// 存放Component对象,该对象可能是被装饰后的
Food food;
public Egg(Food food){
this.food = food;
}
// 计算总价 当前Componen