Java设计模式:三、装饰者模式
2017-05-31 17:00
411 查看
工作一年多了,学习完装饰者模式,然后仔细想了想,好像以前的工作过程中也没有遇到过类似装饰者模式的例子,想找一个实际例子来温习一下,还真难。
偶然想起之前参与过的一个小任务订单活动的任务,我们这边在开发活动规则,另一个同事在通过活动来计算金额。想想,这儿计算金额的例子,倒是可以勉强用用。
- 商品活动 (针对某个商品的活动,比如购买立减xx元,买xx件增y件等等)
- 订单活动(针对一笔订单的活动,例如饿了么满xx元建y元,或增什么什么)
- 券活动(发起活动,顾客可以先领券,然后付款时使用券抵用现金)
在付款时,顾客如果是会员,可以根据会员等级打折,同时如果有足够的积分,可以使用积分抵用现金。
这儿的例子主要是用来计算金额时用到。
假定:
所有商品活动:只要购买,就打9折
订单活动:一笔订单满100元减10元,满200元减25
券活动:一笔订单满300元以上可以使用5元抵用券,满500元以上可以使用20元抵用券
会员享受:会员等级3级以上,打9.5折,会员等级满8级以上,打9折。
假如现在一个4级会员的顾客领取了15元抵用券,购买了300元商品,付款时,实际需要付款多少钱。
上面这个是不使用模式的时候。看到测试类中的一大堆代码,阅读起来就麻烦,何况后期的维护呢。一旦增加一个新的需求,或者说现在少了一个活动,这个改动会很大。
但是如果使用装饰者模式,就不一样了。
如果将一笔订单设计为一个对象,计算实付金额的其他类,就是用来装饰这个订单对象,以达到最终的金额。
装饰者模式的类图:
上面的这里面有很多init方法,这个方法的主要目的是用来初始化一个活动规则,因为价格的计算是依赖这个规则的。还有一些冗余字段,比如name是没有用到的。
也许这个例子举得不好吧。下面是装饰者模式的对象结构。
它的结构是根据new的顺序来创建的。
先创建一个Order对象
然后再创建一个OrderActivity对象,这个对象包含Order对象,也就是说,OrderActivity对象装饰了Order对象。以此类推下去。
它的getAmount()方法的调用顺序,也是如此。下图可以表示:
使用装饰者模式和不适用装饰者模式的主要区别是,使用装饰者模式可以很好的去扩展类,而原始的这种方式,一旦加入新的一种计算方式,势必要去修改代码。而设计原则中,对修改关闭,对扩展开放。就是说,尽量不要去修改以前写好的代码。所以使用装饰者模式,只需要再增加一个类即可,不需要去关心客户端怎么计算。
- 装饰者和被装饰者具有相同的超类型
- 可以用一个或多个装饰者包装一个对象
- 装饰者可以在所委托被装饰者的行为之前/之后加上自己的行为,以达到特定的目的
- 对象可以在任何时候被装饰,可以在运行时动态的不限量的指定装饰者来修饰它。
偶然想起之前参与过的一个小任务订单活动的任务,我们这边在开发活动规则,另一个同事在通过活动来计算金额。想想,这儿计算金额的例子,倒是可以勉强用用。
大概背景
一个类似淘宝一样的销售系统,商户可以在某一段时间内发起活动吸引顾客购买。活动包括:- 商品活动 (针对某个商品的活动,比如购买立减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对象),始终都是同一个。- 装饰者和被装饰者具有相同的超类型
- 可以用一个或多个装饰者包装一个对象
- 装饰者可以在所委托被装饰者的行为之前/之后加上自己的行为,以达到特定的目的
- 对象可以在任何时候被装饰,可以在运行时动态的不限量的指定装饰者来修饰它。
相关文章推荐
- Head First Java 设计模式——装饰者模式
- java设计模式---装饰者模式(简单笔记)
- JAVA设计模式初探之装饰者模式
- 设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 用法
- 设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 使用方法
- java设计模式之装饰者模式
- java 设计模式 学习笔记(三)装饰者模式 推荐
- JAVA设计模式初探之装饰者模式
- Java 设计模式 装饰者模式
- Java设计模式菜鸟系列(三)装饰者模式建模与实现
- 【Java设计模式】装饰者模式
- JAVA系列-设计模式-装饰者模式
- JAVA设计模式——装饰者模式
- java设计模式之装饰者模式Decorator
- Java 装饰者设计模式
- Java设计模式之装饰者模式
- JAVA设计模式——装饰者模式
- java 设计模式中 装饰者模式 与 代理模式的区别
- java设计模式学习(三)装饰者模式(decorator)
- 设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 使用方法