3年工作必备 装饰器模式

好啦,进入我们的主题,今天我给大家分享设计模式中的装饰器模式。用贴切的生活故事,以及真实项目场景来讲设计模式,最后用一句话来总结这个设计模式。

故事

古话说的好:人靠衣裳马靠鞍。下面先带大家来熟悉这句话的背景:

人靠衣装马靠鞍,狗配铃铛跑的欢出自沈自晋《望湖亭记》第十出:“虽然如此,佛靠金装,人靠衣装,打扮也是很要紧的。”《醒世恒言》卷一‧两县令竞义婚孤女:”常言道:’佛是金装,人是衣装,世人眼孔浅的多,只有皮相,没有骨相。’”俗语我们会说成人靠衣装马靠鞍。

这个经典故事,让我想起了一个设计模式:装饰器模式。

什么是装饰器模式呢?请听老田慢慢道来。

装饰器模式概述

装饰器模式(Decorator Pattern)也叫作包装器模式(Wrapper Pattern),指在不改变原有对象的基础上,动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活,属于结构型设计模式。

英文:

Attach additional responsibilities to an object dynamicallykeeping the same interface.Decorators provide a flexible alternativeto subclassing for extending functionality.

装饰器模式提供了比继承更有弹性的替代方案(扩展原有对象的功能)将功能附加到对象上。因此,装饰器模式的核心是功能扩展。使用装饰器模式可以透明且动态地扩展类的功能。

生活中的案例

一套毛坯房,没有装修之前,看起来非常难看,但只要稍微装修一番,那就漂亮多了,并且能洗澡、睡觉、做饭等,但本质还是房子。

一辆汽车,原本就是一辆代步的车,但是玛丽加大,配置提升,然后就成了豪车,但本质还是一辆代步的车。

一个女生,原本很平凡,长相一般,但是经过一番化妆,再穿点好看的衣服,然后就成了很多人心中的女神了。

总之,经过点装饰后,就是不一样了,功能增强了。

装饰器模式通用代码实现

我们还是用代码来实现一把,程序员都喜欢先搞个demo,然后再慢慢研究。

//抽象组件public abstract class Component {    public abstract void operation();}//具体组件public class ConcreteComponent extends Component {    @Override    public void operation() {        System.out.println("ConcreteComponent operation");    }}//装饰器抽象public abstract class Decorator extends Component {    protected Component component;    public Decorator(Component component) {        this.component = component;    }    @Override    public void operation() {        component.operation();    }}//具体装饰器public class ConcreteDecorator extends Decorator {    public ConcreteDecorator(Component component) {        super(component);    }    @Override    public void operation() {        System.out.println("开始前搞点事");        super.operation();        System.out.println("结束后搞点事");    }}//测试public class Client {    public static void main(String[] args) {        Component component = new ConcreteDecorator(new ConcreteComponent());        component.operation();    }}

登录后复制

运行结果:

开始前搞点事ConcreteComponent operation结束后搞点事

登录后复制

以上便是装饰器模式的通用代码实现,下面我们来分析一下。

装饰器模式UML图

3年工作必备 装饰器模式

从UML途中可以看出,其中的角色

装饰器模式中的角色

抽象组件(Component):可以是一个接口或者抽象类,充当被装饰类的原始对象,规定了被装饰对象的行为。
具体组件(ConcreteComponent):实现/继承Component的一个具体对象,即被装饰对象。
抽象装饰器(Decorator):通用的装饰ConcreteComponent的装饰器,其内部必然有一个属性指向Component;其实现一般是一个抽象类,主要为了让其子类按照其构造形式传入一个Component,这是强制的通用行为。如果系统中装饰逻辑单一,则并不需要实现许多装饰器,可以直接省略该类,而直接实现一个具体装饰器即可。
具体装饰器(ConcreteDecorator):Decorator的具体实现类,理论上,每个ConcreteDecorator都扩展了Component对象的一种功能。

小结

装饰器模式角色分配符合设计模式的里氏替换原则、依赖倒置原则,从而使得其具备很强的扩展性,最终满足开闭原则。

装饰器模式的实现原理是,让装饰器实现与被装饰类(例如ConcreteComponent)相同的接口(例如Component),使得装饰器与被扩展类类型一致,并在构造函数中传入该接口对象,然后在实现这个接口的被包装类对象的现有功能上添加新功能。由于装饰器与被包装类属于同一类型(均为Component),且构造函数的参数为其实现接口类(Component),因此装饰器模式具备嵌套扩展功能,这样就能使用装饰器模式一层一层地对底层被包装类进行功能扩展了。

实战

在实际开发中,都会存在系统与系统之间的调用,假如说我们现在有个支付功能,现在一切都是没问题的,但是 我们此时需要对发起支付前的请求参数和支付后的相应参数。进行统一处理,原功能不变,只是在原功能上做了一点扩展(增强)。

老功能代码如下:

/** * @author 田先生 * @date 2021-06-02 * * 欢迎关注公众号:java后端技术全栈 */public interface IOrderPayService {    String payment(Long orderId, BigDecimal amount);}public class OrderPayServiceImpl implements IOrderPayService {    @Override    public String payment(Long orderId, BigDecimal amount) {        //先调用余额查询是否足够        System.out.println("发起支付,订单号:" + orderId + ", 支付金额:" + amount.toString());        //调用支付系统        String result = "订单id=" + orderId + "支付完成";        System.out.println("支付结果:" + result);        return result;    }}public class OrderClient {    public static void main(String[] args) {        IOrderPayService orderPayService = new OrderPayServiceImpl();        orderPayService.payment(10001L,new BigDecimal("5000"));    }}

登录后复制

运行输出:

发起支付,订单号:10001, 支付金额:5000支付结果:订单id=10001支付完成

登录后复制

新需求,需要把这些请求参数和相应结果进行单独搜集处理,此时为了不影响原有功能,于是我们可以对其进行功能增强。

/** * @author 田先生 * @date 2021-06-02 * * 欢迎关注公众号:java后端技术全栈 */public class OrderPayDecorator implements IOrderPayService {    private IOrderPayService orderPayService;    public OrderPayDecorator(IOrderPayService orderPayService) {        this.orderPayService = orderPayService;    }    @Override    public String payment(Long orderId, BigDecimal amount) {        System.out.println("把这个订单信息(发起支付)" + "订单id=" + orderId + "支付金额=" + amount.toString() + " 【发送给MQ】");        String result = orderPayService.payment(orderId, amount);        System.out.println("把订单支付结果信息" + result + " 【发送给MQ】");        return result;    }}public class OrderClient {    public static void main(String[] args) {        IOrderPayService orderPayService =new OrderPayDecorator(new OrderPayServiceImpl());        orderPayService.payment(10001L,new BigDecimal("5000"));    }}

登录后复制

运行输出:

把这个订单信息(发起支付)订单id=10001支付金额=5000 【发送给MQ】发起支付,订单号:10001, 支付金额:5000支付结果:订单id=10001支付完成把订单支付结果信息订单id=10001支付完成 【发送给MQ】

登录后复制

整个过程,大家有没有发现,我们并没动原有的代码,仅仅只是做了功能增强。

装饰器模式在新项目中基本上不会用到,通常都是在老项目中使用,因为已有的功能不变,只是做了一些功能增强。

大神们是怎么用的

装饰器设计模式在JDK源码、Spring源码以及Mybatis源码中都有。

JDK源码中

装饰器模式比较经典的应用就是 JDK 中的 java.io 包下,InputStream、OuputStream、Reader、Writer 及它们的子类。

以 InputStream 为例

FileInputStream 是 InputStream 的子类,用来读取文件字节流
BufferedInputStream 是 InputStream 的子类的子类,可缓存的字节流
DataInputStream 也是 InputStream 的子类的子类,可直接读取 Java 基本类型的字节流

UML图

3年工作必备 装饰器模式

DataInputStream 中构造器入参便是自己的父类(InputStream)。

3年工作必备 装饰器模式

如果希望提供一个可以读取文件 + 可缓存的字节流,使用继承方式,就需要派生 FileBufferedInputStream;

如果希望提供一个可以读取文件 + 直接读取基本类型的字节流,使用继承方式,就需要派生 FileDataInputStream。

字节流功能的增强还包括支持管道 pipe、字节数组 bytearray、字节对象 object、字节流字符流的转换 等维度,如果用继承方式,那类的层级与种类会多到爆炸。

为了解决问题,这边就使用了装饰器模式。

Spring源码中

在Spring中,我们可以尝试理解一下TransactionAwareCacheDecorator类,这个类主要用来处理事务缓存,代码如下。

public class TransactionAwareCacheDecorator implements Cache {    private final Cache targetCache;    //构造方法入参类型为自己的父类(接口类型)    public TransactionAwareCacheDecorator(Cache targetCache) {        Assert.notNull(targetCache, "Target Cache must not be null");        this.targetCache = targetCache;    }    public Cache getTargetCache() {        return this.targetCache;    }    //...}

登录后复制

TransactionAwareCacheDecorator就是对Cache的一个包装,因此,这里也是使用了装饰器模式。

Mybatis源码中

MyBatis中关于Cache和CachingExecutor接口的实现类也使用了装饰者设计模式。Executor是MyBatis执行器,是MyBatis 调度的核心,负责SQL语句的生成和查询缓存的维护;CachingExecutor是一个Executor的装饰器,给一个Executor增加了缓存的功能。此时可以看做是对Executor类的一个增强,故使用装饰器模式是合适的。

在CachingExecutor 中

public class CachingExecutor implements Executor {  //持有组件对象  private Executor delegate;  private TransactionalCacheManager tcm = new TransactionalCacheManager();    //构造方法,传入组件对象  public CachingExecutor(Executor delegate) {    this.delegate = delegate;    delegate.setExecutorWrapper(this);  }  @Override  public int update(MappedStatement ms, Object parameterObject) throws SQLException {      //转发请求给组件对象,可以在转发前后执行一些附加动作    flushCacheIfRequired(ms);    return delegate.update(ms, parameterObject);  }  //... }

登录后复制

总结

看完装饰器模式后,你是否有感觉,装饰器模式和代理模式非常的相像,下面我们就来做个对比。

1.装饰器模式可以理解为一种特殊的代理模式。

2.装饰器模式强调自身的功能扩展,透明的扩展(即用户想增强什么功能就增强什么功能),可动态定制的扩展。

3.代理模式强调的是代理过程的控制。

优点

装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态地给一个对象扩展功能,即插即用。
通过使用不同装饰类及这些装饰类的排列组合,可以实现不同效果。
装饰器模式完全遵守开闭原则。

缺点

会出现更多的代码、更多的类,增加程序的复杂性。
动态装饰在多层装饰时会更复杂。

以上就是3年工作必备 装饰器模式的详细内容,更多请关注【创想鸟】其它相关文章!

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

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2624427.html

(0)
上一篇 2025年3月7日 00:20:03
下一篇 2025年3月6日 10:56:44

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 如何在C++中实现装饰器设计模式?

    装饰器模式在 c++++ 中的实现过程分为以下几个步骤:定义抽象类 icomponent,表示期望实现的对象接口。创建具体的 concretecomponent 类,实现 icomponent 接口,表示原始对象。定义抽象 decorato…

    2025年3月6日
    200
  • 分析Python中设计模式之Decorator装饰器模式的要点

    先给出一个四人团对Decorator mode的定义:动态地给一个对象添加一些额外的职责。再来说说这个模式的好处:认证,权限检查,记日志,检查参数,加锁,等等等等,这些功能和系统业务无关,但又是系统所必须的,说的更明白一点,就是面向方面的编…

    编程技术 2025年3月5日
    200
  • golang的框架如何通过装饰器模式实现代码复用?

    go 框架中的装饰器模式允许动态地为对象添加新功能。通过函数实现,它可以扩展日志记录功能(例:将消息写入文件),并添加认证到 http 处理程序(例:检查请求验证)。装饰器模式提高了代码的复用性和可维护性。 Go 框架中通过装饰器模式实现代…

    2025年2月28日
    200
  • Python的装饰器模式与面向切面编程详解

    今天来讨论一下装饰器。装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用。概括的…

    编程技术 2025年2月28日
    200
  • PHP中如何使用装饰器模式?

    装饰器模式允许在不修改原始对象的情况下动态添加功能,通过创建包装器类实现。通过将装饰器包装在现有对象周围,可以轻松添加额外功能。装饰器模式可以组合使用,并且可以通过移除装饰器来逆转操作,实现灵活的对象功能扩展。 PHP 中的装饰器模式 在 …

    2025年2月19日
    200
  • PHP 设计模式:通往代码卓越的道路

    导言 php设计模式是程序员在开发过程中应用的重要概念,可以提高代码质量和可维护性。php小编新一特别推出“php设计模式:通往代码卓越的道路”系列文章,深入解析常见设计模式的原理和实际应用,帮助读者更好地理解和运用设计模式,提升代码水平,…

    编程技术 2025年2月19日
    200

发表回复

登录后才能评论