设计模式之装饰模式--优雅的增强

目录

  • 概述
    • 什么是装饰模式
    • 为什么使用装饰模式
    • 关键角色
    • 基本代码
    • 应用场景
  • 版本迭代
    • 版本一
    • 版本二
    • 版本三—装饰模式
  • 装饰模式中的巧妙之处
    • 1、被装饰对象和装饰对象共享相同的接口或父类
    • 2、当调用装饰器类的装饰方法时,会先调用被装饰对象的同名方法
    • 3、子类方法与父类方法共享相同的引用
    • 4、装饰模式与继承的对比
  • 总结

概述

什么是装饰模式

    装饰模式(Decorator Pattern)是一种结构型设计模式,它允许通过将对象放入包含行为的特殊封装对象中来为原始对象添加新的行为。装饰模式在不改变原始对象接口的情况下,动态地将责任附加到对象上。就增加功能来看,装饰模式比生成子类更为灵活。

为什么使用装饰模式

    使用装饰模式有以下几个优点:

灵活性:装饰模式允许在运行时动态地给对象添加功能,而不需要修改其代码或使用继承。这使得系统更加灵活,易于扩展和修改。
单一职责原则:装饰模式能够将不同的功能划分到不同的类中,使每个类只负责单一的功能。这遵循了单一职责原则,使得代码更加清晰、可读性更高。
可组合性:由于装饰模式使用了对象组合而不是继承,因此可以通过不同的方式将装饰器组合起来,以获得不同的行为组合。
装饰模式的结构

关键角色

  • 抽象组件(Component):定义了原始对象和装饰器的公共接口,可以是接口或抽象类。
  • 具体组件(Concrete Component):实现了抽象组件接口,是被装饰的原始对象。
  • 装饰器(Decorator):实现了抽象组件接口,并持有一个抽象组件对象的引用,在装饰器中可以添加一些额外的行为。
    -具体装饰器(Concrete Decorator):继承自装饰器,具体实现了在装饰器中定义的额外行为。

基本代码

应用场景

1、在不改变现有对象结构的情况下,对对象的功能进行扩展或修改。
2、需要动态地给对象添加功能,以便根据需要增加或移除功能。
3、对象的职责应该能够被多次扩展,而不会导致类的数量急剧增加。

版本迭代

需求:给人搭配不同的服饰

版本一

//Person
public class Person {private String name;public Person(String name ){this.name=name;}public void wearTShirts(){System.out.println("大T恤");}public void wearBigTrouser(){System.out.println("跨裤");}public void wearSneakers(){System.out.println("球鞋");}public void show(){System.out.println("装扮的"+name);}
}
//客户端
public class Client {public static void main(String[] args) {Person xc=new Person("小蔡");System.out.println("第一种装扮");xc.wearTShirts();xc.wearBigTrouser();xc.wearSneakers();xc.show();System.out.println("--------------");System.out.println("第二种装扮");xc.wearTShirts();xc.wearSneakers();xc.show();}
}

    版本一非常简单,给人穿衣服就只有Person这个类,但是需求如果需要增加“超人”的装扮,就需要修改Person类(毕竟超人不是人,不能使用实例化Person,并且超人的衣服最起码是有个内裤和斗篷的~~)
    这个时候怎么办,把服饰拿出去作为单独的类是不是就可以了呢

版本二

//Person
public class Person {private String name;public Person(String name){this.name=name;}public void show(){System.out.println("装扮的"+name);}
}//抽象服饰类
public abstract class Finery {public abstract void show();
}//具体的服饰类
public class BigTrouser extends Finery{@Overridepublic void show() {System.out.println("跨裤");}
}public class TShirts extends Finery{@Overridepublic void show() {System.out.println("大t恤");}
}//客户端
public class client {public static void main(String[] args) {Person xc=new Person("小蔡");Finery tt=new TShirts();Finery kk=new BigTrouser();kk.show();tt.show();xc.show();}
}

    新的问题好像又来了,客户端看起来就是光着身子的小蔡一件件的穿上了衣服,也就是说穿衣服的过程不应该在客户端显示,而应该在内部完成,并且每个人的爱好不一样,穿衣服的顺序并不确定,这个建造者就不同了,而是需要把所有的功能按照正确顺序串联起来进行控制。装饰模式上场了

版本三—装饰模式

//服饰抽象类
public interface ICharacter {public void show();
}//被装饰对象人
public class Person implements ICharacter{private String name;public Person(String name){this.name=name;}@Overridepublic void show() {System.out.println("装扮的"+name);}
}
//装饰类
public class Finery implements ICharacter{protected ICharacter component;public void decorate(ICharacter component){this.component=component;}@Overridepublic void show() {if(component !=null){component.show();}}
}//具体装饰类
public class Sneakers extends Finery{public void show(){System.out.println("球鞋");super.show();}}public class BigTrouser extends Finery{public void show(){System.out.println("裤子");super.show();}}public class Tshirts extends Finery{public void show(){System.out.println("T恤");super.show();}
}//客户端
public class Client {public static void main(String[] args) {Person xc=new Person("小蔡");System.out.println("第一种装扮");Sneakers pqx=new Sneakers();pqx.decorate(xc);BigTrouser kk=new BigTrouser();kk.decorate(pqx);Tshirts tt=new Tshirts();tt.decorate(kk);tt.show();}
}

装饰模式中的巧妙之处

1、被装饰对象和装饰对象共享相同的接口或父类

    这样做的目的是是为了让它们可以互相替代。这种互相替代的能力是装饰模式的关键特点之一。通过共享相同的接口或父类,可以在不修改原始对象的情况下,动态地添加、删除或更改对象的行为。

    互相替换的好处在于灵活性和扩展性。当需要为对象添加额外的功能时,可以直接用装饰对象替换原始对象,而不需要修改原始对象的代码。可以将装饰器对象看作是被装饰对象的 “包装”。这样可以避免引入大量的条件语句或继承关系,使得代码更加清晰、可维护和可扩展。

    另外,互相替换还可以实现功能的组合和嵌套,比如上述给人装扮的例子,穿了靴子和裤子,就是把靴子和裤子的功能进行了组合。通过将装饰器对象嵌套在其他装饰器对象中,可以构建出复杂的功能组合,以满足不同的需求。

2、当调用装饰器类的装饰方法时,会先调用被装饰对象的同名方法

    例如上面装扮的例子中的show方法.装饰器类(服饰类)通过持有一个被装饰对象(component)的引用来添加额外的功能。当调用装饰器类的 show() 方法时,它会先调用被装饰对象的 show()方法,然后再执行自己的装饰逻辑(如下图所示)。这样的设计可以实现动态地扩展对象的功能,而无需改变原始对象的结构。通过嵌套和组合多个装饰器类灵活地添加、移除和组合各种功能,以满足不同的需求。
在这里插入图片描述

3、子类方法与父类方法共享相同的引用

    在具体装饰子类中都有super.show方法,这个是调取父类的show方法
在这里插入图片描述
    那么这样写的巧妙之处在哪呢,在这里是看不到2中说到的“”当调用装饰器类的 show() 方法时,它会先用被装饰对象的 show()方法,然后再执行自己的装饰逻辑。因为这个逻辑写到了父类中
在这里插入图片描述
    在这里有个疑问,在具体的子类中component为什么肯定是它的上一级被装扮对象呢,比如“穿了裤子的小蔡”接着要穿T恤。这就是因为每次调用装扮对象的decorate方法,传入的都是上一级被装扮对象(如下图所示),同时子类没有重写decorate方法,此时子类方法将与父类方法共享相同的引用,并且它们将具有相同的实现和行为,也就把component传给装备对象的属性,以供后面调用show方法时使用。
在这里插入图片描述

4、装饰模式与继承的对比

    装饰模式将功能添加到对象上的方式与继承不同。使用继承时,子类会继承父类的行为,而装饰模式可以动态地将额外的行为添加到对象上,而不需要继承。这使得装饰模式更加灵活,因为可以在运行时选择不同的行为组合。

    另外,装饰模式遵循了开放-关闭原则,即对扩展开放,对修改关闭。通过组合和委托的方式,可以动态地将功能添加到对象上,而不需要修改现有代码

总结

    装饰模式是一种灵活且可扩展的设计模式,它可以动态地给对象添加新的行为。使用装饰模式可以避免类爆炸问题,并能够在运行时选择不同的功能组合。它与继承相比更加灵活,符合单一职责原则,并遵循开放-关闭原则。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【深度学习基础】专业术语汇总(欠拟合和过拟合、泛化能力与迁移学习、调参和超参数、训练集、测试集和验证集)

📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨ 📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 📢:文章若有幸对你有帮助,可点赞 👍…

vue+asp.net Web api前后端分离项目发布部署

一、前后端项目介绍 1.前端项目是使用vue脚手架进行创建的。 脚手架版本:vue/cli 5.0.8 编译器版本:vs code 1.82.2 2.后端是一个asp.net Core Web API 项目 后端框架版本:.NET 6.0 编译器版本:vs 2022 二、发布部署步骤 第…

Java基础篇 | 多线程详解

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: Java从入门到精通 ✨特色专栏&#xf…

网络工程师进阶课:华为HCIP认证课程介绍

微思网络HCIP VIP试听课程:DHCP协议原理与配置https://www.bilibili.com/video/BV1cy4y1J7yg/?spm_id_from333.999.0.0 【赠送】IT技术视频教程,白拿不谢!思科、华为、红帽、数据库、云计算等等 https://xmws-it.blog.csdn.net/article/det…

如何选择微信管理系统?

如何选择微信管理系统? 1、不用下载安装软件,不越狱不刷机 2、不绑定手机或电脑,不对电脑或手机做限制,也不受电脑、手机关闭、关机影响 3、能更新迭代,不限制版本 4、使用安全登录,保障账号安全的 5、不用…

LangChain+LLM实战---ChatGPT的工作原理

一个词一个词的输出 ChatGPT能够自动生成类似于人类书写的文本,这是非常了不起和出乎意料的。但它是如何做到的?为什么会有效果呢?我的目的在于大致概述ChatGPT内部发生了什么,然后探讨它为什么能够很好地生成我们认为有意义的文…

GEE数据集——原住民土地(原住民土地地图)数据集

原住民土地(原住民土地地图) 土地承认是人们在日常生活中融入原住民存在和土地权利意识的一种方式。这通常在仪式、讲座或教育指南开始时进行。它可以是一种明确但有限的方式来认识殖民主义和第一民族的历史以及定居者殖民社会变革的需要。在这种情况下…

2023.11.03 homework

小学4年级数学 1 2 3 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 19…

2023.11.2事件纪念

然而造化又常常为庸人设计,以时间的流逝,来洗涤旧迹,仅以留下淡红的血色和微漠的悲哀。 回顾这次事件,最深的感触就是什么是团队的力量! 当我们看到希望快要成功的时候,大家洋溢出兴奋开心的表情,一起的欢声笑语;但看…

CleanMyMacX4.16破解版激活码

CleanMyMac X是一款颇受欢迎的专业清理软件,拥有十多项强大的功能,可以进行系统清理、清空废纸篓、清除大旧型文件、程序卸载、除恶意软件、系统维护等等,并且这款清理软件操作简易,非常好上手,特别适用于那些刚入手苹…

如何在 Unbuntu 下安装配置 Apache Zookeeper

简介 Zookeeper 是 apache 基金组织下的项目,项目用于简单的监控和管理一组服务,通过简单的接口就可以集中协调一组服务,如配置管理,信息同步,命名,分布式协调。 准备工作 Ubuntu 23.04 或者 20.04访问…

@Slf4j将日志记录到磁盘和数据库

文章目录 1、背景介绍2、存本地2.1、配置文件2.2、使用 3、存数据库3.1、配置文件改造3.2、过滤器编写3.3、表准备3.4、添加依赖3.5、测试 4、优化4.1、日志定期删除 1、背景介绍 现在我一个SpringBoot项目想记录日志,大概可以分为下面这几种: 用户操作…

NFC芯片MS520:非接触式读卡器 IC

MS520 是一款应用于 13.56MHz 非接触式通信中的高集成 度读写卡芯片。它集成了 13.56MHz 下所有类型的被动非接触 式通信方式和协议,支持 ISO14443A 的多层应用。 主要特点 ◼ 高度集成的解调和解码模拟电路 ◼ 采用少量外部器件,即可将输…

C++二分查找算法的应用:最小好进制

本文涉及的基础知识点 二分查找 题目 以字符串的形式给出 n , 以字符串的形式返回 n 的最小 好进制 。 如果 n 的 k(k>2) 进制数的所有数位全为1,则称 k(k>2) 是 n 的一个 好进制 。 示例 1: 输入:n “13” 输出:“3” …

https网站加载http资源问题

https网站加载http资源问题 前言:最近项目对接了一个第三方的平台、我们需要展示第三方平台返回来的图片资源、由于我们的服务器设置为了https、但是第三方平台返回的图片链接是 http 资源。所以就出现了图片无法加载出来的问题,在此记录一下问题的解决…

会声会影2024对比2023变化以及功能对比

全新会声会影2024版本现已登场,小伙伴们相信已经急不可待地想知道2024版到底有哪些新功能。对比2023版本,会声会影2024版本有没有功能的增强?事不宜迟,现在就让我们一起来看看会声会影2024对比2023的变化,包括功能对比…

VEX —— Quaternion|Euler Angle

目录 一,四元数相关概念 四元数 欧拉角 常用四元数相关函数 相互转换 二,案例 案例:沿面中心翻转 案例:路径导弹 一,四元数相关概念 四元数 在vex内四元数为((x,y&#xff0…

从物理磁盘到数据库 —— 存储IO链路访问图

原图来自:数据库IO链路访问图 – OracleBlog 由于很复杂,为了加深理解自己重新画了一次,另外参考其他文档补充了各部分的插图和介绍。 一、 存储服务器 1. 物理磁盘 外层的壳子称为硬盘笼 cage 2. chunklet Chunklet 是一个虚拟概念而不是实…

代理模式(静态代理、JDK代理、CGLIB代理)

简介 代理模式有三种不同的形式:静态代理、动态代理(JDK代理、接口代理)、CGLIB代理 目标:在不修改目标对象的前提下,对目标对象进行扩展。 静态代理 需要定义接口或父类对象,被代理对象和代理对象通过实…

SpringBoot条件注解底层原理

一、简介 相信大家在学springBoot自动装配的时候会遇到下面这些条件注解,当存在其指定的条件,才会把对应的bean注入到IOC容器中。本博客旨在揭开其神秘的面纱! 其实说白了,springBoot的这些注解只是针对普通spring框架中Conditi…