C++设计模式:装饰器模式(四)

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

  • 在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。

  • 如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响降到最低?

  • 装饰器模式有一个很独特的地方:不仅继承接口(is a)、还组合接口(has a);继承接口是为了重写接口实现接口的方法,组合是为了动态装入接口的其他子类,这样就可以轻松的实现功能拓展,但又不违背单一职责原则!

2、案例分析
  • C++中的文件流对象并没有Java中的那么丰富,在Java语言中存在各种各样的流对象,基类有InputStream/OutputStream、Reader/Writer等等
  • 同时派生出各种IO流类,通过不同的功能、作用、辅助分类可以分很多种:文件流、对象流、缓冲流、缓冲文件流、加密缓冲流、等等
  • 如果代码都单纯的靠继承来实现Java庞大的IO流类库,工作量是非常大的。
  • 导致工作量巨大的原因:单纯通过继承来实现,存在大量的CV代码

在这里插入图片描述

2.1、基础实现
  • 普通的实现方式就是一直继承,代码无线重复的去设计实现
  • 里氏替换原则:当继承基类或者父类的代码大量被重写时,继承失去其原本的意义,换句话说可以不需要继承。
  • 这样设计会导致继承失去其原本的意义,因为大量的代码都被重写(违背里氏替换原则)
class Stream{
    virtual char Read(int n) = 0;
    virtual void Seek(int n) = 0;
    virtual void Write(char data) = 0;
    virtual ~Stream(){}
};
/*
 * FileStream、NetWorkStream、MemoryStream、ObjectStream...
 */
class FileStream: public Stream{
public:
    virtual char Read(int n){
        // 读文件流
    }
    virtual void Seek(int n){
        // 定位文件流
    }
    virtual void Write(char data){
        // 写文件流
    }
};

class NetWorkStream: public Stream{
public:
    virtual char Read(int n){
        // 读网络流
    }
    virtual void Seek(int n){
        // 定位网络流
    }
    virtual void Write(char data){
        // 写网络流
    }
};

// CryptoFileStream、CryptoNetWorkStream、CryptoMemoryStream、CryptoObjectStream...
class CryptoFileStream: public FileStream{
public:
    virtual char Read(int n){
        // 加密
        FileStream::Read(n);
        // 加密
    }
    virtual void Seek(int n){
        // 加密
        FileStream::Seek(n);
        // 加密
    }
    virtual void Write(char data){
        // 加密
        FileStream::Write(n);
        // 加密
    }
};
class CryptoNetWorkStream: public NetWorkStream{
public:
    virtual char Read(int n){

    }
    virtual void Seek(int n){

    }
    virtual void Write(char data){

    }
};

// BufferedFileStream、BufferedNetWorkStream、BufferedMemoryStream、BufferedObjectStream...
class BufferedFileStream: public FileStream{
    // ...
};
class BufferedNetWorkStream: public NetWorkStream{
    // ...
};

// CryptoBufferedFileStream、CryptoBufferedNetWorkStream、CryptoBufferedMemoryStream、CryptoBufferedObjectStream...
class CryptoBufferedFileStream: public BufferedFileStream{
    virtual char Read(int n){
        // 加密
        // 缓存
        BufferedFileStream::Read(n);
    }
    virtual void Seek(int n){
        //...
    }
    virtual void Write(char data){
        //...
    }
};
  • 而这样编写到了后期持续的扩展,类的数量指数增长,代码量急剧上升

在这里插入图片描述

2.2、普通装饰器
  • 为了消除这些冗余的代码,可以优先考虑使用组合的形式来取代靠继承实现的这些IO流类
  • 基于上述代码,只要灵活使用多态和组合就可以省略掉很多冗余的代码
    • CryptoStream类中组合一个Stream抽象类的指针,当构造CryptoStream类时可以通过传入FileStream、NetWorkStream对象来完成动态绑定,然后CryptoFileStream、CryptoNetWorkStream…其内部所有的方法都一样(因为只是基于不同的流类套了一层加密,而具体哪个流类通过Stream抽象类接口来动态绑定),因此可以代码去重。
  • 因此这样就可以实现去重
class Stream{
    virtual char Read(int n) = 0;
    virtual void Seek(int n) = 0;
    virtual void Write(char data) = 0;
    virtual ~Stream(){}
};
/*
 * FileStream、NetWorkStream、MemoryStream、ObjectStream...
 */
class FileStream: public Stream{
public:
    virtual char Read(int n){
        // 读文件流
    }
    virtual void Seek(int n){
        // 定位文件流
    }
    virtual void Write(char data){
        // 写文件流
    }
};

class NetWorkStream: public Stream{
public:
    virtual char Read(int n){
        // 读网络流
    }
    virtual void Seek(int n){
        // 定位网络流
    }
    virtual void Write(char data){
        // 写网络流
    }
};

// CryptoFileStream、CryptoNetWorkStream、CryptoMemoryStream、CryptoObjectStream...
class CryptoStream: public Stream{
private:
    Stream *stream;             // new FileStream()、new NetWorkStream().....
public:
    CryptoStream(Stream *_stream): stream(_stream){
        
    }
    virtual char Read(int n){
        // 加密
        stream->Read(n);
        // 加密
    }
    virtual void Seek(int n){
        // 加密
        stream->Read(n);
        // 加密
    }
    virtual void Write(char data){
        // 加密
        stream->Read(n);
        // 加密
    }
};

// BufferedFileStream、BufferedNetWorkStream、BufferedMemoryStream、BufferedObjectStream...
class BufferedStream: public Stream{
    Stream *stream;
    // ...
};

// CryptoBufferedFileStream、CryptoBufferedNetWorkStream、CryptoBufferedMemoryStream、CryptoBufferedObjectStream...
class CryptoBufferedStream: public Stream{
    Stream *stream;             //....
    virtual char Read(int n){
        // 加密
        // 缓存
    }
    virtual void Seek(int n){
        //...
    }
    virtual void Write(char data){
        //...
    }
};

void process()
{
    // 运行时装配
    FileStream fileStream = new FileStream();
    CryptoStream cryptoStream1 = new CryptoStream(new FileStream());
    CryptoStream cryptoStream2 = new CryptoStream(new NetWorkStream());
    ....    
    CryptoBufferedStream cryptoBufferedStream = new CryptoBufferedStream(new FileStream());
}
2.3、抽象装饰器
  • 抽象装饰器就是在实现类和基类之间套了一个装饰器类
    • 装饰器类负责继承基类,通过多态的动态绑定来运行时确定加载某个类
    • 实现类通过继承装饰器类,调用装饰器类中的对象完成操作
class Stream{
    virtual char Read(int n) = 0;
    virtual void Seek(int n) = 0;
    virtual void Write(char data) = 0;
    virtual ~Stream(){}
};
/*
 * FileStream、NetWorkStream、MemoryStream、ObjectStream...
 */
class FileStream: public Stream{
public:
    virtual char Read(int n){
        // 读文件流
    }
    virtual void Seek(int n){
        // 定位文件流
    }
    virtual void Write(char data){
        // 写文件流
    }
};

class NetWorkStream: public Stream{
public:
    virtual char Read(int n){
        // 读网络流
    }
    virtual void Seek(int n){
        // 定位网络流
    }
    virtual void Write(char data){
        // 写网络流
    }
};

class DecoratorStream: Stream{
protected:
    Stream *stream;
    DecoratorStream(Stream *_stream): stream(_stream){

    }
};

// CryptoFileStream、CryptoNetWorkStream、CryptoMemoryStream、CryptoObjectStream...
class CryptoStream: public DecoratorStream{
public:
    CryptoStream(Stream *_stream): DecoratorStream(_stream){

    }
    virtual char Read(int n){
        // 加密
        stream->Read(n);
        // 加密
    }
    virtual void Seek(int n){
        // 加密
        stream->Read(n);
        // 加密
    }
    virtual void Write(char data){
        // 加密
        stream->Read(n);
        // 加密
    }
};

// BufferedFileStream、BufferedNetWorkStream、BufferedMemoryStream、BufferedObjectStream...
class BufferedStream: public DecoratorStream{
    // ...
};

// CryptoBufferedFileStream、CryptoBufferedNetWorkStream、CryptoBufferedMemoryStream、CryptoBufferedObjectStream...
class CryptoBufferedStream: public DecoratorStream{
    virtual char Read(int n){
        // 加密
        // 缓存
    }
    virtual void Seek(int n){
        //...
    }
    virtual void Write(char data){
        //...
    }
};

void process()
{
    // 运行时装配
    FileStream fileStream = new FileStream();
    CryptoStream cryptoStream1 = new CryptoStream(new FileStream());
    CryptoStream cryptoStream2 = new CryptoStream(new NetWorkStream());
    ....
    CryptoBufferedStream cryptoBufferedStream = new CryptoBufferedStream(new FileStream());
}

在这里插入图片描述

4、总结
  • 通过采用组合而非继承的手法,Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
  • Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所有具有的接口;但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component实现类。
  • Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”—是为“装饰”的含义
    在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ARM体系结构

阅读引言: arm架构处理器的基本知识已在上一篇文章中描述过了, 本文将会从arm的存储模型、异常机制、工作模式、指令集、流水线、arm的寄存器组织方面去描述。 目录 一、ARM处理器概论 1.指令集概述 2.指令分类 3.编译流程 二、ARM的存储模型 1.AR…

电商技术揭秘七:搜索引擎中的SEO关键词策略与内容优化技术

文章目录 引言一、关键词策略1.1 关键词研究与选择1. 确定目标受众2. 使用关键词研究工具3. 分析搜索量和竞争程度4. 考虑长尾关键词5. 关键词的商业意图6. 创建关键词列表7. 持续监控和调整 1.2 关键词布局与密度1. 关键词自然分布2. 标题标签的使用3. 首次段落的重要性4. 关键…

C# 分布式自增ID算法snowflake(雪花算法)

文章目录 1. 概述2. 结构3. 代码3.1 IdWorker.cs3.2 IdWorkerTest.cs (测试) 1. 概述 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长&#xff0c…

pyside6怎么使用Qt Designer设计自定义组件

第一步,新建一个自定义组件的python文件 from PySide6.QtWidgets import QPlainTextEdit from PySide6.QtCore import Signal,Qtclass CustomPlainTextEdit(QPlainTextEdit):enterPressed Signal(str)def __init__(self, parentNone):super().__init__(parent)def…

Vue+node.js蔬菜水果农产品网上商城管理系统

用户能够及时掌握最新的数据,并能有效地提升用户的服务水平。本制度的优势在于: (1)该系统将蔬菜信息及时地提供给使用者。使用者可以在任何时候登陆该网站查询相关资讯,所有资讯均真实可信,并严肃处理各项…

161 Linux C++ 通讯架构实战15,线程池代码分析

线程池应该使用的地方 和 epoll 技术结合 线程池代码处理数据的地方。 线程池分析: 线程池代码1 threadpool_create //Tencent8888 start threadpool_create函数的目的初始化线程池,对应的struct是 threadpool_t /* 1.先malloc整个线程池的大小 2.这里…

牛客 2024春招冲刺题单 ONT98 牛牛猜节点【中等 斐波那契数列 Java,Go,PHP】

题目 题目链接: https://www.nowcoder.com/practice/6a3dfb5be4544381908529dc678ca6dd 思路 斐波那契数列参考答案Java import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规…

Unity与CocosCreator对比学习二

一、锚点与适配 1.在Creator中 适配通过锚点、位置和Widget达到适配目的;锚点是节点在其父节点坐标系中坐标对其点,其x,y范围在[0, 1]之间; 锚点为(0, 0)时在节点自身的左下角,节点坐标指其左下角在父节点中的坐标;锚…

贪心算法|53.最大子序和

力扣题目链接 class Solution { public:int maxSubArray(vector<int>& nums) {int result INT32_MIN;int count 0;for (int i 0; i < nums.size(); i) {count nums[i];if (count > result) {result count;}if (count < 0) count 0;}return result;} …

[AIGC] Spring Filter 过滤器详解

什么是Spring Filter 在Web应用中&#xff0c;Filter&#xff08;过滤器&#xff09;是在Java Servlet规范中的一种组件&#xff0c;它的主要目的是对HTTP请求或者响应进行处理。Spring Filter则是Spring框架对Java原生Filter的封装版本和扩展。 简单来说&#xff0c;Spring …

物联网实战--驱动篇之(二)Modbus协议

目录 一、modbus简介 二、功能码01、02 三、modbus解析 四、功能码03、04 五、功能码05 六、功能码06 七、功能码16 一、modbus简介 我们在网上查阅modbus的资料发现很多很杂&#xff0c;modbus-RTU ASCII TCP等等&#xff0c;还有跟PLC结合的&#xff0c;地址还分1开…

如果在 Ubuntu 系统中两个设备出现两个相同的端口号解决方案

问题描述&#xff1a; 自己的移动机器人在为激光雷达和IMU配置动态指定的端口时&#xff0c;发现激光雷达和深度相机配置的 idVendor 和 idProduct 相同&#xff0c;但是两个设备都具有不同的ttyUSB号&#xff0c;如下图所示 idVendor&#xff1a;代表着设备的生产商ID,由USB设…

并查集学习(836. 合并集合 + 837. 连通块中点的数量)

//得先加集合个数再合并&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 核心代码&#xff1a; int find(int x){//返回父节点if(x ! p[x]) {p[x] find(p[x]);//路径压缩 } //孩子不等于爸爸&#xff0c;就…

springboot+vue学生宿舍物品存放系统tnozt

需求包括&#xff1a; 三个角色&#xff1a;学生&#xff0c;公寓管理员&#xff08;宿舍管理人员&#xff09;&#xff0c;系统管理员。 本系统基于java语言&#xff0c;结合数据库技术&#xff0c;通过面向对象的设计方法&#xff0c;实现学生信息管理、公寓信息管理、物品存…

STM32外部中断编程相关

一&#xff0c;Nested vectored interrupt controller NVIC即嵌套向量中断控制器&#xff0c;它是内核的器件&#xff0c;M3内核都是支持256个中断&#xff0c;其中包含了16系统中断和240个外部中断&#xff0c;并且具有256级的可编程中断设置。芯片厂商一般不会把内核的这些资…

假期别闲着:REST API实战演练之创建Rest API

1、创建实体类&#xff0c;模拟实体对象 创建一个类&#xff0c;模拟数据数据库来存储数据&#xff0c;这个类就叫Person。 其代码如下&#xff1a; package com.restful;public class Person {private String name;private String about;private int birthYear;public Perso…

MacOS下载和安装HomeBrew的详细教程

在MacOS上安装Homebrew的详细教程如下&#xff1a;&#xff08;参考官网&#xff1a;macOS&#xff08;或 Linux&#xff09;缺失的软件包的管理器 — Homebrew&#xff09; 步骤1&#xff1a;检查系统要求 确保你的MacOS版本至少为macOS Monterey (12) (or higher) 或更高版本…

C语言程序编译全流程,从源代码到二进制

源程序 对于一个最简单的程序&#xff1a; int main(){int a 1;int b 2;int c a b;return 0; }预处理 处理源代码中的宏指令&#xff0c;例如#include等 clang -E test.c处理结果&#xff1a; # 1 "test.c" # 1 "<built-in>" 1 # 1 "&…

【力扣】94. 二叉树的中序遍历、144. 二叉树的前序遍历、145. 二叉树的后序遍历

先序遍历&#xff1a;根-左-右中序遍历&#xff1a;左-根-右后序遍历&#xff1a;左-右-根 94. 二叉树的中序遍历 题目描述 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3…

【ControlNet v3版本论文阅读】

网络部分最好有LDM或者Stable Diffusion的基础&#xff0c;有基础的话会看的很轻松 Abstract 1.提出了一种网络结构支持额外输入条件控制大型预训练的扩散模型。利用预训练模型学习一组不同的条件控制。 2.ControlNet对于小型&#xff08;<50k&#xff09;或大型&#xff…