关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

带你读《2022技术人的百宝黑皮书》——浅析设计模式3 —— 装饰者模式(2)

发布时间:2023-06-28 19:00:33

带你读《2022技术人的百宝黑皮书》——浅析设计模式3 —— 装饰者模式(1)

https://developer.leiyu.cn/article/1262382?groupCode=taobaotech



基本概念

装饰者模式的核心思想是通过创建一个装饰对象(即装饰者),动态扩展目标对象的功能,并且不会改变目标对象 的结构,提供了一种比继承更灵活的替代方案。需要注意的是,装饰对象要与目标对象实现相同的接口,或继承相 同的抽象类;另外装饰对象需要持有目标对象的引用作为成员变量,而具体的赋能任务往往通过带参构造方法来完 成。


下面继续从模式结构和使用步骤两个层面,简单阐述装饰者模式的基本概念。


结构


装饰者模式包含四种类,分别是抽象构件类、具体构件类、抽象装饰者类、具体装饰者类,它们各自负责完成特定 任务,并且相互之间存在紧密联系。



使用

有了上述的基本概念,我们将装饰者模式的使用步骤概括为:

1. step1:创建抽象构件类,定义目标对象的抽象类、将要扩展的功能定义成抽象方法;

2. step2:创建具体构件类,定义目标对象的实现类,实现抽象构件中声明的抽象方法;

3. step3:创建抽象装饰者类,维护一个指向抽象构件的引用,并传入构造函数以调用具体构件的实现方法,给具 体构件增加功能;

4. step4:创建具体装饰者类,可以调用抽象装饰者类中定义的方法,并定义若干个新的方法,扩展目标对象的功能。


使用示例

我们在淘宝上购物时,经常会遇到很多平台和商家的优惠活动:满减、聚划算站内的百亿补贴券、店铺折扣等等。 那么在商品自身原价的基础上,叠加了多种优惠活动后,后台应该怎样计算最终的下单结算金额呢?下面就以这种 优惠叠加结算的场景为例,简单分析装饰者模式如何使用。


代码实现

// 定义抽象构件:抽象商品 public interface ItemComponent {  // 商品价格  public double checkoutPrice(); }  // 定义具体构件:具体商品 public class ConcreteItemCompoment implements ItemComponent {  // 原价  @Override  public double checkoutPrice() {  return 200.0;  } }  // 定义抽象装饰者:创建传参(抽象构件)构造方法,以便给具体构件增加功能 public abstract class ItemAbsatractDecorator implements ItemComponent {  protected ItemComponent itemComponent;   public ItemAbsatractDecorator(ItemComponent myItem) {  this.itemComponent = myItem;  }   @Overrid  public double checkoutPrice() {  return this.itemComponent.checkoutPrice();  } }  // 定义具体装饰者A:增加店铺折扣八折 public class ShopDiscountDecorator extends ItemAbsatractDecorator {  public ShopDiscountDecorator(ItemComponent myItem) {  super(myItem);  }   @Override  public double checkoutPrice() {  return 0.8 * super.checkoutPrice();  } }  // 定义具体装饰者B:增加满200减20功能,此处忽略判断逻辑 public class FullReductionDecorator extends ItemAbsatractDecorator {  public FullReductionDecorator(ItemComponent myItem) {  super(myItem);  }   @Override  public double checkoutPrice() {  return super.checkoutPrice() - 20;  } }  // 定义具体装饰者C:增加百亿补贴券50 public class BybtCouponDecorator extends ItemAbsatractDecorator {  public BybtCouponDecorator(ItemComponent myItem) {  super(myItem);  }   @Override  public double checkoutPrice() {  return super.checkoutPrice() - 50;  } }   //客户端调用 public class userPayForItem() {  public static void main(String[] args) {  ItemCompoment item = new ConcreteItemCompoment();  System.out.println("宝贝原价:" + item.checkoutPrice() + " 元");  item = new ShopDiscountDecorator(item);  System.out.println("使用店铺折扣后需支付:" + item.checkoutPrice() + " 元");  item = new FullReductionDecorator(item);  System.out.println("使用满200减20后需支付:" + item.checkoutPrice() + " 元");  item = new BybtCouponDecorator(item);  System.out.println("使用百亿补贴券后需支付:" + item.checkoutPrice() + " 元");  } }

   


结果输出

宝贝原价:200.0 元 使用店铺折扣后需支付:160.0 元 使用满200减20后需支付:140.0 元 使用百亿补贴券后需支付:90.0 元

   


UML图



比较分析


VS 继承

装饰者模式和继承关系都是要对目标类进行功能扩展,但装饰模式可以提供比继承更多的灵活性:继承是静态添加 功能,在系统运行前就会确定下来;装饰者模式是动态添加、删除功能。


比如,一个对象需要具备 10 种功能,但客户端可能要求分阶段使用对象功能:在第一阶段只执行第 1-8 项功能, 第二阶段执行第 3-10 项功能,这种场景下只需先定义好第 3-8 项功能方法。在程序运行的第一个阶段,使用具体 装饰者 A 添加 1、2 功能;在第二个运行阶段,使用具体装饰者 B 添加 9、10 功能。而继承关系难以实现这种需 求,它必须在编译期就定义好要使用的功能。


VS 代理模式


装饰者模式常常被拿来和代理模式比较,两者都要实现目标类的相同接口、声明一个目标对象,并且都可以在不修 改目标类的前提下进行方法扩展,整体设计思路非常相似。那么两者的区别是什么呢?


首先,装饰者模式的重点在于增强目标对象功能,而代理模式的重点在于保护和隐藏目标对象。其中,装饰者模式 需要客户端明确知道目标类,才能对其功能进行增强;代理模式要求客户端对目标类进行透明访问,借助代理类来 完成相关控制功能(如日志记录、缓存设置等),隐藏目标类的具体信息。可见,代理类与目标类的关系往往在编 译时就确定下来,而装饰者类在运行时动态构造而成。


其次,两者获取目标类的方式不同。装饰者模式是将目标对象作为参数传给构造方法,而代理模式是通过在代理类 中创建目标对象的一个实例。


最后,通过上述示例可发现,装饰者模式会使用一系列具体装饰者类来增强目标对象的功能,产生了一种连续、叠 加的效应;而代理模式是在代理类中一次性为目标对象添加功能。


VS 适配器模式


两者都属于包装式行为,即当一个类不能满足需求时,创建辅助类进行包装以满足变化的需求。但是装饰者模式的 装饰者类和被装饰类都要实现相同接口,或者装饰类是被装饰类的子类;而适配器模式中,适配器和被适配的类可 以有不同接口,并且可能会有部分接口重合。


带你读《2022技术人的百宝黑皮书》——浅析设计模式3 —— 装饰者模式(3)

https://developer.leiyu.cn/article/1262380?groupCode=taobaotech


/template/Home/leiyu/PC/Static