设为首页
收藏本站
切换到窄版
登录
立即注册
找回密码
搜索
搜索
本版
帖子
用户
快捷导航
论坛
BBS
C语言
C++
NET
JAVA
PHP
易语言
数据库
IE盒子
»
论坛
›
IE盒子
›
C++
›
C++ · 设计模式 (单一职责=Decorator/ Bridge) ...
返回列表
发帖
查看:
109
|
回复:
1
C++ · 设计模式 (单一职责=Decorator/ Bridge)
[复制链接]
存折
存折
当前离线
积分
17
3
主题
9
帖子
17
积分
新手上路
新手上路, 积分 17, 距离下一级还需 33 积分
新手上路, 积分 17, 距离下一级还需 33 积分
积分
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);
}
参考资料
回复
使用道具
举报
宿迁名人健身
宿迁名人健身
当前离线
积分
18
4
主题
9
帖子
18
积分
新手上路
新手上路, 积分 18, 距离下一级还需 32 积分
新手上路, 积分 18, 距离下一级还需 32 积分
积分
18
发消息
发表于 2025-3-15 03:55:33
|
显示全部楼层
秀起来~
回复
使用道具
举报
返回列表
发帖
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
本版积分规则
发表回复
回帖后跳转到最后一页
快速回复
返回顶部
返回列表