Java设计模式:三、装饰者模式

工作一年多了,学习完装饰者模式,然后仔细想了想,好像以前的工作过程中也没有遇到过类似装饰者模式的例子,想找一个实际例子来温习一下,还真难。
偶然想起之前参与过的一个小任务订单活动的任务,我们这边在开发活动规则,另一个同事在通过活动来计算金额。想想,这儿计算金额的例子,倒是可以勉强用用。

大概背景

一个类似淘宝一样的销售系统,商户可以在某一段时间内发起活动吸引顾客购买。活动包括:
- 商品活动 (针对某个商品的活动,比如购买立减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对象),始终都是同一个。
- 装饰者和被装饰者具有相同的超类型
- 可以用一个或多个装饰者包装一个对象
- 装饰者可以在所委托被装饰者的行为之前/之后加上自己的行为,以达到特定的目的
- 对象可以在任何时候被装饰,可以在运行时动态的不限量的指定装饰者来修饰它。

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 、4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、下载 4使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合;、 4下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.m或d论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 、1资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值