设计模式——单一职责模式之装饰模式

news2024/11/24 4:18:27

文章目录

  • 前言
  • 一、“单一职责” 模式
  • 二、Decorator 装饰模式
    • 1、动机
    • 2、模式定义
    • 3、伪代码示例
      • ①、第一种写法
      • ②、第二种写法
      • ③、第三种写法
    • 4、结构
  • 总结


前言


一、“单一职责” 模式

  • 在软件组件的设计中,如果责任划分的不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键是划清责任。
  • 典型模式
    • Decorator
    • Bridge

二、Decorator 装饰模式

1、动机

  • 在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
  • 如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?

2、模式定义

动态(组合) 地给一个对象增加一些额外的职责。就增加功能而言,Decorator 模式比生成子类(继承)更为灵活(消除重复代码 & 减少子类个数)。

3、伪代码示例

有这样一种设计场景,需要设计一个 IO 库流操作,针对流操作我们有很多的需求,比如说文件流、网络流以及内存流,我们也有需要对流进行加密,对流进行缓存等等各种各样的操作。

①、第一种写法

decorator1.cpp

//decorator1.cpp
//业务操作
class Stream{
publicvirtual char Read(int number)=0;
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0;
    
    virtual ~Stream(){}
};
 
//文件流主体类
class FileStream: public Stream{
public:
    virtual char Read(int number){
        //读文件流
    }
    virtual void Seek(int position){
        //定位文件流
    }
    virtual void Write(char data){
        //写文件流
    }
};
 
//网络流主体类
class NetworkStream :public Stream{
public:
    virtual char Read(int number){
        //读网络流
    }
    virtual void Seek(int position){
        //定位网络流
    }
    virtual void Write(char data){
        //写网络流
    }
};
 
//内存流主体类
class MemoryStream :public Stream{
public:
    virtual char Read(int number){
        //读内存流
    }
    virtual void Seek(int position){
        //定位内存流
    }
    virtual void Write(char data){
        //写内存流
    }
};
 
//扩展操作---加密操作
class CryptoFileStream :public FileStream{
public:
    virtual char Read(int number){
       
        //额外的加密操作...
        FileStream::Read(number);//读文件流  静态特质,确定了只能调FileStream 
    }
    virtual void Seek(int position){
        //额外的加密操作...
        FileStream::Seek(position);//定位文件流
        //额外的加密操作...
    }
    virtual void Write(byte data){
        //额外的加密操作...
        FileStream::Write(data);//写文件流
        //额外的加密操作...
    }
};
 
class CryptoNetworkStream : :public NetworkStream{
public:
    virtual char Read(int number){
        
        //额外的加密操作...
        NetworkStream::Read(number);//读网络流 静态特质,确定了只能调NetworkStream
    }
    virtual void Seek(int position){
        //额外的加密操作...
        NetworkStream::Seek(position);//定位网络流
        //额外的加密操作...
    }
    virtual void Write(byte data){
        //额外的加密操作...
        NetworkStream::Write(data);//写网络流
        //额外的加密操作...
    }
};
 
class CryptoMemoryStream : public MemoryStream{
public:
    virtual char Read(int number){
        
        //额外的加密操作...
        MemoryStream::Read(number);//读内存流
    }
    virtual void Seek(int position){
        //额外的加密操作...
        MemoryStream::Seek(position);//定位内存流
        //额外的加密操作...
    }
    virtual void Write(byte data){
        //额外的加密操作...
        MemoryStream::Write(data);//写内存流
        //额外的加密操作...
    }
};
 
class BufferedFileStream : public FileStream{
    //...
};
 
class BufferedNetworkStream : public NetworkStream{
    //...
};
 
class BufferedMemoryStream : public MemoryStream{
    //...
}
 
// 加密缓冲操作
class CryptoBufferedFileStream :public FileStream{
public:
    virtual char Read(int number){
        
        //额外的加密操作...
        //额外的缓冲操作...
        FileStream::Read(number);//读文件流
    }
    virtual void Seek(int position){
        //额外的加密操作...
        //额外的缓冲操作...
        FileStream::Seek(position);//定位文件流
        //额外的加密操作...
        //额外的缓冲操作...
    }
    virtual void Write(byte data){
        //额外的加密操作...
        //额外的缓冲操作...
        FileStream::Write(data);//写文件流
        //额外的加密操作...
        //额外的缓冲操作...
    }
};
 
 
void Process(){
    //编译时装配
    CryptoFileStream *fs1 = new CryptoFileStream();
    BufferedFileStream *fs2 = new BufferedFileStream();
    CryptoBufferedFileStream *fs3 = new CryptoBufferedFileStream();
}

涉及到的结构:
在这里插入图片描述
类的规模为:1 + n + n × m ! / 2
这份代码存在冗余,加密操作都是相同的,代码大量的重复。

②、第二种写法

重构

decorator2.cpp

//decorator2.cpp
//业务操作
class Stream{
 
publicvirtual char Read(int number)=0;
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0;
    
    virtual ~Stream(){}
};
 
//主体类
class FileStream: public Stream{
public:
    virtual char Read(int number){
        //读文件流
    }
    virtual void Seek(int position){
        //定位文件流
    }
    virtual void Write(char data){
        //写文件流
    }
};
 
class NetworkStream :public Stream{
public:
    virtual char Read(int number){
        //读网络流
    }
    virtual void Seek(int position){
        //定位网络流
    }
    virtual void Write(char data){
        //写网络流
    }
};
 
class MemoryStream :public Stream{
public:
    virtual char Read(int number){
        //读内存流
    }
    virtual void Seek(int position){
        //定位内存流
    }
    virtual void Write(char data){
        //写内存流
    }
};
 
//扩展操作,继承自Stream,是为了符合虚函数的接口规范
class CryptoStream: public Stream {
    
    Stream* stream;//...new FileStream() / new NetworkStream() /...
 
public:
    CryptoStream(Stream* stm) : stream(stm){
    
    }
    
    virtual char Read(int number){
       
        //额外的加密操作...
        stream->Read(number);//读文件流   动态特质,在运行时确定stream的具体类型
    }
    virtual void Seek(int position){
        //额外的加密操作...
        stream->Seek(position);//定位文件流
        //额外的加密操作...
    }
    virtual void Write(byte data){
        //额外的加密操作...
        stream->Write(data);//写文件流
        //额外的加密操作...
    }
};
 
class BufferedStream : public Stream{
    
    Stream* stream;//...
    
public:
    BufferedStream(Stream* stm) : stream(stm){
        
    }
    //...
};
 
void Process(){
    //运行时装配
    FileStream* s1=new FileStream();
    CryptoStream* s2=new CryptoStream(s1); 
    BufferedStream* s3=new BufferedStream(s1);
    BufferedStream* s4=new BufferedStream(s2);
}

修改后的优点:将“继承”改成“对象组合",使用多态,在运行时确定具体类型,“编译时装配"变成了"运行时装配”。

③、第三种写法

进一步优化:将相同字段提到一个新的基类 DecoratorStream 中:

//decorator3.cpp
//业务操作
class Stream{
publicvirtual char Read(int number)=0;
    virtual void Seek(int position)=0;
    virtual void Write(char data)=0;
    
    virtual ~Stream(){}
};
 
//主体类
class FileStream: public Stream{
public:
    virtual char Read(int number){
        //读文件流
    }
    virtual void Seek(int position){
        //定位文件流
    }
    virtual void Write(char data){
        //写文件流
    }
};
 
class NetworkStream :public Stream{
public:
    virtual char Read(int number){
        //读网络流
    }
    virtual void Seek(int position){
        //定位网络流
    }
    virtual void Write(char data){
        //写网络流
    } 
};
 
class MemoryStream :public Stream{
public:
    virtual char Read(int number){
        //读内存流
    }
    virtual void Seek(int position){
        //定位内存流
    }
    virtual void Write(char data){
        //写内存流
    }
};
 
//扩展操作
class DecoratorStream: public Stream{
protected:
    Stream* stream;//...
    
    DecoratorStream(Stream* stm) : stream(stm){
    
    }
};
 
class CryptoStream: public DecoratorStream {
public:
    CryptoStream(Stream* stm): DecoratorStream(stm) {
    
    }
 
    virtual char Read(int number){
        //额外的加密操作...
        stream->Read(number);//读文件流
    }
    
    virtual void Seek(int position){
        //额外的加密操作...
        stream->Seek(position);//定位文件流
        //额外的加密操作...
    }
    
    virtual void Write(byte data){
        //额外的加密操作...
        stream->Write(data);//写文件流
        //额外的加密操作...
    }
};
 
class BufferedStream : public DecoratorStream{
public:
    BufferedStream(Stream* stm):DecoratorStream(stm){
    
    }
    //...
};
 
void Process(){
    //运行时装配
    FileStream* s1=new FileStream();
    
    CryptoStream* s2=new CryptoStream(s1); // 加密操作
    
    BufferedStream* s3=new BufferedStream(s1); // 缓冲操作
    
    BufferedStream* s4=new BufferedStream(s2); // 加密缓冲操作
}

此时类关系:
在这里插入图片描述
类的规模:1 + n + 1 + m

4、结构

在这里插入图片描述

【注】:

  • Component -> Stream (稳定)
  • Decorator -> DecoratorStream (稳定
  • ConcreteComponent -> FileStream/NetworkStream/… (变化
  • ConcreteDecoratorX-> CryptoStream / BufferedStream (变化)

总结

  • 通过采用组合而非继承的手法Decorator 模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
  • Decorator 类在接口上表现为 is-a Component 的继承关系,即 Decorator 类继承了 Component 类所具有的接口。但在实现上又表现为 has-a Component 的组合关系,即 Decorator 类又使用了另外一个 Component 类。【注:DecoratorStream 类继承自 Stream,同时又有一个 Stream 类型的字段,一般这种既继承又组合的方式通常都是装饰模式。例子中的继承是为了完善接口的规范,组合是为了支持实现具体的类】
  • Decorator 模式的目的并非解决“多子类衍生的多继承”问题,Decorator 模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。

我的qq:2442391036,欢迎交流!


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

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

相关文章

深度指南:WhatsApp广播vs WhatsApp群组(二)

如何为创建WhatsApp群组? 建立 WhatsApp 群组,请按下列步骤操作。 1. 导航到您的WhatsApp或WhatsApp for Business(布局或界面相同),然后点击“新建组”。 2.现在,您可以将参与者添加到您的WhatsApp群组…

华为OD机试真题 Java 实现【静态代码扫描服务】【2023Q1 100分】

一、题目描述 静态扫描快速识别源代码的缺陷,静态扫描的结果以扫描报告作为输出: 文件扫描的成本和文件大小相关,如果文件大小为N,则扫描成本为N个金币;扫描报告的缓存成本和文件大小无关,每缓存一个报告…

算法修炼之练气篇——练气十层

博主:命运之光 专栏:算法修炼之练气篇 前言:每天练习五道题,炼气篇大概会练习200道题左右,题目有C语言网上的题,也有洛谷上面的题,题目简单适合新手入门。(代码都是命运之光自己写的…

华为网络篇 静态路由与BFD联动-21

实验难度2实验复杂度2 目录 一、实验原理 二、实验拓扑 三、实验步骤 四、实验过程 总结 一、实验原理 BFD(Bidirectional Forwarding Detection,双向转发检测)是一种实验网络可靠性的机制,它可用于快速检测网络中的链路状况…

常见自动化测试工具及框架有哪些?怎么选?

目录 一、自动化测试简介 1、什么是自动化测试 2、它可以做自动化测试么 二、工具篇 1、UFT(QTP) 2、Selenium 3、Appium 4、工具对比 三、框架篇 1、TestNG 2、unittest 3、pytest 4、Robot Framework 5、框架对比 四、如何选择 总结 一…

ES6知识点

目录 1、let、cons、var的区别 2、const对象的属性可以修改吗 3、如果new一个箭头函数会怎么样 4、箭头函数和普通函数的区别: 5、扩展运算符的作用及使用场景 1、let、cons、var的区别 1️⃣:块级作用域:块作用域由{}包括,le…

Flink 常用API(1)—— 源算子

目录 执行环境(Execution Environment) 创建执行环境 执行模式配置 触发程序执行 源算子(Source) 从集合中读取数据 从文件中读取数据 从Socket读取数据 从Kafka读取数据*** 自定义 Source(数据源&#xff0…

【结构与算法】—— 游戏概率常用算法整理 | 游戏中的常见概率设计分析

📢博客主页:肩匣与橘📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!📢本文由肩匣与橘编写,首发于CSDN🙉📢生活依旧是美好而又温柔的,你也是✨ …

Smartbi X 广州轻工集团,打造集团价值创造型总部

广州轻工工贸集团有限公司(简称“广州轻工集团”)是广州市第一家工贸合一的大型企业集团公司,最早起源于1950年9月成立的广州市合作事业管理局,逐渐演化形成广州市轻工业局。1995年10月,广州市轻工业局成建制改建为经济…

【C++】搜索二叉树

文章目录 📕 概念📕 搜索二叉树的实现框架插入节点查找节点★ 删除节点 ★ 📕 源代码 📕 概念 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 若它的左子树不为空&…

5。STM32裸机开发(2)

嵌入式软件开发学习过程记录,本部分结合本人的学习经验撰写,系统描述各类基础例程的程序撰写逻辑。构建裸机开发的思维,为RTOS做铺垫(本部分基于库函数版实现),如有不足之处,敬请批评指正。 &a…

全景描绘云原生技术图谱,首个《云原生应用引擎技术发展白皮书》发布

5月12日,由神州数码主办、北京经开区国家信创园、中关村云计算产业联盟协办的2023通明湖论坛-云原生分论坛在京召开。论坛期间,神州数码联合北京通明湖信息技术应用创新中心、中国信通院和通明智云正式发布了《云原生应用引擎技术发展白皮书》&#xff0…

干货 | 心理学人电脑选购指南来了!

Hello,大家好! 这里是壹脑云科研圈,我是喵君姐姐~ 当我们在选择电脑时经常会无从下手,不知道该如何才能选择一款既能满足我们的科研需要又具有良好性价比的电脑。 本期我们邀请到了唐仙和梦马来为我们详细解答心理学…

我的Makefile模板

OBJxxxxCFLAGS -g -Wall${OBJ}:${OBJ}.o main.o%*.o:%*.c.PHONY:clean clean:${RM} *.o ${OBJ} core xxxx → xxxx.c xxxx.h main.c 比如: 包含了:

PhotoScan拼接无人机航拍RGB照片

目录 背景 拼接步骤 1.新建并保存项目 2.添加照片 3.对齐照片 4.添加标记(Markers) 5.添加地面控制点 6.建立批处理任务 7.使用批处理文件进行批处理 8.导出DEM 9.导出DOM 背景 本文介绍使用地面控制点(GCPs)拼接​​…

Java面试知识点(全)- Java面试基础部分一

Java基础 语法基础 面向对象 封装 利用抽象数据类型将数据和基于数据的操作封装在一起,使其构成一个不可分割的独立实体。数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,只保留一些对外接口使之与外部发生联系。用户无需知道对…

如何解决ChatGPT网络错误的问题,让AI对话更丝滑~

前言 在当今人工智能技术的飞速发展中,ChatGPT 作为一款大型语言模型备受瞩目。近期,其在各大社交媒体平台上的表现更是引来了一片关注之声。无论是与用户进行有趣的对话,还是帮助人们解决实际问题,ChatGPT 展现出了其强大的自然…

谷歌慌了!想发论文得审批,优先开发产品,让OpenAI没得看

来源 | 机器之心 ID | almosthuman2014 众所周知,谷歌就像人工智能领域的「黄埔军校」,自深度学习兴起后培养出了整整一代机器学习研究人员和工程师。很长一段时间里,谷歌就是领先 AI 技术的代名词。 人们已经习惯跟随谷歌的脚步&#xff0c…

操作符(算术操作符、移位操作符、位操作符、赋值操作符、单目操作符、关系操作符、逻辑操作符)

目录 算术操作符 移位操作符 移位规则 位操作符 交换两个整形变量的写法 赋值操作符 单目操作符 sizeof和数组的纠缠 和--运算符 多组输入的方案 关系操作符 逻辑操作符 算术操作符 -- 加法操作符():用于将两个值相加。 -- 减法操…

算法修炼之练气篇——练气八层

博主:命运之光 专栏:算法修炼之练气篇 前言:每天练习五道题,炼气篇大概会练习200道题左右,题目有C语言网上的题,也有洛谷上面的题,题目简单适合新手入门。(代码都是命运之光自己写的…