C++设计模式

1. 概述

  • 什么叫设计模式——避免重复造轮子
  • 《Design Patterns: Elements of Reusable Object-Oriented Software》——参考教材
  • 现代软件设计固有的复杂性与客户需求不断变化的矛盾。如何解决这些复杂性呢?
    • 结构化分而治之
    • 抽象
  • 什么是好的软件设计?软件设计的金科玉律:复用!!!
  • 分而治之是很难复用的。
  • 抽象使得程序员处理需求有一个通用的技术,抵御变化。

2. 八大面向对象设计原则

2.1 依赖倒置原则-DIP

  • 依赖倒置原则,英文缩写DIP,全称Dependence Inversion Principle。
  • 定义
    • 高层模块不应该依赖低层模块,两者都应该依赖其抽象;
    • 抽象不应该依赖细节,细节应该依赖抽象。
  • 下图以绘图程序示例,绘图程序在很多教材都有出现,设计抽象类的经典例子,这里不讲其代码,大概思路大家懂得都懂。
  • 这里的底层模块指的是具体实现,高层模块指的是框架。
  • 相比传统的软件设计架构,比如我们常说的经典的三层架构,UI层(User Interface用户界面层)依赖于BLL层(Business Logic Layer业务逻辑层),BLL层依赖于DAL层(Data Access Layer 数据访问层)。
  • 由于每一层都是依赖于下层的实现,这样当某一层的结构发生变化时,它的上层就不得不也要发生改变,比如我们DAL里面逻辑发生了变化,可能会导致BLL和UI层都随之发生变化,这种架构是非常荒谬的!
  • 因此,这个时候如果我们换一种设计思路,高层模块不直接依赖低层的实现,而是依赖于低层模块的抽象,具体表现为我们增加一个IBLL 层,里面定义业务逻辑的接口,UI层依赖于IBLL层,BLL层实现IBLL里面的接口,所以具体的业务逻辑则定义在BLL里面,这个时候如果我们BLL里面的逻辑发生变化,只要接口的行为不变,上层UI里面就不用发生任何变化。

2.2 开放封闭原则-OCP

  • 简称开闭原则。
  • 英文缩写OCP,全称Open Closed Principle。
  • 软件实体(包括类、模块、功能等)应该对扩展开放,但是对修改关闭。
  • 开放拓展,封闭修改。

2.3 单一职责原则-SRP

  • 单一职责原则(SRP:Single responsibility principle)。
  • 规定一个类应该只有一个发生变化的原因,变换的方向隐含类的责任
  • 因此我们创造一个类,一定不能过于臃肿,不能放过多的方法接口,因为这样就承担了过多的责任。

2.4 Liskov 替换原则-LSP

  • 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。
  • 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。
    • 子类必须能够替换他们的基类(IS-A)
    • 继承表达类型抽象。
  • 尽管看似这个原则天经地义,但是在实际的业务逻辑中,某些人会把没用到的父类接口抛出异常,这样就违背的这一原则,难免以后不会出现错误。

2.5 接口隔离原则-ISP

  • Interface Segregation Principle-LSP接口隔离原则。
    • 不应该强迫客户程序依赖它们不用的方法。
    • 接口应该小而完备。
  • 即,使用多个专门的接口比使用单一的总接口要好。
  • 换言之,从一个客户类的角度来讲:一个类对另外一个类的依赖性应当是建立在最小的接口上的。
  • 即减少依赖性,避免一处变动进而处处改动。

2.6 优先使用对象组合

  • 优先使用复合关系或委托关系,而不是继承关系。
  • class A 里面放一个 class B,可以是指针或是对象。
  • 类继承通常为“白箱复用”,对象组合通常为“黑箱复用”。
    • 继承在某种程度上破坏了封装性,子类父类耦合度高。
    • 而对象组合则只要求被组合的对象具有良好定义的接口,耦合度低。

2.7 封装变换点

  • 简单来说,就是给对象之间进行分界,一侧封装,一侧修改。
  • 使用封装来创建对象之间的分界层,让设计者可以在分界层的一侧进行修改,而不会对另一侧产生不良的影响,从而实现层次间的松耦合。

2.8 针对接口编程

  • 针对接口编程,而不是针对实现编程。
    • 不将变量类型声明为某个特定的具体类,而是声明为某个接口。
    • 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口。
    • 减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案。
  • 其实从生活中也可管中窥豹,一个产业强盛的标志就是——接口标准化。

2.9 总结

3. 设计模式简介

  • 一共23种设计模式,即 GOF-23

  • 创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、构建器模式、原型模式。

  • 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥模式、组合模式、享元模式。

  • 行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代器模式、职责链模式、命令模式、备忘录模式、状态模式、访问器模式、中介者模式、解析器模式。

“组件协作” 模式

4. Template Method 模板方法模式

  • 个人理解:继承多态,早绑定改为晚绑定。

  • 模板方法 的定义

  • 定义一个操作中的算法的骨架 (稳定),而将一些步骤延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特定步骤。——《设计模式》GoF

  • 结构化软件设计的流程是一种早绑定的写法,Library写的比Application早,写得比较晚的调用实现比较早的程序就叫做早绑定

  • 面向对象软件设计的流程是一种晚绑定的写法,Library反过来调用Application,实现的比较早的调用实现比较晚的就叫做晚绑定

  • 首先分析下结构化设计过程。一种早绑定的写法,一个晚的东西(Application)调用早(Library)的东西。

  • 而对于面向对象过程,采用的是虚函数晚绑定的写法,由程序库开发人员定义主流程。

  • 稳定中有变化,即模板方法模式。

5. Strategy 策略模式

  • 个人理解:继承多态,开放接口,封闭修改。

  • 属于"组件协作"三种模式中的一种。

  • 如何满足开闭原则,就是策略模式的重点。开放拓展,闭合修改。

  • 复用——编译单位,二进制层次的复用,粘贴源代码并不是复用。

  • 以下以计算不同国家税收的程序为例进行介绍。

  • 首先看看,不用策略模式,最简单代码实现就是枚举类型,需要修改的时候就添加相应的代码,但这违背了开闭原则,实际的业务开发应该是开放拓展,闭合修改。

  • 以下是利用多态实现的税收程序。遵循可开放封闭原则,保持复用性。

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    
    class TaxStrategy
    {
    public:
        virtual double Calculate(const Context &context) = 0;
        virtual ~TaxStrategy() {}
    };
    
    class CNTax : public TaxStrategy
    {
    public:
        virtual double Calculate(const Context &context)
        {
            //***********
        }
    };
    
    class USTax : public TaxStrategy
    {
    public:
        virtual double Calculate(const Context &context)
        {
            //***********
        }
    };
    
    // 添加拓展
    class DETax : public TaxStrategy
    {
    public:
        virtual double Calculate(const Context &context)
        {
            //***********
        }
    };
    
    // 扩展
    //*********************************
    class FRTax : public TaxStrategy
    {
    public:
        virtual double Calculate(const Context &context)
        {
            //.........
        }
    };
    
    // 以下并不需要修改基类
    class SalesOrder
    {
    private:
        TaxStrategy *strategy;// 必须是指针,才能动态绑定多态调用
    
    public:
        // 调用工厂模式返回对应的堆对象指针
        SalesOrder(StrategyFactory *strategyFactory)
        {
            this->strategy = strategyFactory->NewStrategy();
        }
        ~SalesOrder()
        {
            delete this->strategy;
        }
    
    public
        double CalculateTax()
        {
            //...
            Context context();
    
            double val =
                strategy->Calculate(context); // 多态调用
            //...
        }
    };
    

6. Observer / Event 观察者模式

  • 观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作 发布-订阅 模式、模型-视图 模式,它是对象行为型模式。

  • 以文件分割为例子,在GUI界面的按钮添加分割大文件的功能,并拓展进度通知的功能。

  • 以下是一个中规中矩的实现。

  • 首先是MainForm源文件,实现窗口类。

  • 然后文件分割FileSplitter类的实现

  • 上述实现违法了依赖倒置原则DIP,可以想到啊,通知不一定使用进度条,也可以设置一个Label显示百分比。

  • 或者这样想,当你需要增加通知机制的时候,你就需要修改文件分割类,这是不合理的,因为文件分割类不应该依赖细节实现,违反了DIP。

  • 所以这里改进通过抽象基类进行通知,而不是固定一个进度条 ProgressBar 进行通知。以下是使用观察者模式改进后的代码。

  • 首先MainForm类,作为其中一个观察者。当需要添加观察者的时候,直接在Button1_Click() 函数里面添加对应的观察者,并挂载到List里面。

  • 然后是FileSplitter 以及 通知抽象基类IProgress 的定义。以下代码是可以复用的,这才是观察者模式的真谛。

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    
    class IProgress
    {
    public:
    	virtual void DoProgress(float value) = 0;
    	virtual ~IProgress() {}
    };
    
    class FileSplitter
    {
    	string m_filePath;
    	int m_fileNumber;
    	// 可以放到windows界面,或者linux界面,或者控制台
    	List<IProgress *> m_iprogressList; // 抽象通知机制,支持多个观察者
    
    public:
    	FileSplitter(const string &filePath, int fileNumber) : m_filePath(filePath),
    														   m_fileNumber(fileNumber)
    	{
    	}
    
    	void split()
    	{
    		// 1.读取大文件
    
    		// 2.分批次向小文件中写入
    		for (int i = 0; i < m_fileNumber; i++)
    		{
    			//...
    
    			float progressValue = m_fileNumber;
    			progressValue = (i + 1) / progressValue;
    			onProgress(progressValue); // 发送通知
    		}
    	}
    
    	// 添加观察者
    	void addIProgress(IProgress *iprogress)
    	{
    		m_iprogressList.push_back(iprogress);
    	}
    
    	// 删除观察者
    	void removeIProgress(IProgress *iprogress)
    	{
    		m_iprogressList.remove(iprogress);
    	}
    
    protected:
    	virtual void onProgress(float value)
    	{
    		// 针对每一个观察者发送通知
    		List<IProgress *>::iterator itor = m_iprogressList.begin();
    
    		while (itor != m_iprogressList.end())
    			(*itor)->DoProgress(value); // 更新进度条
    		itor++;
    	}
    };
    
  • 观察者模式的结构如下。

  • 观察者模式是UI设计非常常见的模式。也是需要重点掌握的。

“单一职责” 模式

7. Decorator 装饰模式

  • 个人理解:把多重继承隔离出来,并行的拓展使用装饰器叠加,而不是继承。

  • 隶属于“单一职责” 模式。

  • 过度的使用继承导致子类的膨胀。

  • 简单来说,就是通过委托关系代替继承关系,进而减少代码冗余度,以及子类的生成个数。

  • 这里以IO流的操作为例,一般有各种流,如文件流、网络流、内存流,同时有拓展的缓存和加密操作,进而可以有缓存文件流、加密文件流、缓存加密文件流等等。

  • 一般的设计,就是不断继承生成所有的流,编译时生成。

  •   1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    
    // 业务操作
    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 CryptoFileStream : 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); // 写文件流
            // 额外的加密操作...
        }
    };
    
    class CryptoNetworkStream : : public NetworkStream
    {
    public:
        virtual char Read(int number)
        {
    
            // 额外的加密操作...
            NetworkStream::Read(number); // 读网络流
        }
        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
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    
    // 业务操作
    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 CryptoStream : public Stream
    {
        // 未来可以有各种各样的Stream在这里新创
        Stream *stream; // = new FileStream()...
    
    public:
        CryptoStream(Stream *stm) : stream(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 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);
    }
    
  • 以上已经解决代码冗余的问题,但为了更加优雅,可以通过新建一个 Decorator类来进一步划分职责。

  •   1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    
    // 业务操作
    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); // 读文件流
        }
        virtual void Seek(int position)
        {
            // 额外的加密操作...
            stream::Seek(position); // 定位文件流
            // 额外的加密操作...
        }
        virtual void Write(byte data)
        {
            // 额外的加密操作...
            stream::Write(data); // 写文件流
            // 额外的加密操作...
        }
    };
    
    class BufferedStream : public DecoratorStream
    {
    
        Stream *stream; //...
    
    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);
    }
    
  • 以上代码的类个数图如下所示。

  • 装饰模式的结构图如下所示。

  • 如何查看是装饰模式呢,一般来时,继承一个base类,类成员又有一个base类的指针,一般是Decorator模式。

  • 注意装饰模式和桥模式的区别

    • 装饰模式:装饰Decorator类继承于base类,且类成员有一个base类的指针;

    • 桥模式:抽象类的成员有一个是实现类的指针,两者无继承关系,只是复合关系。

8. Bridge 桥模式

  • 首先看看一般的设计是怎么样的。通过不断的继承叠加功能,最后创建的子对象会越来越多。

  •   1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    
    class Messager
    {
    public:
        virtual void Login(string username, string password) = 0;
        virtual void SendMessage(string message) = 0;
        virtual void SendPicture(Image image) = 0;
    
        virtual void PlaySound() = 0;
        virtual void DrawShape() = 0;
        virtual void WriteText() = 0;
        virtual void Connect() = 0;
    
        virtual ~Messager() {}
    };
    
    // 平台实现 n
    class PCMessagerBase : public Messager
    {
    public:
        virtual void PlaySound()
        {
            //**********
        }
        virtual void DrawShape()
        {
            //**********
        }
        virtual void WriteText()
        {
            //**********
        }
        virtual void Connect()
        {
            //**********
        }
    };
    
    class MobileMessagerBase : public Messager
    {
    public:
        virtual void PlaySound()
        {
            //==========
        }
        virtual void DrawShape()
        {
            //==========
        }
        virtual void WriteText()
        {
            //==========
        }
        virtual void Connect()
        {
            //==========
        }
    };
    
    // 业务抽象 m
    // 类的个数:1+n+m*n
    
    class PCMessagerLite : public PCMessagerBase
    {
    public:
        virtual void Login(string username, string password)
        {
    
            PCMessagerBase::Connect();
            //........
        }
        virtual void SendMessage(string message)
        {
    
            PCMessagerBase::WriteText();
            //........
        }
        virtual void SendPicture(Image image)
        {
    
            PCMessagerBase::DrawShape();
            //........
        }
    };
    
    class PCMessagerPerfect : public PCMessagerBase
    {
    public:
        virtual void Login(string username, string password)
        {
    
            PCMessagerBase::PlaySound();
            //********
            PCMessagerBase::Connect();
            //........
        }
        virtual void SendMessage(string message)
        {
    
            PCMessagerBase::PlaySound();
            //********
            PCMessagerBase::WriteText();
            //........
        }
        virtual void SendPicture(Image image)
        {
    
            PCMessagerBase::PlaySound();
            //********
            PCMessagerBase::DrawShape();
            //........
        }
    };
    
    class MobileMessagerLite : public MobileMessagerBase
    {
    public:
        virtual void Login(string username, string password)
        {
    
            MobileMessagerBase::Connect();
            //........
        }
        virtual void SendMessage(string message)
        {
    
            MobileMessagerBase::WriteText();
            //........
        }
        virtual void SendPicture(Image image)
        {
    
            MobileMessagerBase::DrawShape();
            //........
        }
    };
    
    class MobileMessagerPerfect : public MobileMessagerBase
    {
    public:
        virtual void Login(string username, string password)
        {
    
            MobileMessagerBase::PlaySound();
            //********
            MobileMessagerBase::Connect();
            //........
        }
        virtual void SendMessage(string message)
        {
    
            MobileMessagerBase::PlaySound();
            //********
            MobileMessagerBase::WriteText();
            //........
        }
        virtual void SendPicture(Image image)
        {
    
            MobileMessagerBase::PlaySound();
            //********
            MobileMessagerBase::DrawShape();
            //........
        }
    };
    
    void Process()
    {
        // 编译时装配
        Messager *m =
            new MobileMessagerPerfect();
    }
    
  • 可以看到,有很多是重复冗余可以删减的,因此可以做以下更改。

  •   1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    
    class Messager
    {
    protected:
        MessagerImp *messagerImp; //...
    public:
        virtual void Login(string username, string password) = 0;
        virtual void SendMessage(string message) = 0;
        virtual void SendPicture(Image image) = 0;
    
        virtual ~Messager() {}
    };
    
    // 实现类
    class MessagerImp
    {
    public:
        virtual void PlaySound() = 0;
        virtual void DrawShape() = 0;
        virtual void WriteText() = 0;
        virtual void Connect() = 0;
    
        virtual MessagerImp() {}
    };
    
    // 平台实现,即pc或mobile n个对象
    class PCMessagerImp : public MessagerImp
    {
    public:
        virtual void PlaySound()
        {
            //**********
        }
        virtual void DrawShape()
        {
            //**********
        }
        virtual void WriteText()
        {
            //**********
        }
        virtual void Connect()
        {
            //**********
        }
    };
    
    class MobileMessagerImp : public MessagerImp
    {
    public:
        virtual void PlaySound()
        {
            //==========
        }
        virtual void DrawShape()
        {
            //==========
        }
        virtual void WriteText()
        {
            //==========
        }
        virtual void Connect()
        {
            //==========
        }
    };
    
    // 业务抽象,即lite perfect版本 m个对象
    
    // 类的数目:1+n+m
    
    class MessagerLite : public Messager
    {
    public:
        // 赋值构造函数,以叠加新的桥
        MessagerLite(MessagerImp *mesimp) : messagerImp(mesimp) {}
        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:
        // 赋值构造函数,以叠加新的桥
        MessagerPerfect(MessagerImp *mesimp) : messagerImp(mesimp) {}
        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();
        // 即创建pclite
        Messager *m = new MessagerLite(mImp);
    }
    
  • 该设计模式结构如下所示。

  • 注意装饰模式和桥模式的区别

    • 装饰模式:装饰Decorator类继承于base类,且类成员有一个base类的指针;

    • 桥模式:抽象类的成员有一个是实现类的指针,两者无继承关系,只是复合关系。

"对象创建" 模式

  • 绕开new。

9. Factory Method 工厂方法模式

  • 目的:创建对象的时候不直接new,不静态创建,而是解耦,运行时创建,交给将来的子类创建。

  • 还是以文件分割为例子,这里文件分割可以分为多种,如二进制分割、文本分割等等。但是在MainForm窗口类只能是一个固定的文件分割类别,如下所示,这样就产生了依赖。

  • 如何解除这种依赖关系呢,答案就是创建工厂基类。

  • 适合自动创建无依赖关系的类,若是创建的类有关系,可以考虑抽象工厂模式。

  • 首先在MainForm里面新建一个工厂指针,并在初始化的时候传入具体的工厂指针,即交给将来去创建。

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    class MainForm : public Form
    {
        SplitterFactory *factory; // 工厂
    
    public:
        // 交个将来创建具体哪一个工厂
        MainForm(SplitterFactory *factory)
        {
            this->factory = factory;
        }
    
        void Button1_Click()
        {
    
            ISplitter *splitter =
                factory->CreateSplitter(); // 多态new
    
            splitter->split();
        }
    };
    
  • 然后创建抽象工厂,并使用虚函数继承到具体工厂,来在未来使用多态性质。

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    // 工厂基类
    class SplitterFactory
    {
    public:
        virtual ISplitter *CreateSplitter() = 0;
        virtual ~SplitterFactory() {}
    };
    
    // 具体工厂
    class BinarySplitterFactory : public SplitterFactory
    {
    public:
        virtual ISplitter *CreateSplitter()
        {
            return new BinarySplitter();
        }
    };
    
    class TxtSplitterFactory : public SplitterFactory
    {
    public:
        virtual ISplitter *CreateSplitter()
        {
            return new TxtSplitter();
        }
    };
    
    class PictureSplitterFactory : public SplitterFactory
    {
    public:
        virtual ISplitter *CreateSplitter()
        {
            return new PictureSplitter();
        }
    };
    
    class VideoSplitterFactory : public SplitterFactory
    {
    public:
        virtual ISplitter *CreateSplitter()
        {
            return new VideoSplitter();
        }
    };
    
  • 接着是最后本来就有的具体文件分割类。

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    // 抽象类
    class ISplitter
    {
    public:
        virtual void split() = 0;
        virtual ~ISplitter() {}
    };
    
    // 具体文件分割类
    class BinarySplitter : public ISplitter
    {
    };
    
    class TxtSplitter : public ISplitter
    {
    };
    
    class PictureSplitter : public ISplitter
    {
    };
    
    class VideoSplitter : public ISplitter
    {
    };
    
  • 以下为工厂方法的结构图,其中虚线实心三角形标表示依赖关系。

  • 值得一提的是,工厂模式记得手动delete,上述伪代码没有涉及内存管理的部分。

10. Abstract Factory 抽象工厂模式

  • 和工厂方法很类似,但是创建的是一系列“相互依赖的对象”。

  • 来看以下例子,任务是创建一个数据访问层,如果是创建的是SQL Server的数据库,那么就要创建相对应的连接对象、命令对象以及DateReader对象。但是这个对象明显不能是一个固定的数据库,因为客户也许今天需要的是SQL Serve,但明天也许就是Myql,后天就是Oracle。

  • 以下是固定数据库的业务写法,这种是不能支持多个数据库的。

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    class EmployeeDAO
    {
    public:
        vector<EmployeeDO> GetEmployees()
        {
            // 以下不支持多数据库的业务
            SqlConnection *connection =
                new SqlConnection();
            connection->ConnectionString = "...";
    
            // 命令和数据库进行连接
            SqlCommand *command =
                new SqlCommand();
            command->CommandText = "...";
            command->SetConnection(connection);
    
            // 从制定数据库读取
            SqlDataReader *reader = command->ExecuteReader();
            while (reader->Read())
            {
            }
    
            // 以上可以看到,连接、创建命令对象、创建读取对象三者是相互依赖的,并非独立对象
        }
    };
    
  • 以下代码是为连接对象、命令对象和读取对象分别设计一个对象工厂,可以解决以上不能适配不同数据库的问题。但是也暴露了其他的问题。

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    
    // 数据库访问有关的基类
    class IDBConnection
    {
    };
    class IDBConnectionFactory
    {
    public:
        virtual IDBConnection *CreateDBConnection() = 0;
    };
    
    class IDBCommand
    {
    };
    class IDBCommandFactory
    {
    public:
        virtual IDBCommand *CreateDBCommand() = 0;
    };
    
    class IDataReader
    {
    };
    class IDataReaderFactory
    {
    public:
        virtual IDataReader *CreateDataReader() = 0;
    };
    
    // 支持SQL Server
    class SqlConnection : public IDBConnection
    {
    };
    class SqlConnectionFactory : public IDBConnectionFactory
    {
    };
    
    class SqlCommand : public IDBCommand
    {
    };
    class SqlCommandFactory : public IDBCommandFactory
    {
    };
    
    class SqlDataReader : public IDataReader
    {
    };
    class SqlDataReaderFactory : public IDataReaderFactory
    {
    };
    
    // 支持Oracle
    class OracleConnection : public IDBConnection
    {
    };
    
    class OracleCommand : public IDBCommand
    {
    };
    
    class OracleDataReader : public IDataReader
    {
    };
    
    class EmployeeDAO
    {
        // 三个工厂
        IDBConnectionFactory *dbConnectionFactory;
        IDBCommandFactory *dbCommandFactory;
        IDataReaderFactory *dataReaderFactory;
    
    public:
        vector<EmployeeDO> GetEmployees()
        {
            IDBConnection *connection =
                dbConnectionFactory->CreateDBConnection();
            connection->ConnectionString("...");
    
            IDBCommand *command =
                dbCommandFactory->CreateDBCommand();
            command->CommandText("...");
            command->SetConnection(connection); // 关联性
    
            IDBDataReader *reader = command->ExecuteReader(); // 关联性
            while (reader->Read())
            {
            }
        }
    };
    
  • 但是我们可以想象,以上三个对象是有相关性的,一个数据库必须要使用其本身的读取接口。因为我们可以想到,那这可以只创建一个工厂就可以了,生产一组对象

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    
    // 数据库访问有关的基类
    class IDBConnection
    {
    };
    
    class IDBCommand
    {
    };
    
    class IDataReader
    {
    };
    
    class IDBFactory
    {
    public:
        virtual IDBConnection *CreateDBConnection() = 0;
        virtual IDBCommand *CreateDBCommand() = 0;
        virtual IDataReader *CreateDataReader() = 0;
    };
    
    // 支持SQL Server
    class SqlConnection : public IDBConnection
    {
    };
    class SqlCommand : public IDBCommand
    {
    };
    class SqlDataReader : public IDataReader
    {
    };
    
    class SqlDBFactory : public IDBFactory
    {
    public:
        virtual IDBConnection *CreateDBConnection();
        virtual IDBCommand *CreateDBCommand();
        virtual IDataReader *CreateDataReader();
    };
    
    // 支持Oracle
    class OracleConnection : public IDBConnection
    {
    };
    
    class OracleCommand : public IDBCommand
    {
    };
    
    class OracleDataReader : public IDataReader
    {
    };
    
    class OrcleFactory
    {
    public:
        virtual IDBConnection *CreateDBConnection();
        virtual IDBCommand *CreateDBCommand();
        virtual IDataReader *CreateDataReader();
    };
    
    
    class EmployeeDAO
    {
        IDBFactory *dbFactory;
    
    public:
        vector<EmployeeDO> GetEmployees()
        {
            IDBConnection *connection =
                dbFactory->CreateDBConnection();
            connection->ConnectionString("...");
    
            IDBCommand *command =
                dbFactory->CreateDBCommand();
            command->CommandText("...");
            command->SetConnection(connection); // 关联性
    
            IDBDataReader *reader = command->ExecuteReader(); // 关联性
            while (reader->Read())
            {
            }
        }
    };
    
  • 所以综述,抽象工厂就是可以产生一系列有相互关系对象的工厂。

11. Prototype 原型模式

  • 工厂方法模式的变体,本质也是绕开new。

  • 通过clone() 克隆自己来创建对象。原型对象是供你克隆的,而不是供你使用的,我们需要克隆得到新对象,再来使用。

  • 实例如下,以各种分割类为例子,如何创建不同的分割类。

  • 结构图如下:

  • 为什么用Prototype而不用Factory Method,因为对于复杂的类,这样可以更加灵活的创建相同的对象;而不是像工厂模式一样,进行功能的拓展。

12. Builder 构建器模式

  • 类似Template Method,但是用于解决对象创建的问题。

  • 比如构建一个房子,你必须打地基、建屋顶、建窗户等待一系列固定的流程,但是每一个流程都有特定的方式,这个是不固定的。

  • 结构图

  • 示例代码,可以看《大话设计模式》

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    
    class House {
      //....
    };
    
    class StoneHouse : public House {};
    
    class HouseBuilder {
    public:
      House *GetResult() { return pHouse; }
      virtual ~HouseBuilder() {}
    
    protected:
      House *pHouse;
      virtual void BuildPart1() = 0;
      virtual void BuildPart2() = 0;
      virtual void BuildPart3() = 0;
      virtual void BuildPart4() = 0;
      virtual void BuildPart5() = 0;
    };
    
    class StoneHouseBuilder : public HouseBuilder {
    protected:
      virtual void BuildPart1() { pHouse->Part1 = ...; }
      virtual void BuildPart2() {}
      virtual void BuildPart3() {}
      virtual void BuildPart4() {}
      virtual void BuildPart5() {}
    };
    
    class HouseDirector {
    
    public:
      HouseBuilder *pHouseBuilder;
    
      HouseDirector(HouseBuilder *pHouseBuilder) {
        this->pHouseBuilder = pHouseBuilder;
      }
    
      House *Construct() {
    
        pHouseBuilder->BuildPart1();
    
        for (int i = 0; i < 4; i++) {
          pHouseBuilder->BuildPart2();
        }
    
        bool flag = pHouseBuilder->BuildPart3();
    
        if (flag) {
          pHouseBuilder->BuildPart4();
        }
    
        pHouseBuilder->BuildPart5();
    
        return pHouseBuilder->GetResult();
      }
    };
    

“对象性能” 模式

13. Singleton 单例模式

  • 保证一个类只有一个实例。不同于工厂模式,绕过new;单件模式是为了性能,保证只有一个类。

  • 如何设计

    • 设计一个私有的拷贝构造函数;
    • 设计静态函数、静态指针变量;
  • 关于饿汉式和懒汉式单例模式

    • 饿汉式:只要类被加载,就会立刻实例化Singleton实例,后续无论怎么操作,只要严格使用getInstance,就不会出现其他实例。类一加载就实例化对象,所有需要提前占用系统资源,所以叫饿汉式。
    • 懒汉式:类被加载的时候,没有立刻被实例化,第一次调用getInstance的时候,才真正的实例化。如果要是代码一整场都没有调用getInstance,此时实例化的过程也就被省略掉了,又称“延时加载”。一般认为“懒汉模式” 比 “饿汉模式”效率更高。懒汉模式有很大的可能是“实例用不到”,此时就节省了实例化的开销。
  • 1.饿汉模式是否线程安全? 安全。

    类加载只有一次机会,不可能并发执行,对于饿汉模式来说,多线程同时调用getInstance,由于getInstance里只做了一件事:读取instance实例的地址=》多个线程在同时读取同一个变量,不会产生线程不安全。

  • 2.懒汉模式线程安全吗?为什么? 懒汉模式是线程不安全的,只有在实例化之前调用,存在线程不安全问题。

  • 以下是c++下的单线程、多线程加锁、多线程加双检查锁、最终的线程安全版本代码

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    
    class Singleton{
    private:
        Singleton();
        Singleton(const Singleton& other);
    public:
        static Singleton* getInstance();
        static Singleton* m_instance;
    };
    
    Singleton* Singleton::m_instance=nullptr;
    
    //线程非安全版本,仅单线程安全
    Singleton* Singleton::getInstance() {
        if (m_instance == nullptr) {
            m_instance = new Singleton();
        }
        return m_instance;
    }
    
    
    //线程安全版本,但锁的代价过高
    Singleton* Singleton::getInstance() {
        Lock lock;
        if (m_instance == nullptr) {
            m_instance = new Singleton();
        }
        return m_instance;
    }
    
    
    
    //双检查锁,但由于内存读写reorder不安全
    Singleton* Singleton::getInstance() {
    
        if(m_instance==nullptr){
            Lock lock;
            if (m_instance == nullptr) {
                m_instance = new Singleton();
            }
        }
        return m_instance;
    }
    
    
    
    //C++ 11版本之后的跨平台实现 (volatile)
    std::atomic<Singleton*> Singleton::m_instance;
    std::mutex Singleton::m_mutex;
    
    Singleton* Singleton::getInstance() {
        Singleton* tmp = m_instance.load(std::memory_order_relaxed);
        std::atomic_thread_fence(std::memory_order_acquire);//获取内存fence
        if (tmp == nullptr) {
            std::lock_guard<std::mutex> lock(m_mutex);
            tmp = m_instance.load(std::memory_order_relaxed);
            if (tmp == nullptr) {
                tmp = new Singleton;
                std::atomic_thread_fence(std::memory_order_release);//释放内存fence
                m_instance.store(tmp, std::memory_order_relaxed);
            }
        }
        return tmp;
    }
    
  • 什么叫reorder

    • new对象的时候,我们认为的是先分配内存、然后调用构造器构造对象、最后返回对象指针 这三部分组成。但是由于编译器的优化,可能出现reorder行为,即上述三步骤打乱,可能是先分配内存、然后返回对象指针、最后调用构造器构造对象。
    • 这样对于双检查锁就会出现问题,因为可能第二个线程读取到指针,但是该指针指向的内存无对象。
    • c#和java语言存在volatile关键字,告诉编译器该对象创建的时候不需要reorder,就避免了这个问题。
    • c++在11版本后可以通过原子操作解决上述问题。

14. Flyweight 享元模式

  • 解决抽象问题带来的性能问题。

  • 比如字符串,Java里面就有串池这个字符串共享的方法;还有线程的创建,可以采用线程池完成共享。

  • 总体而言,就是通过共享减少资源的消耗。

  • 结构图如下:

  • 以字体享元为例子,如下伪代码

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    
    class Font {
    private:
    
        //unique object key
        string key;
    
        //object state
        //....
    
    public:
        Font(const string& key){
            //...
        }
    };
    ß
    
    class FontFactory{
    private:
        map<string,Font* > fontPool;
    
    public:
        Font* GetFont(const string& key){
    
            map<string,Font*>::iterator item=fontPool.find(key);
    
            if(item!=footPool.end()){
                return fontPool[key];
            }
            else{
                Font* font = new Font(key);
                fontPool[key]= font;
                return font;
            }
    
        }
    
        void clear(){
            //...
        }
    };
    

"接口隔离" 模式

  • 重点就是 隔离。

15. Facade 外观模式

  • 也称为门面模式。

  • 模式定义

  • Facade内部是高内聚的,对外是松耦合的。

  • 没有具体的代码框架,这更具体的是一种思想,因为不同项目,代码千差万别。

16. Proxy 代理模式

  • 一样属于“接口隔离”模式的一种。

  • 无法直接创建realSubject,只能通过Proxy代理来创建。

  • 一般的设计

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    class ISubject{
    public:
        virtual void process();
    };
    
    
    class RealSubject: public ISubject{
    public:
        virtual void process(){
            //....
        }
    };
    
    class ClientApp{
    
        ISubject* subject;
    
    public:
    
        ClientApp(){
    		// 这里因为安全、性能开销等问题,不允许创建
            subject=new RealSubject();
        }
    
        void DoTask(){
            //...
            subject->process();
    
            //....
        }
    };
    
  • 使用代理的设计

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    class ISubject{
    public:
        virtual void process();
    };
    
    
    //Proxy的设计
    class SubjectProxy: public ISubject{
    
    
    public:
        virtual void process(){
            //对RealSubject的一种间接访问
            //....
        }
    };
    
    class ClientApp{
    
        ISubject* subject;
    
    public:
    
        ClientApp(){
            subject=new SubjectProxy();
        }
    
        void DoTask(){
            //...
            subject->process();
    
            //....
        }
    };
    

17. Adapter 适配器模式

  • 结构图

  • STL里面的stack和queue的实现也是利用了适配器模式,里面利用了 deque 充当适配器的角色。

  • 对象适配器:使用组合关系,即复合或委托。——具备灵活性。

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    //对象适配器
    class Adapter: public ITarget{ //继承
    protected:
        IAdaptee* pAdaptee;//组合(复合/委托)
    
    public:
    
        Adapter(IAdaptee* pAdaptee){
            this->pAdaptee=pAdaptee;
        }
    
    	// 实现具体的适配方法
        virtual void process(){
            int data=pAdaptee->bar();
            pAdaptee->foo(data);
    
        }  
    };
    
  • 类适配器:使用多继承。——尽量不要用这,缺乏灵活性。

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    //类适配器,需要继承特定的类,因为抽象类继承没有意义
    //因此少了灵活性
    /*
    class Adapter: public ITarget,
                   protected IAdaptee{ //多继承
    }
    */
    class Adapter: public ITarget,
                   protected OldClass{ //多继承
    
    
    }
    
  • 示例代码

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    
    //目标接口(新接口)
    class ITarget{
    public:
        virtual void process()=0;
    };
    
    //遗留接口(老接口)
    class IAdaptee{
    public:
        virtual void foo(int data)=0;
        virtual int bar()=0;
    };
    
    //遗留类型
    class OldClass: public IAdaptee{
        //....
    };
    
    //对象适配器
    class Adapter: public ITarget{ //继承
    protected:
        IAdaptee* pAdaptee;//组合(复合/委托)
    
    public:
        Adapter(IAdaptee* pAdaptee){
            this->pAdaptee=pAdaptee;
        }
    
    	// 实现具体的适配方法
        virtual void process(){
            int data=pAdaptee->bar();
            pAdaptee->foo(data); 
        }  
    };
    
    
    //类适配器,需要继承特定的类,因为抽象类继承没有意义
    //因此少了灵活性
    /*
    class Adapter: public ITarget,
                   protected IAdaptee{ //多继承
    }
    */
    class Adapter: public ITarget,
                   protected OldClass{ //多继承   
    }
    
    int main(){
        IAdaptee* pAdaptee=new OldClass();
    
        ITarget* pTarget=new Adapter(pAdaptee);
        pTarget->process();
    
    }
    
    // 示例,使用deque转为stack和queue
    class stack{
        deqeue container;
    
    };
    
    class queue{
        deqeue container;
    
    };
    

18. Mediator 中介者模式

  • 多个对象相互依赖,编译时依赖转为运行时依赖。

  • 结构图

  • 实际业务中,更可能存在如下的关系。

"状态变化" 模式

19. State 状态模式

  • 需求的变化,会导致源码的if-else删改,违背开闭原则。

  • 类似 Strategy 策略模式。

  • 以网络的状态转换为应用,一般的代码如下

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    
    // 如果后续添加WaitState等状态,修改很多
    
    enum NetworkState
    {
        Network_Open,
        Network_Close,
        Network_Connect,
    };
    
    class NetworkProcessor{
    
        NetworkState state;
    
    public:
    
        void Operation1(){
            if (state == Network_Open){
    
                //**********
                state = Network_Close;
            }
            else if (state == Network_Close){
    
                //..........
                state = Network_Connect;
            }
            else if (state == Network_Connect){
    
                //$$$$$$$$$$
                state = Network_Open;
            }
        }
    
        public void Operation2(){
    
            if (state == Network_Open){
    
                //**********
                state = Network_Connect;
            }
            else if (state == Network_Close){
    
                //.....
                state = Network_Open;
            }
            else if (state == Network_Connect){
    
                //$$$$$$$$$$
                state = Network_Close;
            }
    
        }
    
        public void Operation3(){
    
        }
    };
    
  • 利用State状态模式,修改如下

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    
    /*
    利用虚函数,即使后期添加WaitState等新的状态;
    也不需要修改原代码
    */
    
    class NetworkState{
    public:
        NetworkState* pNext;
        virtual void Operation1()=0;
        virtual void Operation2()=0;
        virtual void Operation3()=0;
    
        virtual ~NetworkState(){}
    };
    
    
    class OpenState :public NetworkState{
    
        static NetworkState* m_instance;
    public:
        static NetworkState* getInstance(){
            if (m_instance == nullptr) {
                m_instance = new OpenState();
            }
            return m_instance;
        }
    
        void Operation1(){
    
            //**********
            pNext = CloseState::getInstance();
        }
    
        void Operation2(){
    
            //..........
            pNext = ConnectState::getInstance();
        }
    
        void Operation3(){
    
            //$$$$$$$$$$
            pNext = OpenState::getInstance();
        }
    
    
    };
    
    class CloseState:public NetworkState{ 
    //...	
    };
    
    
    // 实际的操作函数
    class NetworkProcessor{
    
        NetworkState* pState;
    
    public:
    
        NetworkProcessor(NetworkState* pState){
    
            this->pState = pState;
        }
    
        void Operation1(){
            //...
            pState->Operation1();
            pState = pState->pNext;
            //...
        }
    
        void Operation2(){
            //...
            pState->Operation2();
            pState = pState->pNext;
            //...
        }
    
        void Operation3(){
            //...
            pState->Operation3();
            pState = pState->pNext;
            //...
        }
    
    };
    

20. Memento 备忘录模式

  • 示例代码如下,就是多利用一些类储存当前类的状态信息,具体实现各有各的不同,以下示例只是简单以字符串储存表示。实际可以有自定义编码、传输加密等等等不同的实现方式。

  • 理解很简单,但是实现根据不同的要求有不同的设计。

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    
    class Memento
    {
        string state;
        //..
    public:
        Memento(const string & s) : state(s) {}
        string getState() const { return state; }
        void setState(const string & s) { state = s; }
    };
    
    
    
    class Originator
    {
        string state;
        //....
    public:
        Originator() {}
        Memento createMomento() {
            Memento m(state);
            return m;
        }
        void setMomento(const Memento & m) {
            state = m.getState();
        }
    };
    
    
    
    int main()
    {
        Originator orginator;
    
        //捕获对象状态,存储到备忘录
        Memento mem = orginator.createMomento();
    
        //... 改变orginator状态
    
        //从备忘录中恢复
        orginator.setMomento(memento);
    }
    
  • 小模式,现在看来已经算过时了,现在的高级语言(java c#)可利用对象序列化编码来实现 Memonto模式。

  • 对象序列化是将对象转换为可在网络上传输或存储到本地磁盘的字节序列的过程。在序列化期间,对象的状态(即变量值)被转换为字节流,以便它可以在网络上传输或存储到本地磁盘中。在反序列化期间,字节流被转换回对象状态,以便可以重新创建原始对象。序列化和反序列化通常用于分布式系统、远程调用、缓存和持久化等应用场景。在Java中,常用的序列化方式是利用Java序列化API进行对象序列化和反序列化。

"数据结构" 模式

21. Composite 组合模式

  • Composite 模式使得对象和组合对象的实际调用有一致性,否则需要多个if-else判断不同的情况,从而添加不同功能需要修改,违背开闭原则。

  • 使用多态针对树形结构和叶子节点进行统一化的处理。

  • 示例代码

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    
    #include <iostream>
    #include <list>
    #include <string>
    #include <algorithm>
    
    using namespace std;
    
    class Component
    {
    public:
        virtual void process() = 0;
        virtual ~Component(){}
    };
    
    //树节点
    class Composite : public Component{
    
        string name;
        // 子节点,指针-多态
        list<Component*> elements;
    public:
        Composite(const string & s) : name(s) {}
    
        void add(Component* element) {
            elements.push_back(element);
        }
        void remove(Component* element){
            elements.remove(element);
        }
    
        void process(){
    
            //1. process current node
    
    
            //2. process leaf nodes
            for (auto &e : elements)
                e->process(); //多态调用
    
        }
    };
    
    //叶子节点
    class Leaf : public Component{
        string name;
    public:
        Leaf(string s) : name(s) {}
    
        void process(){
            //process current node
        }
    };
    
    
    void Invoke(Component & c){
        //...
        c.process();
        //...
    }
    
    
    int main()
    {
    
        Composite root("root");
        Composite treeNode1("treeNode1");
        Composite treeNode2("treeNode2");
        Composite treeNode3("treeNode3");
        Composite treeNode4("treeNode4");
        Leaf leat1("left1");
        Leaf leat2("left2");
    
        root.add(&treeNode1);
        treeNode1.add(&treeNode2);
        treeNode2.add(&leaf1);
    
        root.add(&treeNode3);
        treeNode3.add(&treeNode4);
        treeNode4.add(&leaf2);
    
        process(root);
        process(leaf2);
        process(treeNode3);
    
    }
    

22. Iterator 迭代器模式

  • 这种模式对于c++而言,在现在已经过时了;因为STL和泛型编程的 Iterator,其实现思想和迭代器模式的思想思想是一样的,都是通过一种接口的方式,隔离算法和容器之间的变化。

  • 面向对象的迭代器代码如下

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    
    // 面向对象实现迭代器
    // 使用多态机制
    
    template<typename T>
    class Iterator
    {
    public:
        virtual void first() = 0;
        virtual void next() = 0;
        virtual bool isDone() const = 0;
        virtual T& current() = 0;
    };
    
    
    
    template<typename T>
    class MyCollection{
    public:
        Iterator<T> GetIterator(){
            //...
        }
    
    };
    
    template<typename T>
    class CollectionIterator : public Iterator<T>{
        MyCollection<T> mc;
    public:
    
        CollectionIterator(const MyCollection<T> & c): mc(c){ }
    
        void first() override {
    
        }
        void next() override {
    
        }
        bool isDone() const override{
    
        }
        T& current() override{
    
        }
    };
    
    void MyAlgorithm()
    {
        MyCollection<int> mc;
    
        Iterator<int> iter= mc.GetIterator();
    
        for (iter.first(); !iter.isDone(); iter.next()){
            cout << iter.current() << endl;
        }
    
    }
    
  • 面向对象的实现方式不太好,因为虚函数调用有额外的性能开销,这是一种运行时多态。

  • c++在98年的STL泛型编程实现的迭代器,使用的是基于模板的多态的迭代器,是一种编译时多态,性能相对虚函数的面向对象的实现更好。

  • 有了泛型编程的迭代器,c++就很少有面向对象的迭代器了。

  • 以下代码定义了两个迭代器类:ListIteratorArrayIteratorListIterator 用于迭代 std::list 中的元素,ArrayIterator 用于迭代数组中的元素。每个迭代器类都实现了 hasNext()next() 方法。hasNext() 方法用于判断迭代器是否还有元素可用,next() 方法用于获取迭代器的下一个元素。

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    
    class Iterator {
    public:
      virtual ~Iterator() {}
    
      virtual bool hasNext() = 0;
      virtual int next() = 0;
    };
    
    class ListIterator : public Iterator {
    public:
      ListIterator(std::list<int> list) : list_(list) {}
    
      virtual bool hasNext() override {
        return !list_.empty();
      }
    
      virtual int next() override {
        int value = list_.front();
        list_.pop_front();
        return value;
      }
    
    private:
      std::list<int> list_;
    };
    
    class ArrayIterator : public Iterator {
    public:
      ArrayIterator(int* array, int size) : array_(array), size_(size) {}
    
      virtual bool hasNext() override {
        return index_ < size_;
      }
    
      virtual int next() override {
        int value = array_[index_];
        index_++;
        return value;
      }
    
    private:
      int* array_;
      int size_;
      int index_;
    };
    
  • 如今STL的迭代器采用的是模板以及函数重载实现的,是编译时多态,效率更高。

  • 但是Java、c#是通过运行时多态实现迭代器,尽管该思想对c++已经过时,但是java还是使用这种方式实现的迭代器。

23. Chain of Responsibility 职责链模式

  • 形成一个职责链表。

  • 归根到底就是一个多态指针链表数据结构的应用。到现在其实有点过时了。

  • 示例代码

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    
    #include <iostream>
    #include <string>
    
    using namespace std;
    
    enum class RequestType
    {
        REQ_HANDLER1,
        REQ_HANDLER2,
        REQ_HANDLER3
    };
    
    class Reqest
    {
        string description;
        RequestType reqType;
    public:
        Reqest(const string & desc, RequestType type) : description(desc), reqType(type) {}
        RequestType getReqType() const { return reqType; }
        const string& getDescription() const { return description; }
    };
    
    class ChainHandler{
        // 多态指针,指向自身,形成链表
        ChainHandler *nextChain;
        void sendReqestToNextHandler(const Reqest & req)
        {
            if (nextChain != nullptr)
                nextChain->handle(req);
        }
    protected:
        virtual bool canHandleRequest(const Reqest & req) = 0;
        virtual void processRequest(const Reqest & req) = 0;
    public:
        ChainHandler() { nextChain = nullptr; }
        void setNextChain(ChainHandler *next) { nextChain = next; }
    
    
        void handle(const Reqest & req)
        {
            if (canHandleRequest(req))
                processRequest(req);
            else
                sendReqestToNextHandler(req);
        }
    };
    
    
    class Handler1 : public ChainHandler{
    protected:
        bool canHandleRequest(const Reqest & req) override
        {
            return req.getReqType() == RequestType::REQ_HANDLER1;
        }
        void processRequest(const Reqest & req) override
        {
            cout << "Handler1 is handle reqest: " << req.getDescription() << endl;
        }
    };
    
    class Handler2 : public ChainHandler{
    protected:
        bool canHandleRequest(const Reqest & req) override
        {
            return req.getReqType() == RequestType::REQ_HANDLER2;
        }
        void processRequest(const Reqest & req) override
        {
            cout << "Handler2 is handle reqest: " << req.getDescription() << endl;
        }
    };
    
    class Handler3 : public ChainHandler{
    protected:
        bool canHandleRequest(const Reqest & req) override
        {
            return req.getReqType() == RequestType::REQ_HANDLER3;
        }
        void processRequest(const Reqest & req) override
        {
            cout << "Handler3 is handle reqest: " << req.getDescription() << endl;
        }
    };
    
    int main(){
        Handler1 h1;
        Handler2 h2;
        Handler3 h3;
        h1.setNextChain(&h2);
        h2.setNextChain(&h3);
    
        Reqest req("process task ... ", RequestType::REQ_HANDLER3);
        h1.handle(req);
        return 0;
    }
    

“行为变化” 模式

24. Command 命令模式

  • 示例代码

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    
    #include <iostream>
    #include <vector>
    #include <string>
    using namespace std;
    
    class Command
    {
    public:
        virtual void execute() = 0;
    };
    
    class ConcreteCommand1 : public Command
    {
        string arg;
    public:
        ConcreteCommand1(const string & a) : arg(a) {}
        void execute() override
        {
            cout<< "#1 process..."<<arg<<endl;
        }
    };
    
    class ConcreteCommand2 : public Command
    {
        string arg;
    public:
        ConcreteCommand2(const string & a) : arg(a) {}
        void execute() override
        {
            cout<< "#2 process..."<<arg<<endl;
        }
    };
    
    
    class MacroCommand : public Command
    {
        vector<Command*> commands;
    public:
        void addCommand(Command *c) { commands.push_back(c); }
        void execute() override
        {
            for (auto &c : commands)
            {
                c->execute();
            }
        }
    };
    
    
    
    int main()
    {
        ConcreteCommand1 command1(receiver, "Arg ###");
        ConcreteCommand2 command2(receiver, "Arg $$$");
    
        MacroCommand macro;
        macro.addCommand(&command1);
        macro.addCommand(&command2);
    
        macro.execute();
    }
    
  • GOF使用的是虚函数即运行时多态实现命令模式,但c++的函数对象可以使用模板即编译时多态完成实现。

  • C++ 的函数对象是一个可以像函数一样被调用的对象。 函数对象通常由类实现,该类重载了 operator() 运算符。 函数对象可以用于各种目的,例如在标准库算法中使用、作为函数的参数或作为函数的返回值。

    以下是 C++ 函数对象的一些示例:

    • 标准库中的函数对象,例如 std::greater 和 std::less
    • 自定义函数对象,例如一个可以比较两个字符串的函数对象。
    • 匿名函数对象,例如通过 lambda 表达式创建的函数对象。
  • 函数对象可以使 C++ 代码更加灵活和可扩展。 它们可以用来简化代码、使代码更具可读性,并使代码更容易进行测试。

  • 以下是stl的一个例子

  • 1
    2
    3
    4
    5
    6
    
    template <typename T>
    struct greater {
      bool operator()(const T& a, const T& b) const {
        return a > b;
      }
    };
    

25. Visitor 访问器模式

  • 如果直接在基类修改,会违背开闭原则。

  • 以下设计如果添加新需求,就违背了开闭原则:

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    
    #include <iostream>
    using namespace std;
    
    class Element
    {
    public:
        virtual void Func1() = 0;
    
    	// 假设如今添加的新需求,这样就违背了开闭原则
        virtual void Func2(int data)=0;
        //...
    
        virtual ~Element(){}
    };
    
    class ElementA : public Element
    {
    public:
        void Func1() override{
            //...
        }
    
    	// 这样每一个子类都要添加新方法,繁重的变更
        void Func2(int data) override{
            //...
        }
    
    };
    
    class ElementB : public Element
    {
    public:
        void Func1() override{
            //***
        }
    
        void Func2(int data) override {
            //***
        }
    
    };
    
  • 使用访问器模式的修改代码,最重要是accept函数的实现,两次多态辨析。

  •  1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    
    #include <iostream>
    using namespace std;
    
    class Visitor;
    
    // Element的类个数必须是固定的,不能运行时多态
    class Element
    {
    public:
        virtual void accept(Visitor& visitor) = 0; //第一次多态辨析
    
        virtual ~Element(){}
    };
    
    class ElementA : public Element
    {
    public:
        void accept(Visitor &visitor) override {
            visitor.visitElementA(*this);
        }
    
    
    };
    
    class ElementB : public Element
    {
    public:
        void accept(Visitor &visitor) override {
            visitor.visitElementB(*this); //第二次多态辨析
        }
    
    };
    
    
    class Visitor{
    public:
        virtual void visitElementA(ElementA& element) = 0;
        virtual void visitElementB(ElementB& element) = 0;
    
        virtual ~Visitor(){}
    };
    
    //=============以上结构不可改变了=====================
    // 每个拓展给ElementA和ElementB进行新操作
    //扩展1
    class Visitor1 : public Visitor{
    public:
        void visitElementA(ElementA& element) override{
            cout << "Visitor1 is processing ElementA" << endl;
        }
    
        void visitElementB(ElementB& element) override{
            cout << "Visitor1 is processing ElementB" << endl;
        }
    };
    
    //扩展2
    class Visitor2 : public Visitor{
    public:
        void visitElementA(ElementA& element) override{
            cout << "Visitor2 is processing ElementA" << endl;
        }
    
        void visitElementB(ElementB& element) override{
            cout << "Visitor2 is processing ElementB" << endl;
        }
    };
    
    
    int main()
    {
        Visitor2 visitor;
        ElementB elementB;
    	// 多次辨析line 10->29->65
        elementB.accept(visitor);// double dispatch
    
        ElementA elementA;
        elementA.accept(visitor);
    
        return 0;
    }
    
  • 可以看到,访问器模式的条件非常苛刻,因为Element的类个数必须是固定 这个条件很难保证。

"领域规则" 模式

26. Interpreter 解析器模式

  • 以最简单的加减运算为例子。

  •   1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    
    #include <iostream>
    #include <map>
    #include <stack>
    
    using namespace std;
    
    class Expression {
    public:
        virtual int interpreter(map<char, int> var)=0;
        virtual ~Expression(){}
    };
    
    //变量表达式
    class VarExpression: public Expression {
    
        char key;
    
    public:
        VarExpression(const char& key)
        {
            this->key = key;
        }
    
        int interpreter(map<char, int> var) override {
            return var[key];
        }
    
    };
    
    //符号表达式
    class SymbolExpression : public Expression {
    
        // 运算符左右两个参数
    protected:
        Expression* left;
        Expression* right;
    
    public:
        SymbolExpression( Expression* left,  Expression* right):
            left(left),right(right){
    
        }
    
    };
    
    //加法运算
    class AddExpression : public SymbolExpression {
    
    public:
        AddExpression(Expression* left, Expression* right):
            SymbolExpression(left,right){
    
        }
        int interpreter(map<char, int> var) override {
            return left->interpreter(var) + right->interpreter(var);
        }
    
    };
    
    //减法运算
    class SubExpression : public SymbolExpression {
    
    public:
        SubExpression(Expression* left, Expression* right):
            SymbolExpression(left,right){
    
        }
        int interpreter(map<char, int> var) override {
            return left->interpreter(var) - right->interpreter(var);
        }
    
    };
    
    
    // 解析字符串得到表达式树
    Expression*  analyse(string expStr) {
    
        stack<Expression*> expStack;
        Expression* left = nullptr;
        Expression* right = nullptr;
        for(int i=0; i<expStr.size(); i++)
        {
            switch(expStr[i])
            {
                case '+':
                    // 加法运算
                    left = expStack.top();
                    right = new VarExpression(expStr[++i]);
                    expStack.push(new AddExpression(left, right));
                    break;
                case '-':
                    // 减法运算
                    left = expStack.top();
                    right = new VarExpression(expStr[++i]);
                    expStack.push(new SubExpression(left, right));
                    break;
                default:
                    // 变量表达式
                    expStack.push(new VarExpression(expStr[i]));
            }
        }
    
        Expression* expression = expStack.top();
    
        return expression;
    }
    
    void release(Expression* expression){
    
        //释放表达式树的节点内存...
    }
    
    int main(int argc, const char * argv[]) {
        string expStr = "a+b-c+d-e";
        map<char, int> var;
        var.insert(make_pair('a',5));
        var.insert(make_pair('b',2));
        var.insert(make_pair('c',1));
        var.insert(make_pair('d',6));
        var.insert(make_pair('e',10));
    
        Expression* expression= analyse(expStr);
    
        int result=expression->interpreter(var);
    
        cout<<result<<endl;
    
        release(expression);
    
        return 0;
    }
    
  • 解析器模式现在不同常用了,对于复杂的文法规则,有很多语法生成器开源工具,没必要用面向对象,而且这样开销也小。

27. 总结

  • 管理变化,提高复用。

  • 关于对象模型

  • 继承转委托(指针)

  • 关注变化点和稳定的。