考虑要设计一个 Messager 模块, 这个模块要实现如下功能
登录
发送消息
播放声音(登录或者播放的时候播放声音)
画图(登录的时候显示的图片)
那么最直观的设计如下
- class Messager {
- public:
- virtual void Login(string userName, string password) = 0;
- virtual void SendMessage(string message) = 0;
- virtual void PlaySound() = 0;
- virtual void DrawShape() = 0;
- virtual ~Message() {}
- };
然后呢, 我们这个 Messager 要实现跨平台的功能, 要在 PC 平台和手机平台都能适用. 然而对于 PC 和 Mobile 来说, 唯一有区别的地方只有播放声音和画图这些与平台相关的函数不同, 而登录, 发送消息这些业务功能是相同的, 所以你可能会设计出如下的两个类:
- class PCMessagerBase : public Messager {
- virtual void PlaySound() { /*******/ }
- virtual void DrawShape() { /*******/ }
- };
- class MobileMessagerBase : public Messager {
- virtual void PlaySound() { /*******/ }
- virtual void DrawShape() { /*******/ }
- };
接下来继续考虑在同一个平台要推出两个不同版本的 Messager, 一个是精简版, 一个是完美版. 比如说精简版的在登录的时候不会播放声音和显示图片, 而完美版的则支持这两个功能. 其设计可能如下:
- class PCMessagerLite : public PCMessagerBase {
- virtual void Login(string userName, string password) { /******/ }
- virtual void SendMessage() { /*******/ }
- };
- class PCMessagerPerfect : public PCMessagerBase {
- virtual void Login(string userName, string password) {
- PCMessageBase::PlaySound();
- PCMessageBase::DrawShape();
- }
- virtual void SendMessage() {
- PCMessageBase::PlaySound();
- PCMessageBase::DrawShape();
- }
- }
- class MobileMessagerLite : public MobileMessagerBase {
- virtual void Login(string userName, string password) { /******/ }
- virtual void SendMessage() { /*******/ }
- };
- class MobileMessagerPerfect : public MobileMessagerBase {
- virtual void Login(string userName, string password) {
- MobileMessageBase::PlaySound();
- MobileMessageBase::DrawShape();
- }
- virtual void SendMessage() {
- MobileMessageBase::PlaySound();
- MobileMessageBase::DrawShape();
- }
- }
那么到目前为止, 类的结构图如下
这么设计有什么缺点呢? 缺点是代码重复性太大了. 这种重复是一种结构性的重复. 你仔细观察观察 MobileMessagerPerfect 和 PCMessagerPeferct 这两个类中 Login 和 SendMessage 的实现, 会发现他们的流程是一模一样的, 都是播放声音, 显示登录画面, 登录. 不同的只是因为平台导致的播放声音和登录画面的实现不同.
那么有什么改进的方法呢? 你可以发现在 Perfect 类的 Login 中的 PlaySound 和 DrawShape 可以追溯到同一个虚函数调用. 如果你知道装饰者模式的话, 那么你很快的可以想到改进的方法是将 PCMobildBase 作为一个类的字段去组合它, 而不是去继承它
- class PCMessagePerfect {
- private:
- PCMessageBase* message;
- }
通过 message->PlaySound()来调用. 同理 MobildMessagePerfect 中设计一个 MobileMessageBase * 字段. 但是如果都申明为一个具体平台的 Base 字段的话, 程序又写的太死了, 所以你又发现可以将这个类的字段申明为 Message*, 然后在将来要构造 Perfect 对象的时候传进去一个具体的 Base 对象. 但是现在问题又来了, 现在 * Base 对象是个抽象类, 是无法 new 出对象的.(Login, SendMessage 等方法没有实现)
那么问题的根源是什么? 原因在是 Message 类中, 你把业务逻辑 (Login, SendMessage) 和平台实现 (PlaySound, DrawShap) 混合到一起了, 这样做是很不合适的, 应该将他们拆分开.
最终的类设计应该如下
- class Message { Login, SendMessage } // 业务功能部分
- class MessageImp { PlaySound, DrawShap } // 平台实现部分
- class PCMessageBase : MessageImp {} // 具体的平台实现
- class PCMessagePerfect : Message { // 继承 Message 的业务功能部分
- MessageImp* messageImp; // 组合得到 Message 的平台功能部分
- }
来源: https://www.cnblogs.com/David-Lin/p/10807168.html