释义
从字面理解,装饰,人的装饰可以是衣服,饰品等。但是在程序设计中,就可以理解为对一个类,一个对象等的一种装饰,具体如何进行装饰。我们可以对超市促销程序继续进行修改。也可以说是添加装饰。现在有这样一个需求,先打折后再开始满减。完全可以新增一个计算类,如下面的代码示例,然后switch里面加一个分支创建新增的计算类即可。但是想一下,满减,打折,已经存在的计算类,需要重新写一遍,只因为需要将两者结合成一个类,感觉不是很符合一个完美的程序设计理念。
private double zk = 0.8;
public double acceptCash(double price,int num){
double result = price * num * zk:
if (moneyCondition>0 && result >= moneyCondition)
result = result - Math,floor(result / moneyCondition) * moneyReturn;
return result;
}
具体实现
先直接说下装饰模式的处理逻辑,就是通过不断对一种事物进行装饰来达到最终需要的效果,在该需求中。就是对原价的商品,先装饰一层打折,再装饰一层满减。满减和打折的类已经存在。逻辑也已经存在,那就不需要再额外的添加类来处理相关的逻辑。
根据该逻辑先确定,被装饰类,和装饰类。我们需求是要对原价做一定的计算处理,那么我们就可以把原价计算类CashNormal类当作被装饰类,而满减类CashReturn和打折类CashRebate就是装饰类。
public interface ISale {
public double acceptCash(double price, int num);
}
定义一个Isale的接口。把装饰类和被装饰类都实现该接口,因为他们都需要实现计算这个功能。 区别就是,由于装饰类有两个,我们需要使用一个父类来实现该接口。而被装饰类只有一个,直接进行实现。
public class CashSuper implements ISale {
protected ISale iSale;
public void decorate(ISale iSale) {
this.iSale = iSale;
}
@Override
public double acceptCash(double price, int num) {
return iSale.acceptCash(price, num);
}
}
public class CashNormal implements ISale {
@Override
public double acceptCash(double price, int num) {
return price * num;
}
}
public class CashRebate extends CashSuper {
private double moneyRebate = 1d;
public CashRebate(double moneyRebate) {
this.moneyRebate = moneyRebate;
}
@Override
public double acceptCash(double price, int num) {
double v = price * num * moneyRebate;
if (super.iSale != null) {
return super.acceptCash(v, 1);
}
return v;
}
}
public class CashReturn extends CashSuper {
private double moneyCondition;
private double moneyReturn;
public CashReturn(double moneyCondition, double moneyReturn) {
this.moneyCondition = moneyCondition;
this.moneyReturn = moneyReturn;
}
@Override
public double acceptCash(double price, int num) {
double total = price * num;
if (total >= moneyCondition) {
total -= Math.floor(total / moneyCondition) * moneyReturn;
}
if (super.iSale != null) {
return super.acceptCash(total, 1);
}
return total;
}
}
和前两个案例最大的区别就是在CashSuper类中,加入了一个注入Isale类型参数的逻辑。只要实现了Isale类就可以注入到CashSuper中,而CashSuper是装饰类的父类(此时被装饰类CashNormal已经不再继承该类)。并且,CashSuper不再是一个抽象类,抽象的工作交给了Isale,这里CashSuper有一个具体的实现,使用Isale类型对象进行调用。代码结构设计结束,下面我们看具体的使用。回到CashContext中添加一个先满减后打折的case分支,其余不变。
public class CashContext {
private ISale cs;
public CashContext(int cashType) {
switch (cashType) {
case 1:
cs = new CashNormal();
break;
case 2:
cs = new CashRebate(0.7);
break;
case 3:
cs = new CashRebate(0.8);
break;
case 4:
cs = new CashReturn(300, 100);
break;
case 5:
CashNormal cashNormal = new CashNormal();
CashReturn cashReturn = new CashReturn(300, 100);
CashRebate cashRebate = new CashRebate(0.8);
cashReturn.decorate(cashNormal);
cashRebate.decorate(cashReturn);
cs = cashRebate;
}
}
public double getResult(double price, int num) {
return cs.acceptCash(price, num);
}
}
根据上面所叙述,抽象工作交给了Isale,那么可以看到生成的对象已经从CashSuper类型改为了Isale类型。
//创建被装饰类,该类直接执行计算是原价
CashNormal cashNormal = new CashNormal();
//生成满减对象
CashReturn cashReturn = new CashReturn(300, 100);
//生成打折对象
CashRebate cashRebate = new CashRebate(0.8);
//将满减装饰给cashNormal
cashReturn.decorate(cashNormal);
//将满减装饰给cashReturn
cashRebate.decorate(cashReturn);
//将cashReturn复制给ISale类型
cs = cashRebate;
继续对该分支进行一一解析,最后的调用是使用CashReturn进行调用的。根据debug可以看出执行顺序,CashRebate的acceptCash先执行,进行了一次满减,这里对Isale进行了一次判空。将代码再次粘贴过来分析一下。
@Override
public double acceptCash(double price, int num) {
double v = price * num * moneyRebate;
if (super.iSale != null) {
return super.acceptCash(v, 1);
}
return v;
}
这里判断父类中的iSale是否有值,如果不进行装饰,不执行decorate方法的话,那么它就是空的,也就是前四个switch分支中,就不再执行父类的方法,当前类执行完,计算就已经结束。但是对于第五个分支中,给CashRebate中注入了ISale类型参数,那么此时就不为空,就会继续执行super中的方法,super中使用了iSale.acceptCash();即执行注入的对象的acceptCash方法,该案例中注入的是CashReturn,那么就会执行其中的计算方式,该类是负责满减的,那就又执行了一次满减。同理,该对象中又注入了原价计算逻辑。最后将打折又满减后的价钱以数量1的逻辑进行一次计算(每次调用super中的方法都默认数量为1,因为最开始的计算类中已经对价格和数量进行计算,根据该业务不再多进行计算)。
这里总有一种递归的感觉,只要执行类还有注入对象,那就通过super调用的方式继续执行对象中的方法。而该对象还可以被注入对象,可以无限循环下去。那么如果需要的话,可以无限的满减,打折,满减,打折循环下去。这里先被注入的最后才会执行,正如人穿衣服进行装饰一样,最后穿上的才是最先看见的。