设计模式之装饰模式(2)--有意思的想法

news2024/11/15 19:49:39

目录

  • 背景
  • 概述
    • 概念
    • 角色
  • 基本代码分析
  • ❀❀花样重难点
    • 聚合关系
    • 认贼作父和认孙做父
    • 客户端的优化及好处
    • 继承到设计模式的演变过程
  • 总结

背景

    这是我第二次写装饰模式,这一次是在上一次的基础上进一步探究装饰模式,这一次有了很多新的感受和想法,也多了很多趣味性的内容,读完一定让你觉得写代码原来这么有意思。还是简单介绍一下装饰模式的概念,如果想了解更基础更详细的内容,可以先读一下我的第一篇装饰模式的博客:设计模式之装饰模式–优雅的增强

概述

概念

    装饰模式(Decorator Pattern)是一种结构型设计模式,动态地给一个对象添加一些额外的功能,就增加功能来说,装饰模式比生成子类更为灵活。
在这里插入图片描述
这张图今天将反复出现,先来对照概念来一一认识一下这上面的每一个类

角色

在装饰模式中,通常涉及以下几个重要角色(请对照概念和类图):

  • 抽象组件(Component)
    抽象组件定义了被装饰对象和装饰对象的共同接口。

  • 具体组件(Concrete Component)
    具体组件是实现了抽象组件接口的类,它是装饰的目标对象(要增加额外职能的类)。

  • 抽象装饰器(Decorator)
    抽象装饰器是一个包含了与抽象组件相同接口的成员变量,并从抽象组件继承的类。它的职责是在调用原始对象之前或之后,执行额外的操作(增加的额外职能的父类)

  • 具体装饰器(Concrete Decorator)
    具体装饰器是实现了抽象装饰器接口的类,它包装了具体组件,并可以在调用前后执行额外的操作(增加的具体的额外职能)。

总结一下:这种图昨天的类是要增加额外职能的类,右边一坨是要增加的额外职能。

打个比方,左边的 具体组件(Concrete Component)和右边要增加的职能抽象装饰器(Decorator)本来毫不相干,但是现在非要把它们往一起凑(非要变成一家人),怎么办,那就找了一个义父(抽象组件(Component)),强硬的变成了一家人(同一个义父的子类)

基本代码分析

抽象组件:(定义的规范:子类如何实现的规范)

public interface Component {
    void operation();
}

具体组件:

public class ConcreteComponent implements Component {
    @Override
    public void operation() {
        System.out.println("具体组件的操作");
    }
}

抽象装饰器:
    这是关键类,里面一共三部分,一个Component 类型的属性,一个setComponent方法,用来给属性赋值,这里使用了多态,参数Component component,看起来接受的是一个Component 类型的变量,但是自始至终,接受的只是Component 的子类(ConcreteComponent )和孙类(ConcreteDecoratorA 和ConcreteDecoratorB),用两个词语里形容就是 认贼作父和认孙做父!这个放到后面细说。第三部分是重写的父类方法,调的是传进来的Component类型的变量的operation方法。

public abstract class Decorator implements Component {
    protected Component component;

    public void setComponent(Component component) {
        this.component = component;
    }

    @Override
    public void operation() {
        if (component != null) {
            component.operation();
        }
    }
}

具体装饰器A:
    这两个具体的装饰类继承自Decorator ,同时也继承自Component (双层继承),这里的重点是operation方法中的super.operation();也就是这个方法让整个装饰的过程按照正确顺序串联起来
    ConcreteDecoratorA 中增加了一个属性,也就是给被装饰对象增加的额外职责。
    ConcreteDecoratorB中增加了一个方法,给给装饰对象增加的额外职责。
    这两个类算是从两个维度,告诉我们如何给被装饰对象增加额外职责的。

public class ConcreteDecoratorA extends Decorator {
    private String addedProperty;
    @Override
    public void operation() {
        super.operation();
         addedProperty="New State";
        System.out.println("具体装饰器A的操作);
    }

   
}

具体装饰器B:

public class ConcreteDecoratorB extends Decorator {
    @Override
    public void operation() {
        super.operation();
        addedMethod();
    }

    public void addedMethod() {
        System.out.println("具体装饰器B的操作");
    }
}

客户端使用:

ConcreteComponent c= new ConcreteComponent();
ConcreteDecoratorA d1= new ConcreteDecoratorA();
ConcreteDecoratorB d2= new ConcreteDecoratorB();

d1.setComponent(c);
d2.setComponent(d1);
ConcreteDecoratorB.operation();

    客户端里先实例化了三个对象
    第一对象C是要装饰的原有对象。
    后面两个对象是具体的装饰对象。

重点来了:我来说一下下面代码的执行顺序,不要眨眼**

    d1.setComponent ( c ) 这句代码执行的时候会先到ConcreteDecoratorA 类中,没有找到setComponent 方法,因此会向它的父类Decorator 中找,然后把C对象传递给Decorator 的component属性
    同样,d2.setComponent(d1)执行的时候,会把装饰了C对象的d1对象传递给Decorator 的component属性。这里一定要注意是装饰了C对象的d1对象。

    接下来,执行ConcreteDecoratorB.operation();
会先到ConcreteDecoratorB找到operation方法,这个方法里的第一句是super.operation();
在这里插入图片描述
    是先执行父类Decorator 的operation方法,这个方法里先判空,然后执行component的operation,这个时候要注意,这个component是刚刚上面传进来的d1,
在这里插入图片描述
    此时会d1是ConcreteDecoratorA 类型的,那又回到了ConcreteDecoratorA 中执行operation方法,接下来和刚才一样,执行父类Decorator 的operation方法,判空,然后执行component的operation,这个component是刚刚上面传进来的c,c是ConcreteComponent 类型,然后执行ConcreteComponent 里面的operation方法,打印“具体组件的操作”,后面就是一路返回,怎么来的怎么回去。

输出结果为:

具体组件的操作
具体装饰器A的操作
具体装饰器B的操作

❀❀花样重难点

聚合关系

    还是这张图
    红框框住的两部分,被装饰对象和具体装饰对象加起来是Component (儿子和孙子的抽象),也就是聚合关系的箭头所指,他们加起来聚合到了Decorator ,这是形式上的聚合,在代码中并没有体现(关于这一点还有待探究)
那么为什么不是Decorator自聚合呢,如果是自聚合,只是代表Decorator是具体装饰的容器,那么就不能把服饰包装到ConcreteComponent 上了
在这里插入图片描述
    关于上面的解释在代码中的体现就是Decorator类中的Component 类型的参数
在这里插入图片描述
    这里引出来我的下一个问题

认贼作父和认孙做父

    Component 本来是Decorator的父类,但是在代码的实际运行中,传的只有ConcreteComponent 和ConcreteDecoratorA 和ConcreteDecoratorB。
    ConcreteComponent 相对于Decorator来说是同辈份。(认贼作父)
    ConcreteDecoratorA 和ConcreteDecoratorB都是Decorator的子类。(认孙做父)

客户端的优化及好处

在这里插入图片描述
    这三段代码,可以看到引用都是子类型的,其实完全是可以用父类型去接
    先问个问题,这三个实例都可以使用Component去接吗?
    不要被迷惑住了,虽然他们都继承自Component,但是Component里可是没有setComponent方法的,所以只有new ConcreteComponent()可以用Component接。
代码可以改成:

Component c= new ConcreteComponent();
Decorator d1= new ConcreteDecoratorA();
Decorator d2= new ConcreteDecoratorB();

    这样写有什么好处呢?
多客户端的复用
    比如后两句都用Decorator 去接,=号右边完全可以使用配置文件,动态new对象,如果把没一句都看成一个按钮背后的逻辑,每个按钮都是一个客户端,放到页面上就实现了并行。重复的代码就可以使用自动生成。

继承到设计模式的演变过程

    最后再说一个我自己的想法,仅供参考
    还记得开头概念中说过的“就增加功能来说,装饰模式比生成子类更为灵活”,就先来说说继承,如果我们想给一个类增加功能,如果不在原有类中增加代码的话,可以使用继承的形式增加新的代码,那先看下面的图中左边的图,这个例子是设计模式书上的给人穿衣服的例子,如果不清楚,可以参照我的上一篇博客中的代码,链接在最上面。
    加入TShirt是要给Person增加的功能,那么可以使用继承。左边的图。
    那么根据封装变化点(这是面向对象的精髓),现在是增加了一个TShirt,我又想增加一个其他的类TShirt2,你会怎么做,会抽出一个父类,遵循依赖倒置原则,会让Person和接口Finery产生关系,就变成了右边的图
在这里插入图片描述

    同样的道理,现在被装扮类不止一个Person,我也想给Animal装扮。那么就给Person和Animal抽出一个父类Component,遵循依赖倒置原则,会让Finery和接口Component产生关系,就变成了右边的图。现在看这张图和我开篇放的那张装饰模式的图是不是基本一致了,只是缺少了聚合关系。
关于聚合关系,在业务实现过程中并没有体现,这点以后有机会再研究。

在这里插入图片描述

总结

    装饰模式算比较难的设计模式了,进行了多次学习,每次学习都有不一样的收获,而且越来越有意思,希望今天的文章也能带给你同样的感受。如果你也有有意思的想法和不同意见,欢迎指教,最后感谢阅读。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1272329.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

盘点:信息化、智能化、可视化监控系统EasyCVR能给高危行业带来哪些好处

在当前社会中,高危行业的安全问题备受关注。为了有效预防事故的发生,信息化、智能化和可视化监控系统被广泛应用于各个高危行业。本文将从多个角度分析并总结,这些系统给高危行业带来的好处。 一、实时监控与预警能力 国标GB28181视频平台E…

【Java Web学习笔记】0 - 技术体系的说明

B/S软件开发架构简述 B/S架构 1.B/S框架,意思是前端(Browser浏览器)和服务器端( Server )组成的系统的框架结构。 2. B/S架构也可理解为web架构,包含前端、后端、数据库三大组成部分。 3.示意图 ●前端 前端开发技术工具包括三要素: HTML、CSS和Jav…

WIN10系统自带硬盘测速工具使用

前段时间在捣腾电脑硬盘这一块,因为现在固态硬盘价格比较低了,所以想换一下,给自己的电脑升个级。现在硬盘有多种接口,常见的就是SATA,mSATA,m.2, NVME,PCIE。这里PCIE的接口是直连的&#xff0…

基于Java SSM框架+Vue实现汉服文化平台网站项目【项目源码+论文说明】

基于java的SSM框架Vue实现汉服文化平台系统演示 摘要 本论文主要论述了如何使用JAVA语言开发一个汉服文化平台网站 ,本系统将严格按照软件开发流程进行各个阶段的工作,采用B/S架构,面向对象编程思想进行项目开发。在引言中,作者将…

zookeeper 单机伪集群搭建简单记录(实操课程系列)

本系列是zookeeper相关的实操课程,课程测试环环相扣,请按照顺序阅读测试来学习zookeeper 1、官方下载加压后,根目录下新建data和log目录,然后分别拷贝两份,分别放到D盘,E盘,F盘 2、data目录下面…

甘草书店记:2023年10月24日 星期二 「在完美和高效之间寻求平衡」

书店装修设计图出了第一版,不能够完全满意也在不在预料之外。 中国人的哲学是中庸的哲学。在高效中去追逐完美,在追逐完美中提升效率。 分享余华先生在节目中的一段话: 一种阅读和一本书的相遇有时候也是一种缘分。但也可能就是所有人都说…

航城街道携股份公司一行莅临联诚发考察调研

11月30日,深圳市宝安区航城街道组织开展主题为“交流促发展携手创未来”的调研服务日活动。当日上午,航城街道委员刘甜携集体资产监管办、黄田股份合作公司、钟屋股份合作公司、草围股份合作公司、鹤洲股份合作公司、九围股份合作公司、后瑞股份合作公司…

习题补充整理

目录 一、自己封装response 二、在响应头中放数据 HttpResponse redirect ​编辑 render JsonResponse 三、函数和方法区别 ----》绑定方法区别 四、上传图片和开启media访问 五、页面静态化(解决访问率高的问题) 一、自己封装response 第一步,在app01下…

【注册表】Sublime Text添加到右键菜单

官网下载 windows下地地址: http://www.sublimetext.com/download_thanks?targetwin-x64设置右键菜单和菜单小图标 win R打开运行,并输入regedit打开注册表编辑器依次找到HKEY_CLASSESS_ROOT -> * -> Shell,下面新建项, 这个项的名…

制作rpm安装包时如何处理升级

博客主页:https://tomcat.blog.csdn.net 博主昵称:农民工老王 主要领域:Java、Linux、K8S 期待大家的关注💖点赞👍收藏⭐留言💬 目录 升级命令执行顺序重要参数版本问题注意事项 升级命令 rpm安装包的升级…

web前端tips:js继承——寄生组合式继承

上篇文章给大家分享了 js继承中的 寄生式继承 web前端tips:js继承——寄生式继承 今天给大家分享一下 js 继承中的 寄生组合式继承 寄生组合式继承 寄生组合式继承是一种结合了寄生式继承和组合式继承的方式,它的目标是减少组合式继承中多余的调用父…

力扣295. 数据流的中位数(java,堆解法)

Problem: 295. 数据流的中位数 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 由于该题目的数据是动态的我们可以维护两个堆来解决该问题 1.维护一个大顶堆,一个小顶堆 2.每个堆中元素个数接近n/2;如果n是偶数,两个堆中的数据个数…

最简单的链路追踪收集器

链路追踪可帮助您快速了解程序服务之间的调用关系,并快速洞悉内部发生的情况。主流的链路追踪系统有zipkin,jaeger,skywalking等,由于opentelemetry的存在,都具有opentelemetry的转换器。 我们利用opentelemetry来进行zipkin,jaeger,skywalk…

在编程中遇到的问题总结

IDEA空包粘黏问题 创建好目录以后会发现idea自动将空包合并在一起了,而且点击设置里面也没有Compact Middle Package Compact Middle Package如果不在设置的主面板上,则点击Tree Appearance,会发现Compact Middle Package在Tree Appearance里…

git-5

1.GitHub为什么会火? 2.GitHub都有哪些核心功能? 3.怎么快速淘到感兴趣的开源项目 github上面开源项目非常多,为了我们高效率的找到我们想要的资源 根据时间 不进行登录,是没有办法享受到高级搜索中的代码功能的,登录…

Glove学习笔记

global vectors for word representation B站学习视频 1、LSA与word2vec 我们用我们的见解,构建一个新的模型,Glove,全局向量的词表示,因为这个模型捕捉到全局预料的统计信息。 LSA:全局矩阵分解word2vec:局部上下文…

基于Linux下搭建NextCloud构建自己的私有网盘

NextCloud是什么 Nextcloud是一款开源免费的私有云存储网盘项目,可以让你快速便捷地搭建一套属于自己或团队的云同步网盘,从而实现跨平台跨设备文件同步、共享、版本控制、团队协作等功能。它的客户端覆盖了Windows、Mac、Android、iOS、Linux 等各种平…

《opencv实用探索·五》opencv小白也能看懂的图像腐蚀

1、图像腐蚀原理简单理解: 腐蚀是形态学最基本的操作,都是针对白色部分(高亮部分)而言的。即原图像中高亮部分被蚕食,得到比原图更小的区域。 2、图像腐蚀的作用: (1)去掉毛刺&…

在idea中写sql语句,向数据库添加数据时,添加的字符串却显示???,解决方法

这是字符编码的问题 如何解决: 在idea的配置数据库的地方修改下边:mysql8版本和5版本差距不大。 在URL后加?useUnicodetrue&characterEncodingUTF8 例如 原来:String url “jdbc:mysql://localhost:3306/stu”; 改变后:St…

Android的启动模式

Android的四种启动模式:standard、singleTop、singleTask和singleInstance。 1.standard Android默认的启动模式是standard,每启动一个Activity,它都会在返回栈中入栈,并处于栈顶,不管这个返回栈是否存在这个activit…