当前位置:   article > 正文

掌握装饰器模式:概念、用法及商品价格策略优化案例_装饰器模式在电商中的实战

装饰器模式在电商中的实战

目录

一、装饰器模式的概念及怎么用?

1.基本概念和功能

2.结构图分析

3.举例分析

二、装饰器模式的基本用法

1.接口定义:去定义具体需要实现的相关方法

2.具体对象:针对需要实现的方法做初始化操作,即基本的实现

3.装饰类:抽象类,初始化具体对象

4.其他具体装饰类实现自己特性的需求

5.实际使用

三、具体案例分析

目标:用装饰器模式动手实现一套商品价格策略的优化方案。

1.先建立订单和商品的属性类:主订单+详细订单+商品+促销类型+优惠券+红包

2.建立计算支付金额的接口类以及基本类

3.建立计算支付金额的抽象类(调用基本类)

4.优惠券计算类通过继承抽象类来实现所需要的修饰类

5.红包计算类通过继承抽象类来实现所需要的修饰类

6.通过一个工厂类来组合商品的促销类型

7.实际使用操作

参考书籍、文献和资料


干货分享,感谢您的阅读!

一、装饰器模式的概念及怎么用?

1.基本概念和功能

装饰器模式能够实现从一个对象的外部来给对象添加功能,有非常灵活的扩展性,可以在对原来的代码毫无修改的前提下,为对象添加新功能。除此之外,装饰器模式还能够实现对象的动态组合,借此我们可以很灵活地给动态组合的对象,匹配所需要的功能

2.结构图分析

  • Component为统一接口,也是装饰类和被装饰类的基本类型。
  • ConcreteComponent为具体实现类,也是被装饰类,他本身是个具有一些功能的完整的类。
  • Decorator是装饰类,实现了Component接口的同时还在内部维护了一个ConcreteComponent的实例,并可以通过构造函数初始化。而Decorator本身,通常采用默认实现,他的存在仅仅是一个声明:我要生产出一些用于装饰的子类了。而其子类才是赋有具体装饰效果的装饰产品类。
  • ConcreteDecorator是具体的装饰产品类,每一种装饰产品都具有特定的装饰效果。可以通过构造器声明装饰哪种类型的ConcreteComponent,从而对其进行装饰。

3.举例分析

假设现在有这样一个需求,让你设计一个装修功能,用户可以动态选择不同的装修功能来装饰自己的房子。例如,水电装修、天花板以及粉刷墙等属于基本功能,而设计窗帘装饰窗户、设计吊顶装饰房顶等未必是所有用户都需要的,这些功能则需要实现动态添加。还有就是一旦有新的装修功能,我们也可以实现动态添加。

采用装饰器模式可以很好的解决以上问题。

二、装饰器模式的基本用法

基于装饰器模式实现的装修功能的代码结构简洁易读,业务逻辑也非常清晰,并且如果我们需要扩展新的装修功能,只需要新增一个继承了抽象装饰类的子类即可。

装饰器模式包括了以下几个角色:接口、具体对象、装饰类、具体装饰类

  • 接口定义了具体对象的一些实现方法;
  • 具体对象定义了一些初始化操作,比如开头设计装修功能的案例中,水电装修、天花板以及粉刷墙等都是初始化操作;
  • 装饰类则是一个抽象类,主要用来初始化具体对象的一个类;
  • 其它的具体装饰类都继承了该抽象类。

1.接口定义:去定义具体需要实现的相关方法

  1. /**
  2. * 描述:定义一个基本装修接口
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 13:32
  6. */
  7. public interface IDecorator {
  8. /**
  9. * 装修方法
  10. */
  11. void decorate();
  12. }

2.具体对象:针对需要实现的方法做初始化操作,即基本的实现

  1. /**
  2. * 描述:装修基本类
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 13:32
  6. */
  7. public class Decorator implements IDecorator {
  8. /**
  9. * 基本实现方法
  10. */
  11. @Override
  12. public void decorate() {
  13. System.out.println("水电装修、天花板以及粉刷墙.");
  14. }
  15. }

3.装饰类:抽象类,初始化具体对象

  1. /**
  2. * 描述:基本装饰类
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 13:34
  6. */
  7. public abstract class BaseDecorator implements IDecorator {
  8. private IDecorator decorator;
  9. public BaseDecorator(IDecorator decorator) {
  10. this.decorator = decorator;
  11. }
  12. /**
  13. * 调用装饰方法
  14. */
  15. @Override
  16. public void decorate() {
  17. if (decorator != null) {
  18. decorator.decorate();
  19. }
  20. }
  21. }

4.其他具体装饰类实现自己特性的需求

如果我们想要在基础类上添加新的装修功能,只需要基于抽象类 BaseDecorator 去实现继承类,通过构造函数调用父类,以及重写装修方法实现装修窗帘的功能即可。

  1. /**
  2. * 描述:窗帘装饰类
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 13:35
  6. */
  7. public class CurtainDecorator extends BaseDecorator {
  8. public CurtainDecorator(IDecorator decorator) {
  9. super(decorator);
  10. }
  11. /**
  12. * 窗帘具体装饰方法
  13. */
  14. @Override
  15. public void decorate() {
  16. System.out.println("窗帘装饰。。。");
  17. super.decorate();
  18. }
  19. }

5.实际使用

  1. /**
  2. * 描述:具体使用测试
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 13:36
  6. */
  7. public class Test {
  8. public static void main(String[] args) {
  9. IDecorator decorator = new Decorator();
  10. IDecorator curtainDecorator = new CurtainDecorator(decorator);
  11. curtainDecorator.decorate();
  12. }
  13. }

三、具体案例分析

每逢双十一,为了加大商城的优惠力度,开发往往要设计红包 + 限时折扣或红包 + 抵扣券等组合来实现多重优惠。而在平时,由于某些特殊原因,商家还会赠送特殊抵扣券给购买用户,而特殊抵扣券 + 各种优惠又是另一种组合方式。

要实现以上这类组合优惠的功能,刚刚介绍的装饰器模式就很适合用在这里,其相互独立、自由组合以及方便动态扩展功能的特性,可以很好地解决这类需求。

目标:用装饰器模式动手实现一套商品价格策略的优化方案。

1.先建立订单和商品的属性类:主订单+详细订单+商品+促销类型+优惠券+红包

订单和商品的属性类只建立了几个关键字段,主订单包含若干详细订单,详细订单中记录了商品信息,商品信息中包含了促销类型信息,一个商品可以包含多个促销类型(只讨论单个促销和组合促销)

  • 主订单
  1. import java.math.BigDecimal;
  2. import java.util.List;
  3. /**
  4. * 描述:主订单
  5. *
  6. * @author yanfengzhang
  7. * @date 2020-04-19 14:01
  8. */
  9. @Data
  10. public class Order {
  11. /**
  12. * 订单ID
  13. */
  14. private int id;
  15. /**
  16. * 订单号
  17. */
  18. private String orderNo;
  19. /**
  20. * 总支付金额
  21. */
  22. private BigDecimal totalPayMoney;
  23. /**
  24. * 详细订单列表
  25. */
  26. private List<OrderDetail> list;
  27. }
  • 详细订单
  1. import java.math.BigDecimal;
  2. /**
  3. * 描述:详细订单
  4. *
  5. * @author yanfengzhang
  6. * @date 2020-04-19 14:06
  7. */
  8. @Data
  9. public class OrderDetail {
  10. /**
  11. * 详细订单ID
  12. */
  13. private int id;
  14. /**
  15. * 主订单ID
  16. */
  17. private int orderId;
  18. /**
  19. * 商品详情
  20. */
  21. private Merchandise merchandise;
  22. /**
  23. * 支付单价
  24. */
  25. private BigDecimal payMoney;
  26. }
  • 具体商品
  1. import java.math.BigDecimal;
  2. import java.util.Map;
  3. /**
  4. * 描述:具体商品
  5. *
  6. * @author yanfengzhang
  7. * @date 2020-04-19 14:09
  8. */
  9. @Data
  10. public class Merchandise {
  11. /**
  12. * 商品SKU
  13. */
  14. private String sku;
  15. /**
  16. * 商品名称
  17. */
  18. private String name;
  19. /**
  20. * 商品单价
  21. */
  22. private BigDecimal price;
  23. /**
  24. * 支持促销类型
  25. */
  26. private Map<PromotionType, SupportPromotions> supportPromotions;
  27. }
  • 促销类型
  1. /**
  2. * 描述:促销类型
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 14:16
  6. */
  7. public enum PromotionType {
  8. /**
  9. * 优惠卷
  10. */
  11. COUPON,
  12. /**
  13. * 红包
  14. */
  15. REDPACKED;
  16. }
  • 优惠券
  1. import java.math.BigDecimal;
  2. /**
  3. * 描述:优惠券
  4. *
  5. * @author yanfengzhang
  6. * @date 2020-04-19 14:13
  7. */
  8. @Data
  9. public class UserCoupon {
  10. /**
  11. * 优惠券ID
  12. */
  13. private int id;
  14. /**
  15. * 领取优惠券用户ID
  16. */
  17. private int userId;
  18. /**
  19. * 商品SKU
  20. */
  21. private String sku;
  22. /**
  23. * 优惠金额
  24. */
  25. private BigDecimal coupon;
  26. }
  • 红包
  1. import java.math.BigDecimal;
  2. /**
  3. * 描述:红包
  4. *
  5. * @author yanfengzhang
  6. * @date 2020-04-19 14:14
  7. */
  8. @Data
  9. public class UserRedPacket {
  10. /**
  11. * 红包ID
  12. */
  13. private int id;
  14. /**
  15. * 领取用户ID
  16. */
  17. private int userId;
  18. /**
  19. * 商品SKU
  20. */
  21. private String sku;
  22. /**
  23. * 领取红包金额
  24. */
  25. private BigDecimal redPacket;
  26. }
  • 促销类型
  1. /**
  2. * 描述:促销类型
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 14:16
  6. */
  7. @Data
  8. public class SupportPromotions implements Cloneable {
  9. /**
  10. * 该商品促销的ID
  11. */
  12. private int id;
  13. /**
  14. * 促销类型 1\优惠券 2\红包
  15. */
  16. private PromotionType promotionType;
  17. /**
  18. * 优先级
  19. */
  20. private int priority;
  21. /**
  22. * 用户领取该商品的优惠券
  23. */
  24. private UserCoupon userCoupon;
  25. /**
  26. * 用户领取该商品的红包
  27. */
  28. private UserRedPacket userRedPacket;
  29. /**
  30. * 重写clone方法
  31. */
  32. @Override
  33. public SupportPromotions clone() {
  34. SupportPromotions supportPromotions = null;
  35. try {
  36. supportPromotions = (SupportPromotions) super.clone();
  37. } catch (CloneNotSupportedException e) {
  38. e.printStackTrace();
  39. }
  40. return supportPromotions;
  41. }
  42. }

2.建立计算支付金额的接口类以及基本类

  • 基本接口定义
  1. /**
  2. * 描述:计算支付金额接口类
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 14:09
  6. */
  7. public interface IBaseCount {
  8. /**
  9. * 功能描述:计算支付金额
  10. * @author yanfengzhang
  11. * @date 2020-04-19 14:40
  12. * @param orderDetail
  13. * @return BigDecimal
  14. */
  15. BigDecimal countPayMoney(OrderDetail orderDetail);
  16. }
  • 基本实现类
  1. /**
  2. * 描述:支付基本类
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 14:41
  6. */
  7. public class BaseCount implements IBaseCount {
  8. @Override
  9. public BigDecimal countPayMoney(OrderDetail orderDetail) {
  10. orderDetail.setPayMoney(orderDetail.getMerchandise().getPrice());
  11. System.out.println("商品原单价金额为:" + orderDetail.getPayMoney());
  12. return orderDetail.getPayMoney();
  13. }
  14. }

3.建立计算支付金额的抽象类(调用基本类)

  1. /**
  2. * 描述:计算支付金额的抽象类
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 14:48
  6. */
  7. public abstract class BaseCountDecorator implements IBaseCount {
  8. private IBaseCount count;
  9. public BaseCountDecorator(IBaseCount count) {
  10. this.count = count;
  11. }
  12. @Override
  13. public BigDecimal countPayMoney(OrderDetail orderDetail) {
  14. BigDecimal payTotalMoney = new BigDecimal(0);
  15. if (count != null) {
  16. payTotalMoney = count.countPayMoney(orderDetail);
  17. }
  18. return payTotalMoney;
  19. }
  20. }

4.优惠券计算类通过继承抽象类来实现所需要的修饰类

  1. /**
  2. * 描述:计算使用优惠券后的金额
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 14:50
  6. */
  7. public class CouponDecorator extends BaseCountDecorator {
  8. public CouponDecorator(IBaseCount count) {
  9. super(count);
  10. }
  11. @Override
  12. public BigDecimal countPayMoney(OrderDetail orderDetail) {
  13. BigDecimal payTotalMoney = new BigDecimal(0);
  14. payTotalMoney = super.countPayMoney(orderDetail);
  15. payTotalMoney = countCouponPayMoney(orderDetail);
  16. return payTotalMoney;
  17. }
  18. private BigDecimal countCouponPayMoney(OrderDetail orderDetail) {
  19. BigDecimal coupon = orderDetail.getMerchandise().getSupportPromotions().get(PromotionType.COUPON).getUserCoupon().getCoupon();
  20. System.out.println("优惠券金额:" + coupon);
  21. orderDetail.setPayMoney(orderDetail.getPayMoney().subtract(coupon));
  22. return orderDetail.getPayMoney();
  23. }
  24. }

5.红包计算类通过继承抽象类来实现所需要的修饰类

  1. /**
  2. * 描述:计算使用红包后的金额
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 15:24
  6. */
  7. public class RedPacketDecorator extends BaseCountDecorator {
  8. public RedPacketDecorator(IBaseCount count) {
  9. super(count);
  10. }
  11. @Override
  12. public BigDecimal countPayMoney(OrderDetail orderDetail) {
  13. BigDecimal payTotalMoney = new BigDecimal(0);
  14. payTotalMoney = super.countPayMoney(orderDetail);
  15. payTotalMoney = countCouponPayMoney(orderDetail);
  16. return payTotalMoney;
  17. }
  18. private BigDecimal countCouponPayMoney(OrderDetail orderDetail) {
  19. BigDecimal redPacket = orderDetail.getMerchandise().getSupportPromotions().get(PromotionType.REDPACKED).getUserRedPacket().getRedPacket();
  20. System.out.println("红包优惠金额:" + redPacket);
  21. orderDetail.setPayMoney(orderDetail.getPayMoney().subtract(redPacket));
  22. return orderDetail.getPayMoney();
  23. }
  24. }

6.通过一个工厂类来组合商品的促销类型

  1. /**
  2. * 描述:计算促销后的支付价格
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 14:58
  6. */
  7. public class PromotionFactory {
  8. public static BigDecimal getPayMoney(OrderDetail orderDetail) {
  9. /**获取给商品设定的促销类型*/
  10. Map<PromotionType, SupportPromotions> supportPromotionsList = orderDetail.getMerchandise().getSupportPromotions();
  11. /**初始化计算类*/
  12. IBaseCount baseCount = new BaseCount();
  13. if (supportPromotionsList != null && supportPromotionsList.size() > 0) {
  14. for (PromotionType promotionType : supportPromotionsList.keySet()) {
  15. /**遍历设置的促销类型,通过装饰器组合促销类型*/
  16. baseCount = protmotion(supportPromotionsList.get(promotionType), baseCount);
  17. }
  18. }
  19. return baseCount.countPayMoney(orderDetail);
  20. }
  21. /**
  22. * 组合促销类型 * @param supportPromotions * @param baseCount * @return
  23. */
  24. private static IBaseCount protmotion(SupportPromotions supportPromotions, IBaseCount baseCount) {
  25. if (supportPromotions.getPromotionType() == PromotionType.COUPON) {
  26. baseCount = new CouponDecorator(baseCount);
  27. } else if (supportPromotions.getPromotionType() == PromotionType.REDPACKED) {
  28. baseCount = new RedPacketDecorator(baseCount);
  29. }
  30. return baseCount;
  31. }
  32. }

7.实际使用操作

  1. /**
  2. * 描述:测试
  3. *
  4. * @author yanfengzhang
  5. * @date 2020-04-19 15:35
  6. */
  7. public class Test {
  8. volatile int counter = 0;
  9. private static Order init(Order order) {
  10. Map<PromotionType, SupportPromotions> supportPromotionslist = new HashMap<PromotionType, SupportPromotions>();
  11. SupportPromotions supportPromotions = new SupportPromotions();
  12. supportPromotions.setPromotionType(PromotionType.COUPON);
  13. supportPromotions.setPriority(1);
  14. UserCoupon userCoupon = new UserCoupon();
  15. userCoupon.setCoupon(new BigDecimal(3));
  16. userCoupon.setSku("aaa1111");
  17. userCoupon.setUserId(11);
  18. supportPromotions.setUserCoupon(userCoupon);
  19. supportPromotionslist.put(PromotionType.COUPON, supportPromotions);
  20. SupportPromotions supportPromotions1 = supportPromotions.clone();
  21. supportPromotions1.setPromotionType(PromotionType.REDPACKED);
  22. supportPromotions1.setPriority(2);
  23. UserRedPacket userRedPacket = new UserRedPacket();
  24. userRedPacket.setId(1);
  25. userRedPacket.setRedPacket(new BigDecimal(10));
  26. userRedPacket.setSku("aaa1111");
  27. userCoupon.setUserId(11);
  28. supportPromotions1.setUserRedPacket(userRedPacket);
  29. supportPromotionslist.put(PromotionType.REDPACKED, supportPromotions1);
  30. Merchandise merchandise = new Merchandise();
  31. merchandise.setSku("aaa1111");
  32. merchandise.setName("苹果");
  33. merchandise.setPrice(new BigDecimal(20));
  34. merchandise.setSupportPromotions(supportPromotionslist);
  35. List<OrderDetail> OrderDetailList = new ArrayList<OrderDetail>();
  36. OrderDetail orderDetail = new OrderDetail();
  37. orderDetail.setId(1);
  38. orderDetail.setOrderId(1111);
  39. orderDetail.setMerchandise(merchandise);
  40. OrderDetailList.add(orderDetail);
  41. order.setList(OrderDetailList);
  42. return order;
  43. }
  44. public static void main(String[] args) throws InterruptedException, IOException {
  45. Order order = new Order();
  46. init(order);
  47. for (OrderDetail orderDetail : order.getList()) {
  48. BigDecimal payMoney = PromotionFactory.getPayMoney(orderDetail);
  49. orderDetail.setPayMoney(payMoney);
  50. System.out.println("最终支付金额:" + orderDetail.getPayMoney());
  51. }
  52. }
  53. }

实际测试输入:

  1. 商品原单价金额为:20
  2. 红包优惠金额:10
  3. 优惠券金额:3
  4. 最终支付金额:7

参考书籍、文献和资料

1.极客时间课程《Java性能调优实战》,刘超,2019.

2.https://www.cnblogs.com/jzb-blog/p/6717349.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/煮酒与君饮/article/detail/948958
推荐阅读
相关标签
  

闽ICP备14008679号