装饰器模式的使用总结

装饰器模式的使用总结一 装饰器模式的概念及怎么用 1 基本概念和功能 装饰器模式能够实现从一个对象的外部来给对象添加功能 有非常灵活的扩展性 可以在对原来的代码毫无修改的前提下 为对象添加新功能 除此之外 装饰器模式还能够实现对象的动态组合 借此我们可以很灵活地给动态组合的对象 匹配所需要的功能 2 举例分析 假设现在有这样一个需求 让你设计一个装修功能 用户可以动态选择不同的装修功能来装饰自己的房子

目录

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

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.接口定义:去定义具体需要实现的相关方法

/ * 描述:定义一个基本装修接口 * * @author yanfengzhang * @date 2020-04-19 13:32 */ public interface IDecorator { / * 装修方法 */ void decorate(); }

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

/ * 描述:装修基本类 * * @author yanfengzhang * @date 2020-04-19 13:32 */ public class Decorator implements IDecorator { / * 基本实现方法 */ @Override public void decorate() { System.out.println("水电装修、天花板以及粉刷墙."); } }

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

/ * 描述:基本装饰类 * * @author yanfengzhang * @date 2020-04-19 13:34 */ public abstract class BaseDecorator implements IDecorator { private IDecorator decorator; public BaseDecorator(IDecorator decorator) { this.decorator = decorator; } / * 调用装饰方法 */ @Override public void decorate() { if (decorator != null) { decorator.decorate(); } } }

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

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

/ * 描述:窗帘装饰类 * * @author yanfengzhang * @date 2020-04-19 13:35 */ public class CurtainDecorator extends BaseDecorator { public CurtainDecorator(IDecorator decorator) { super(decorator); } / * 窗帘具体装饰方法 */ @Override public void decorate() { System.out.println("窗帘装饰。。。"); super.decorate(); } }

5.实际使用

/ * 描述:具体使用测试 * * @author yanfengzhang * @date 2020-04-19 13:36 */ public class Test { public static void main(String[] args) { IDecorator decorator = new Decorator(); IDecorator curtainDecorator = new CurtainDecorator(decorator); curtainDecorator.decorate(); } }

三、具体案例分析

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

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

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

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

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

  • 主订单
import java.math.BigDecimal; import java.util.List; / * 描述:主订单 * * @author yanfengzhang * @date 2020-04-19 14:01 */ @Data public class Order { / * 订单ID */ private int id; / * 订单号 */ private String orderNo; / * 总支付金额 */ private BigDecimal totalPayMoney; / * 详细订单列表 */ private List 
   
     list; } 
   
  • 详细订单
import java.math.BigDecimal; / * 描述:详细订单 * * @author yanfengzhang * @date 2020-04-19 14:06 */ @Data public class OrderDetail { / * 详细订单ID */ private int id; / * 主订单ID */ private int orderId; / * 商品详情 */ private Merchandise merchandise; / * 支付单价 */ private BigDecimal payMoney; }
  • 具体商品
import java.math.BigDecimal; import java.util.Map; / * 描述:具体商品 * * @author yanfengzhang * @date 2020-04-19 14:09 */ @Data public class Merchandise { / * 商品SKU */ private String sku; / * 商品名称 */ private String name; / * 商品单价 */ private BigDecimal price; / * 支持促销类型 */ private Map 
   
     supportPromotions; } 
   
  • 促销类型
/ * 描述:促销类型 * * @author yanfengzhang * @date 2020-04-19 14:16 */ public enum PromotionType { / * 优惠卷 */ COUPON, / * 红包 */ REDPACKED; }
  • 优惠券
import java.math.BigDecimal; / * 描述:优惠券 * * @author yanfengzhang * @date 2020-04-19 14:13 */ @Data public class UserCoupon { / * 优惠券ID */ private int id; / * 领取优惠券用户ID */ private int userId; / * 商品SKU */ private String sku; / * 优惠金额 */ private BigDecimal coupon; }
  • 红包
import java.math.BigDecimal; / * 描述:红包 * * @author yanfengzhang * @date 2020-04-19 14:14 */ @Data public class UserRedPacket { / * 红包ID */ private int id; / * 领取用户ID */ private int userId; / * 商品SKU */ private String sku; / * 领取红包金额 */ private BigDecimal redPacket; }
  • 促销类型
/ * 描述:促销类型 * * @author yanfengzhang * @date 2020-04-19 14:16 */ @Data public class SupportPromotions implements Cloneable { / * 该商品促销的ID */ private int id; / * 促销类型 1\优惠券 2\红包 */ private PromotionType promotionType; / * 优先级 */ private int priority; / * 用户领取该商品的优惠券 */ private UserCoupon userCoupon; / * 用户领取该商品的红包 */ private UserRedPacket userRedPacket; / * 重写clone方法 */ @Override public SupportPromotions clone() { SupportPromotions supportPromotions = null; try { supportPromotions = (SupportPromotions) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return supportPromotions; } }

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

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

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

/ * 描述:计算支付金额的抽象类 * * @author yanfengzhang * @date 2020-04-19 14:48 */ public abstract class BaseCountDecorator implements IBaseCount { private IBaseCount count; public BaseCountDecorator(IBaseCount count) { this.count = count; } @Override public BigDecimal countPayMoney(OrderDetail orderDetail) { BigDecimal payTotalMoney = new BigDecimal(0); if (count != null) { payTotalMoney = count.countPayMoney(orderDetail); } return payTotalMoney; } }

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

/ * 描述:计算使用优惠券后的金额 * * @author yanfengzhang * @date 2020-04-19 14:50 */ public class CouponDecorator extends BaseCountDecorator { public CouponDecorator(IBaseCount count) { super(count); } @Override public BigDecimal countPayMoney(OrderDetail orderDetail) { BigDecimal payTotalMoney = new BigDecimal(0); payTotalMoney = super.countPayMoney(orderDetail); payTotalMoney = countCouponPayMoney(orderDetail); return payTotalMoney; } private BigDecimal countCouponPayMoney(OrderDetail orderDetail) { BigDecimal coupon = orderDetail.getMerchandise().getSupportPromotions().get(PromotionType.COUPON).getUserCoupon().getCoupon(); System.out.println("优惠券金额:" + coupon); orderDetail.setPayMoney(orderDetail.getPayMoney().subtract(coupon)); return orderDetail.getPayMoney(); } }

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

/ * 描述:计算使用红包后的金额 * * @author yanfengzhang * @date 2020-04-19 15:24 */ public class RedPacketDecorator extends BaseCountDecorator { public RedPacketDecorator(IBaseCount count) { super(count); } @Override public BigDecimal countPayMoney(OrderDetail orderDetail) { BigDecimal payTotalMoney = new BigDecimal(0); payTotalMoney = super.countPayMoney(orderDetail); payTotalMoney = countCouponPayMoney(orderDetail); return payTotalMoney; } private BigDecimal countCouponPayMoney(OrderDetail orderDetail) { BigDecimal redPacket = orderDetail.getMerchandise().getSupportPromotions().get(PromotionType.REDPACKED).getUserRedPacket().getRedPacket(); System.out.println("红包优惠金额:" + redPacket); orderDetail.setPayMoney(orderDetail.getPayMoney().subtract(redPacket)); return orderDetail.getPayMoney(); } }

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

/ * 描述:计算促销后的支付价格 * * @author yanfengzhang * @date 2020-04-19 14:58 */ public class PromotionFactory { public static BigDecimal getPayMoney(OrderDetail orderDetail) { /获取给商品设定的促销类型*/ Map 
   
     supportPromotionsList = orderDetail.getMerchandise().getSupportPromotions(); /初始化计算类*/ IBaseCount baseCount = new BaseCount(); if (supportPromotionsList != null && supportPromotionsList.size() > 0) { for (PromotionType promotionType : supportPromotionsList.keySet()) { /遍历设置的促销类型,通过装饰器组合促销类型*/ baseCount = protmotion(supportPromotionsList.get(promotionType), baseCount); } } return baseCount.countPayMoney(orderDetail); } / * 组合促销类型 * @param supportPromotions * @param baseCount * @return */ private static IBaseCount protmotion(SupportPromotions supportPromotions, IBaseCount baseCount) { if (supportPromotions.getPromotionType() == PromotionType.COUPON) { baseCount = new CouponDecorator(baseCount); } else if (supportPromotions.getPromotionType() == PromotionType.REDPACKED) { baseCount = new RedPacketDecorator(baseCount); } return baseCount; } } 
   

7.实际使用操作

/ * 描述:测试 * * @author yanfengzhang * @date 2020-04-19 15:35 */ public class Test { volatile int counter = 0; private static Order init(Order order) { Map 
   
     supportPromotionslist = new HashMap 
    
      (); SupportPromotions supportPromotions = new SupportPromotions(); supportPromotions.setPromotionType(PromotionType.COUPON); supportPromotions.setPriority(1); UserCoupon userCoupon = new UserCoupon(); userCoupon.setCoupon(new BigDecimal(3)); userCoupon.setSku("aaa1111"); userCoupon.setUserId(11); supportPromotions.setUserCoupon(userCoupon); supportPromotionslist.put(PromotionType.COUPON, supportPromotions); SupportPromotions supportPromotions1 = supportPromotions.clone(); supportPromotions1.setPromotionType(PromotionType.REDPACKED); supportPromotions1.setPriority(2); UserRedPacket userRedPacket = new UserRedPacket(); userRedPacket.setId(1); userRedPacket.setRedPacket(new BigDecimal(10)); userRedPacket.setSku("aaa1111"); userCoupon.setUserId(11); supportPromotions1.setUserRedPacket(userRedPacket); supportPromotionslist.put(PromotionType.REDPACKED, supportPromotions1); Merchandise merchandise = new Merchandise(); merchandise.setSku("aaa1111"); merchandise.setName("苹果"); merchandise.setPrice(new BigDecimal(20)); merchandise.setSupportPromotions(supportPromotionslist); List 
     
       OrderDetailList = new ArrayList 
      
        (); OrderDetail orderDetail = new OrderDetail(); orderDetail.setId(1); orderDetail.setOrderId(1111); orderDetail.setMerchandise(merchandise); OrderDetailList.add(orderDetail); order.setList(OrderDetailList); return order; } public static void main(String[] args) throws InterruptedException, IOException { Order order = new Order(); init(order); for (OrderDetail orderDetail : order.getList()) { BigDecimal payMoney = PromotionFactory.getPayMoney(orderDetail); orderDetail.setPayMoney(payMoney); System.out.println("最终支付金额:" + orderDetail.getPayMoney()); } } } 
       
      
     
   

实际测试输入:

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

 

参考书籍、文献和资料

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/199594.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月20日 下午12:39
下一篇 2026年3月20日 下午12:39


相关推荐

  • python中object转为string_java中Object转String

    python中object转为string_java中Object转StringObject 转为 String 的几种形式在 java 项目的实际开发和应用中 常常需要用到将对象转为 String 这一基本功能 本文将对常用的转换方法进行一个总结 常用的方法有 Object toString String 要转换的对象 String valueOf Object 等 下面对这些方法一一进行分析 方法 1 采用 Object toString 方法请看下面的例子 Objectobje

    2026年3月19日
    0
  • Java最新JDK和API下载(持续同步更新于官网)

    Java最新JDK和API下载(持续同步更新于官网)Java最新JDK和API下载(持续同步更新于官网)提供涵盖所有主流操作系统(包括Windows、Linux、MacOS等)的Java最新稳定版JDK和对应API的下载以下所有JavaJDK的安装包、压缩包(免安装版)以及对应的API,均直接来自官网,没有添加其它任何额外内容;旨在便捷地获取最新版的JavaJDK(避免因地域原因被限制访问,因网络原因无法稳定有效下载等情况)更新时间:2021年3月17日…

    2022年7月26日
    20
  • JedisCluster密码设置「建议收藏」

    JedisCluster密码设置「建议收藏」问题:通过jedisCluster.auth(”password”);报错:redis.clients.jedis.exceptions.JedisDataException:NOAUTHAuthenticationrequired如果是jedis单机模式的话,我们可以直接使用jedis.auth来进行设置Jedisjedis=newJedis(“127.0.0.1”,6379);jedis.auth(“password”);但是jedisCluster.auth(”p..

    2022年10月14日
    8
  • CAN2.0和J1939协议的关系

    CAN2.0和J1939协议的关系转发自http://www.cankau.cn/support/help/can-vs-j1939.html很长时间没搞明白j1939与CAN2.0的关系,这篇文章让我明白了。CAN2.0是一种总线规范,是数据链路层的技术。J1939是SAE(美国汽车协会)定义的基于CAN总线的规范,目的是解决不同发动机厂商、不同ECU厂商的兼容性问题。1、J1939和CAN2.0的关系J1939是在CAN2.0…

    2022年5月18日
    49
  • 免费CDN:jsDelivr+Github 使用方法

    免费CDN:jsDelivr+Github 使用方法本文有参考 jsDeliver github 使用教程 免费好用的 cdn ByhojunCDN 的全称是 ContentDeliv 即内容分发网络 CDN 是构建在网络之上的内容分发网络 依靠部署在各地的边缘服务器 通过中心平台的负载均衡 内容分发 调度等功能模块 使用户就近获取所需内容 降低网络拥塞 提高用户访问响应速度和命中率 CDN 的关键技术主要有内容存储和分发

    2026年3月26日
    2
  • zabbix监控多实例的mysql_zabbix监控MySQL多实例实践[通俗易懂]

    zabbix监控多实例的mysql_zabbix监控MySQL多实例实践[通俗易懂]zabbix监控MySQL多实例实践发布时间:2020-07-2120:50:01来源:51CTO阅读:494作者:zxdave一、实践背景:一台机器上部署了多个MySQL实例,每个实例使用不同的端口,需要通过zabbix将其都纳入监控中。二、实践方法及原理说明:1.在Zabbix上创建监控MySQL数据库使用的模版,导入案例模版:链接:https://pan.baidu.com/s/1nXo…

    2022年5月4日
    79

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号