IE盒子

搜索
查看: 109|回复: 1

C++ · 设计模式 (单一职责=Decorator/ Bridge)

[复制链接]

3

主题

9

帖子

17

积分

新手上路

Rank: 1

积分
17
发表于 2022-11-27 16:16:44 | 显示全部楼层 |阅读模式
"单一职责"模式:
在软件组件的设计中,如果责任划分不清晰,使用继承得到的结果往往是随着需求的变化,子类急剧膨胀,同时充斥着重复代码,这时候的关键点就是划分清晰责任。
典型模式:

  • Decorator
  • Bridge
Decorator (装饰模式)

我们设计一个流处理类,有两个纬度去衍生流处理。一个是流属性(文件流、网络流、内存流等等),另外一个纬度是流处理的纬度,比如加密、缓冲等等。
常规设计,会如下图一样,分三层依次创建子类来继承父类。当每层元素增加,整体类数量会阶乘放大。
(1+N+N*M!/2 的类个数)


从代码来看,如何重构。



① 首先把子类的继承改为委托,即子类包含父类的指针。去除掉 Read等成员函数的重复编码
② 多个子类静态装配,重构成运行时装配,实现多态。
③ CryptoStream 即有委托也有继承。继承了父类,overwrite父类的虚函数,为了保证虚接口的规范;委托了父类指针,为了在运行实现多态的支持。

但看着还是很别扭。多个子类有相同data,Stream* stream, 需要提到父类,或者增加一层 Decorator(装饰者)。又因为, FileStream显然不需要再委托 Stream指针了,所以不能再提前到父类,而应该增加Decorator。


再来看下整体代码结构。

Stream 分为File/Network/Memory,这三个类始终保持不变。为什么,因为它们是固定属性子类,可以单独创建使用,没有进一步重构的必要性;而加密、缓冲属于扩展功能,增加一个纬度的特性,就有了重构的必要性。
重构后,类的数量规模减小到  1+N+1+M。


总结

分析两种调用方式的不同,


模式+结构


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

  • 结构



  • 稳定性划线



  • 示例伪代码
// [decorator]

class Stream {
public:
        virtual 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); //读文件流
        }
        ...
};

// 扩展操作
class BufferedStream : public DecoratorStream {

public:
        BufferedStream(Stream* stm) : DecoratorStream(stm) {}

        virtual char Read(int number) {
                //额外的缓冲操作...
                stream->Read(number); //读文件流
        }
        ...
};

void Process() {
        // 运行时装配
         FileStream* s1 = new FileStream();
         CryptoStream* s11 = new CryptoStream(s1);
         BufferedStream* s12 = new BufferedStream(s1);
         BufferedStream* s13 = new BufferedStream(s11);  // CryptoBufferedFileStream

}
有兴趣可以再回顾
结合本章节,理解下 委托+继承 解决问题的思路。

Bridge 模式

模型示例

在某些类型的固有实现逻辑,使得它们具有多个变化纬度,比如发消息的系统,Messager有Lite和Perfect版本,同时基础功能接口又因为PC和Mobile类型不同,而各异。
用直观类继承的方式实现,代码是这样的。


Mobile版本要再照搬一遍PC的类实现。那么1+M*N的类设计产生了。
重构版本



再直白一点讲,PC、Mobile的成员函数的指令代码,被Messager类通过指针引用,提供给不同的Lite或Perfect版本调用。
模式定义

将抽象部分(业务功能)与实现部分(平台实现)分离,使得它们都可以独立地变化。
结构



Bridge(桥),将多个纬度的任务组合了起来。
伪代码

// bridge

class Messager {
protected:
        MessagerImp* messagerImp;
public:
        Messager(MessagerImp* msgImp) : messagerImp(msgImp) {}

        // 交互型接口
        virtual void Login(string username, string password) = 0;
        virtual void SendMessage(string message) = 0;
        virtual void SendPicture(Image image) = 0;

        virtual ~Message(){}
};

class MessagerImp {
public:
        // 底层接口
        virtual void PlaySound() = 0;
        virtual void DrawShape() = 0;
        virtual void WriteText() = 0;
        virtual void Connect() = 0;

        virtual ~MessagerImp(){}
};

class PCMessagerImp : public MessagerImp {
public:
        virtual void PlaySound() {
                // ....
        }
        virtual void DrawShape() {
                // ....
        }
        virtual void WriteText() {
                // ....
        }
        virtual void Connect() {
                // ....
        }
};

class MobileMessageImp : public MessagerImp {
public:
        virtual void PlaySound() {
                // ....
        }
        virtual void DrawShape() {
                // ....
        }
        virtual void WriteText() {
                // ....
        }
        virtual void Connect() {
                // ....
        }
};

class MessagerLite : public Messager {

public:
        virtual void Login(string username, string password) {
                messagerImp->Connect();
                // ...
        }
        virtual void SendMessage(string message) {
                messagerImp->WriteText();
                // ...
        }
        virtual void SendPicture(Image image) {
                messagerImp->DrawShape();
                // ...
        }
};

class MessagerPerfect : public Messager {
       
public:
        virtual void Login(string username, string password) {
                messagerImp->PlaySound();
                // ***
                messagerImp->Connect();
                // ...
        }
        virtual void SendMessage(string message) {
                messagerImp->PlaySound();
                // ***
                messagerImp->WriteText();
                // ...
        }
        virtual void SendPicture(Image image) {
                messagerImp->PlaySound();
                // ***
                messagerImp->DrawShape();
                // ...
        }
};

void Process() {
        MessagerImp* mImp = new PCMessagerImp();
        Messager* m = new Messager(mImp);
        MessagerLite* mPcLite = new MessagerLite(m);
}




参考资料
回复

使用道具 举报

4

主题

9

帖子

18

积分

新手上路

Rank: 1

积分
18
发表于 2025-3-15 03:55:33 | 显示全部楼层
秀起来~
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表