工作一年多了,学习完装饰者模式,然后仔细想了想,好像以前的工作过程中也没有遇到过类似装饰者模式的例子,想找一个实际例子来温习一下,还真难。
偶然想起之前参与过的一个小任务订单活动的任务,我们这边在开发活动规则,另一个同事在通过活动来计算金额。想想,这儿计算金额的例子,倒是可以勉强用用。
大概背景
一个类似淘宝一样的销售系统,商户可以在某一段时间内发起活动吸引顾客购买。活动包括:
- 商品活动 (针对某个商品的活动,比如购买立减xx元,买xx件增y件等等)
- 订单活动(针对一笔订单的活动,例如饿了么满xx元建y元,或增什么什么)
- 券活动(发起活动,顾客可以先领券,然后付款时使用券抵用现金)
在付款时,顾客如果是会员,可以根据会员等级打折,同时如果有足够的积分,可以使用积分抵用现金。
这儿的例子主要是用来计算金额时用到。
假定:
所有商品活动:只要购买,就打9折
订单活动:一笔订单满100元减10元,满200元减25
券活动:一笔订单满300元以上可以使用5元抵用券,满500元以上可以使用20元抵用券
会员享受:会员等级3级以上,打9.5折,会员等级满8级以上,打9折。
假如现在一个4级会员的顾客领取了15元抵用券,购买了300元商品,付款时,实际需要付款多少钱。
不使用模式
/**
* 商品活动
*/
public class GoodsActivity {
private String name; //活动名
private Double discount; //折扣价
public static GoodsActivity initGoodsActivity() {
GoodsActivity goodsActivity = new GoodsActivity();
goodsActivity.setName("商品活动");
goodsActivity.setDiscount(0.9);
return goodsActivity;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getDiscount() {
return discount;
}
public void setDiscount(Double discount) {
this.discount = discount;
}
}
/**
* 订单活动
*/
public class OrderActivity {
private String name;
private List<ActivityDetail> detailList;
public static class ActivityDetail {
public BigDecimal aggregateAmount; //单笔订单总金额
public BigDecimal reduceAmount; //满减金额
private ActivityDetail(BigDecimal aggregateAmount, BigDecimal reduceAmount) {
this.aggregateAmount = aggregateAmount;
this.reduceAmount = reduceAmount;
}
}
public static OrderActivity initOrderActivity(){
List<ActivityDetail> orderActivityDetailList = new ArrayList<>();
ActivityDetail orderDetail1 = new ActivityDetail(BigDecimal.valueOf(100), BigDecimal.valueOf(10));
ActivityDetail orderDetail2 = new ActivityDetail(BigDecimal.valueOf(200), BigDecimal.valueOf(25));
orderActivityDetailList.add(orderDetail1);
orderActivityDetailList.add(orderDetail2);
OrderActivity orderActivity = new OrderActivity();
orderActivity.setName("订单活动");
orderActivity.setDetailList(orderActivityDetailList);
return orderActivity;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ActivityDetail> getDetailList() {
return detailList;
}
public void setDetailList(List<ActivityDetail> detailList) {
this.detailList = detailList;
}
}
/**
* 券活动
*/
public class CouponActivity {
private String name;
private List<Detail> detailList;
public static class Detail {
public BigDecimal aggregateAmount; //订单总金额
public BigDecimal enableUseAmount; //可使用券
private Detail(BigDecimal aggregateAmount, BigDecimal enableUseAmount) {
this.aggregateAmount = aggregateAmount;
this.enableUseAmount = enableUseAmount;
}
}
public static CouponActivity initCouponActivity() {
List<Detail> detailList = new ArrayList<>();
Detail detail1 = new Detail(BigDecimal.valueOf(300), BigDecimal.valueOf(5));
Detail detail2 = new Detail(BigDecimal.valueOf(500), BigDecimal.valueOf(20));
detailList.add(detail1);
detailList.add(detail2);
CouponActivity couponActivity = new CouponActivity();
couponActivity.setName("订单活动");
couponActivity.setDetailList(detailList);
return couponActivity;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Detail> getDetailList() {
return detailList;
}
public void setDetailList(List<Detail> detailList) {
this.detailList = detailList;
}
}
/**
* 会员信息
*/
public class Memory {
private int grade; //会员等级
private long points; //账户含有积分
public static Memory initMemory() {
Memory memory = new Memory();
memory.setGrade(4);
memory.setPoints(500);
return memory;
}
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
public long getPoints() {
return points;
}
public void setPoints(long points) {
this.points = points;
}
}
/**
* 实付金额计算测试
*/
public class PayTest {
public static void main(String[] args) {
//初始化活动
GoodsActivity goodsActivity = GoodsActivity.initGoodsActivity();
OrderActivity orderActivity = OrderActivity.initOrderActivity();
CouponActivity couponActivity = CouponActivity.initCouponActivity();
Memory memory = Memory.initMemory();
//购买了300元的商品
BigDecimal purchaseAmount = BigDecimal.valueOf(300);
//计算商品折扣
Double goodsActivityDiscount = goodsActivity.getDiscount();
//计算订单减价
BigDecimal orderReduceAmount = BigDecimal.ZERO;
List<OrderActivity.ActivityDetail> detailList = orderActivity.getDetailList();
for (OrderActivity.ActivityDetail detail : detailList) {
if (purchaseAmount.compareTo(detail.aggregateAmount) >= 0) {
orderReduceAmount = detail.reduceAmount;
}
}
//计算可使用券金额
BigDecimal couponReduceAmount = BigDecimal.ZERO;
List<CouponActivity.Detail> couponActivityDetailList = couponActivity.getDetailList();
for (CouponActivity.Detail detail : couponActivityDetailList) {
if (purchaseAmount.compareTo(detail.aggregateAmount) >= 0) {
couponReduceAmount = detail.enableUseAmount;
}
}
//计算会员折扣
double memoryDiscount = 1;
if (memory.getGrade() > 3 && memory.getGrade() < 8) {
memoryDiscount = 0.95;
} else if (memory.getGrade() > 8) {
memoryDiscount = 0.9;
}
//计算实付金额
BigDecimal actualAmount = purchaseAmount.subtract(orderReduceAmount)
.subtract(couponReduceAmount)
.multiply(BigDecimal.valueOf(goodsActivityDiscount))
.multiply(BigDecimal.valueOf(memoryDiscount));
System.out.println(actualAmount.toString());
}
}
最后输出:
230.850
上面这个是不使用模式的时候。看到测试类中的一大堆代码,阅读起来就麻烦,何况后期的维护呢。一旦增加一个新的需求,或者说现在少了一个活动,这个改动会很大。
但是如果使用装饰者模式,就不一样了。
装饰者模式
动态的将责任添加到对象上。对象和装饰者具有相同的类型。
如果将一笔订单设计为一个对象,计算实付金额的其他类,就是用来装饰这个订单对象,以达到最终的金额。
装饰者模式的类图:
使用装饰者模式
因为此例只有一种order,所以,代码为右边这条线为主。
/**
* 订单基类
**/
public class Order {
private BigDecimal allamount; //订单总额
//初始化一个订单
public static Order initOrder(){
Order order = new Order();
order.setAllamount(BigDecimal.valueOf(300));
return order;
}
public BigDecimal getAllamount() {
return allamount;
}
public void setAllamount(BigDecimal allamount) {
this.allamount = allamount;
}
public BigDecimal getAmount() {
return getAllamount();
}
}
/**
* 商品活动
**/
public class GoodsActivity extends Order {
private Order order;
private String name; //活动名
private Double discount; //折扣价
public GoodsActivity(Order order) {
this.order = order;
}
public GoodsActivity() {
}
//初始化商品活动
public static GoodsActivity initGoodsActivity() {
GoodsActivity goodsActivity = new GoodsActivity();
goodsActivity.setName("商品活动");
goodsActivity.setDiscount(0.9);
return goodsActivity;
}
@Override
public BigDecimal getAmount() {
return order.getAmount().multiply(BigDecimal.valueOf(initGoodsActivity().getDiscount()));
}
public void setName(String name) {
this.name = name;
}
public Double getDiscount() {
return discount;
}
public void setDiscount(Double discount) {
this.discount = discount;
}
}
/**
* 订单活动
*/
public class OrderActivity extends Order{
private Order order;
private String name;
private List<ActivityDetail> detailList;
public static class ActivityDetail {
public BigDecimal aggregateAmount; //单笔订单总金额
public BigDecimal reduceAmount; //满减金额
private ActivityDetail(BigDecimal aggregateAmount, BigDecimal reduceAmount) {
this.aggregateAmount = aggregateAmount;
this.reduceAmount = reduceAmount;
}
}
public OrderActivity(Order order) {
this.order = order;
this.setAllamount(order.getAllamount());
}
public OrderActivity() {
}
public static OrderActivity initOrderActivity(){
List<ActivityDetail> orderActivityDetailList = new ArrayList<>();
ActivityDetail orderDetail1 = new ActivityDetail(BigDecimal.valueOf(100), BigDecimal.valueOf(10));
ActivityDetail orderDetail2 = new ActivityDetail(BigDecimal.valueOf(200), BigDecimal.valueOf(25));
orderActivityDetailList.add(orderDetail1);
orderActivityDetailList.add(orderDetail2);
OrderActivity orderActivity = new OrderActivity();
orderActivity.setName("订单活动");
orderActivity.setDetailList(orderActivityDetailList);
return orderActivity;
}
@Override
public BigDecimal getAmount() {
BigDecimal orderReduceAmount = BigDecimal.ZERO;
List<ActivityDetail> detailList = initOrderActivity().getDetailList();
for (ActivityDetail detail : detailList) {
if (order.getAllamount().compareTo(detail.aggregateAmount) >= 0) {
orderReduceAmount = detail.reduceAmount;
}
}
return order.getAmount().subtract(orderReduceAmount);
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ActivityDetail> getDetailList() {
return detailList;
}
public void setDetailList(List<ActivityDetail> detailList) {
this.detailList = detailList;
}
}
/**
* 券活动
*/
public class CouponActivity extends Order{
private Order order;
private String name;
private List<Detail> detailList;
public static class Detail {
public BigDecimal aggregateAmount; //订单总金额
public BigDecimal enableUseAmount; //可使用券
private Detail(BigDecimal aggregateAmount, BigDecimal enableUseAmount) {
this.aggregateAmount = aggregateAmount;
this.enableUseAmount = enableUseAmount;
}
}
public CouponActivity() {
}
public CouponActivity(Order order) {
this.order = order;
this.setAllamount(order.getAllamount());
}
public static CouponActivity initCouponActivity() {
List<Detail> detailList = new ArrayList<>();
Detail detail1 = new Detail(BigDecimal.valueOf(300), BigDecimal.valueOf(5));
Detail detail2 = new Detail(BigDecimal.valueOf(500), BigDecimal.valueOf(20));
detailList.add(detail1);
detailList.add(detail2);
CouponActivity couponActivity = new CouponActivity();
couponActivity.setName("订单活动");
couponActivity.setDetailList(detailList);
return couponActivity;
}
@Override
public BigDecimal getAmount() {
BigDecimal couponReduceAmount = BigDecimal.ZERO;
List<Detail> couponActivityDetailList = initCouponActivity().getDetailList();
for (Detail detail : couponActivityDetailList) {
if (order.getAllamount().compareTo(detail.aggregateAmount) >= 0) {
couponReduceAmount = detail.enableUseAmount;
}
}
return order.getAmount().subtract(couponReduceAmount);
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Detail> getDetailList() {
return detailList;
}
public void setDetailList(List<Detail> detailList) {
this.detailList = detailList;
}
}
//会员类保持不变
/**
* 测试代码
**/
public class PayTest {
public static void main(String[] args) {
//计算会员折扣
Memory memory = Memory.initMemory();
double memoryDiscount = 1;
if (memory.getGrade() > 3 && memory.getGrade() < 8) {
memoryDiscount = 0.95;
} else if (memory.getGrade() > 8) {
memoryDiscount = 0.9;
}
Order order = Order.initOrder();
order = new OrderActivity(order);
order = new CouponActivity(order);
order = new GoodsActivity(order);
System.out.println(order.getAmount().multiply(BigDecimal.valueOf(memoryDiscount)));
}
}
输出:
230.850
上面的这里面有很多init方法,这个方法的主要目的是用来初始化一个活动规则,因为价格的计算是依赖这个规则的。还有一些冗余字段,比如name是没有用到的。
也许这个例子举得不好吧。下面是装饰者模式的对象结构。
它的结构是根据new的顺序来创建的。
先创建一个Order对象
然后再创建一个OrderActivity对象,这个对象包含Order对象,也就是说,OrderActivity对象装饰了Order对象。以此类推下去。
它的getAmount()方法的调用顺序,也是如此。下图可以表示:
使用装饰者模式和不适用装饰者模式的主要区别是,使用装饰者模式可以很好的去扩展类,而原始的这种方式,一旦加入新的一种计算方式,势必要去修改代码。而设计原则中,对修改关闭,对扩展开放。就是说,尽量不要去修改以前写好的代码。所以使用装饰者模式,只需要再增加一个类即可,不需要去关心客户端怎么计算。
总结
装饰者模式的意图是动态的将责任增加到原对象上。所以,每一个装饰者都和原对象具有相同的类型。需要注意的就是每一个装饰者中的原对象(示例中的Order对象),始终都是同一个。
- 装饰者和被装饰者具有相同的超类型
- 可以用一个或多个装饰者包装一个对象
- 装饰者可以在所委托被装饰者的行为之前/之后加上自己的行为,以达到特定的目的
- 对象可以在任何时候被装饰,可以在运行时动态的不限量的指定装饰者来修饰它。