为了支持多态性,在vector里放指针类型
因为放的是指针,所以对象我们要用new的形式得到,得到堆对象的指针,而不能是栈对象
多态调用,各负其责
(1) 依赖倒置原则(DIP) 应该让不稳定的依赖于稳定的 -> 提出抽象类
高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定) 。
抽象(稳定)不应该依赖于实现细节(变化) ,实现细节应该依赖于抽象(稳定)。
第一种:
MainForm -> (依赖) Line、Rect (MainForm里包含 Line Rect)
高层的稳定的依赖了底层的不稳定的,高层也变得不稳定了
第二种:
MainForm -> Shape ( <- Line Rect) shape里并没有包含Line、Rect这两个对象,而是说,它俩是作为Shape的子类实现的,这个Shape类依旧是稳定的
抽象不应该依赖实现细节,否则它也变得不稳定了
这里是Line 、Rect 不稳定的依赖稳定的,MainForm 和 Shape 是两个稳定的
(2)开放封闭原则(OCP) 用增加一个东西的形式来应对需求的变化,而不是满街的去改
(3)单一职责原则(SRP) 不要承担太多本不该自己承担的责任
(4)Liskov 替换原则(LSP)
(5)接口隔离原则(ISP)不要把不必要的方法public出去,无节制的用public 就会让客户产生依赖
**(6)优先使用对象组合,而不是类继承 **
**(7)封装变化点 **
(8)针对接口编程,而不是针对实现编程 和依赖导致原则是相辅相成的,基本违背一个另一个也违背
下面两图明显是针对实现编程
Refactoring to Patterns 重构的方式得到模式
(1) 静态->动态
(2)早绑定->晚绑定
(3)继承->组合
(4)编译时依赖->运行时依赖
(5)紧耦合->松耦合
注意C++里任何一个基类的虚构函数都要写成虚函数,否则当你真正delete动态绑定创建出的指针对象时,是调不到它本应该调用的那个函数的
程序主流程(Run方法)是由Library开发人员写的了
第一种:
class Library{
public:
void Step1(){
//...
}
void Step3(){
//...
}
void Step5(){
//...
}
};
class Application{
public:
bool Step2(){
//...
}
void Step4(){
//...
}
};
int main()
{
Library lib();
Application app();
lib.Step1();
if (app.Step2()){
lib.Step3();
}
for (int i = 0; i < 4; i++){
app.Step4();
}
lib.Step5();
}
第二种:
class Library{
public:
void Run(){
Step1();
if (Step2()) {
Step3();
}
for (int i = 0; i < 4; i++){
Step4();
}
Step5();
}
virtual ~Library(){ }
protected:
void Step1() {
//.....
}
void Step3() {
//.....
}
void Step5() {
//.....
}
virtual bool Step2() = 0;
virtual void Step4() =0;
};
class Application : public Library {
protected:
virtual bool Step2(){
}
virtual void Step4() {
}
};
int main()
{
Library* pLib=new Application();
lib->Run();
delete pLib;
}
Library是比你写的早的,一个晚的东西调用早的东西,就是早绑定
面向对象里面:一个早的东西调用晚的东西,就是晚绑定
延迟到子类,支持子类来变化
定义一个操作中的算法的骨架(稳定),而将一些步骤延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特定步骤。
稳定中有变化
设计模式最大的作用就是在变化和稳定中间寻找一个点来管理这些变化,如果一切都是稳定的,那设计模式也没有意义了
红色是稳定的部分,蓝色是变化的部分
晚绑定不止是可以用虚函数,也可以使用函数指针。当然其实虚函数背后也是函数指针
一般来说,把被Template Method调用的虚方法设置为protected方法,一般不直接供外界调用
开放封闭原则:类模块尽可能要用扩展的方式去面对未来的变化,而不是找到源代码去更改它
(增加一个法国的枚举属性,并且在下面else if 里更改了加了法国)
(一个类,通过判断里面枚举的值,判断是计算哪国的税法,新的国家出现时要更改代码)
真正的复用是二进制意义的单位复用,不是源代码意义的复用
原来的代码应该是原封不动的,粘贴源代码不叫复用。
我们应该这样:
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);
//...
}
};
红色:稳定的
蓝色:变化的
总结:
(一般来说,出现if-else就表示我们现在可以用strategy)
if-else是结构化分而治之的提现,如果后面还要扩展,肯定是strategy。那如果if-else只有固定的几种,比方说一周有七天,那么它不会改变了,就可以用if-else。
加进度条:违背了依赖倒置原则
(我们讲的依赖都是编译时依赖)
我们在设计FileSpliter时里面加入了ProgressBar,这是一种实现细节。抽象不应该依赖于实现细节,也许我们后面会在界面上通过一个label展示出来。
高层不能依赖于低层,二者都应该依赖于抽象
抽象不能依赖于实现细节
那不依赖ProgressBar,我们用谁呢?它的基类ControlBase吗?它自己都没有设置进度条的方法诶。
所以我们要分析,它扮演的是什么角色?它扮演的是一个通知的角色,因此我们不一定只是要用控件去表示呢
所以我们建立一个类去表示一个抽象的通知机制
完成了从具体到抽象的跃迁
C++虽然不推荐使用多继承,但是他推荐一种:一个是主继承,其他是接口/抽象基类
如果有多个观察者呢?
我们定义为List
在构造函数里也没法直接构造了,而是定义一个void addIprogress()、removeIprogress()
定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
Attach 相当于 addIprogress、Detach 相当于 removeIprogress
Notify 相当于 onProgress
可以把这三个方法再放到一个基类里面,让Filespliter再去继承这个基类(也就是图中画的Subject)
下面的ConcreteSubject (主体对象) 是我们的 Filespliter
ConcreteObserver是具体的观察者
你随便添加任何观察者,我这里是松耦合,都可以调用
Subject里有很多observer
要点总结
核心:抽象的通知依赖关系
class IProgress{
public:
virtual void DoProgress(float value)=0; //具体Observer的更新函数是在这里定义
virtual ~IProgress(){}
};
class FileSplitter//(ConcreteSubject,这和Subject写一起了)
{
string m_filePath;
int m_fileNumber;
List<IProgress*> m_iprogressList;
public:
FileSplitter(const string& filePath, int fileNumber) :
m_filePath(filePath),
m_fileNumber(fileNumber){
}
void split(){
for (int i = 0; i < m_fileNumber; i++){
//...
float progressValue = m_fileNumber;
progressValue = (i + 1) / progressValue;
onProgress(progressValue);
}
}
void addIProgress(IProgress* iprogress){ //Attach
m_iprogressList.push_back(iprogress);
}
void removeIProgress(IProgress* iprogress){ //Detach
m_iprogressList.remove(iprogress);
}
protected:
virtual void onProgress(float value){ //Notify
List<IProgress*>::iterator itor=m_iprogressList.begin();
while (itor != m_iprogressList.end() )
(*itor)->DoProgress(value); //具体的DoProgress是在Observer里定义
itor++;
}
}
};
两个Observer
这边随便添加多少观察者,Subject对象是稳定不变的,可以支持任何观察者往里放
// 继承了Form, 以窗口的形式出现
class MainForm : public Form, public IProgress
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_Click(){
string filePath = txtFilePath->getText();
int number = atoi(txtFileNumber->getText().c_str());
ConsoleNotifier cn;
FileSplitter splitter(filePath, number);
splitter.addIProgress(this); //订阅通知
splitter.addIProgress(&cn) //订阅通知
splitter.split();
splitter.removeIProgress(this);
}
virtual void DoProgress(float value){
progressBar->setValue(value);
}
};
class ConsoleNotifier : public IProgress {
public:
virtual void DoProgress(float value){
cout << ".";
}
};
代码在被大量的重复,如何重构?
把继承拿下来,作为组合的形式
把编译时的东西变成了运行时的东西,把所有要组合的对象都变成基类的指针,下面去通过基类调用他们的方法
编译时让他们复用,运行时不一样
消除了代码的重复性
注意,虽然你不使用继承了,把他们变成了组合的形式。但是还是要继承他们最基础的那个类,才可以使用虚函数
非常特别地,既继承了基类,还有一个基类的字段成员
从编译时装配变为运行时装配
当所有子类都有同一个字段的时候,要把他们再抽象出一个基类出来,把字段给到他 -> 中间类
原来滥用继承导致复杂的,很多重复的代码:
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);//写文件流
//额外的加密操作...
//额外的缓冲操作...
}
};
把继承改成组合之后:
//业务操作
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;//...
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(){
//编译时装配
CryptoFileStream *fs1 = new CryptoFileStream();
BufferedFileStream *fs2 = new BufferedFileStream();
CryptoBufferedFileStream *fs3 =new CryptoBufferedFileStream();
}
void Process(){
//运行时装配
FileStream* s1=new FileStream();
//给CryptoStream构造的时候,用FileStream来构造stream指针
CryptoStream* s2=new CryptoStream(s1);
//给BufferedStream构造的时候,用FileStream来构造stream指针
BufferedStream* s3=new BufferedStream(s1);
/*
给s4构造的时候,用s2来构造,它是一个CryptoStream的对象,在调用它的Read函数时, 除了用了自己的额外Buffer部分,还用到了它的那个stream的Read函数, 它那是CryptoStream类型的Stream, 根据多态调用的是它自己的Read,它自己里本身除了有自己的Read外,还有FileStream的Read
*/
BufferedStream* s4=new BufferedStream(s2);
// 因为都是Stream的子类,所以可以一环套一换,调用函数的时候根据多态一直往下找
// 都是Stream的子类,所以就都能把指针赋给stream指针
}
提取出中间类,把有Stream指针的子类再抽象出一个基类出来,作为中间类DecoratorStream
//业务操作
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){
//写内存流
}
};
//扩展操作
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){
}
//...
};
ConcreteComponent 和 ConcreteDecoratorA/B 都是不稳定的
前者代表 FileStream、NetworkStream、MemoryStream
A代表:CryptoStream
B代表:BufferedStream
通过采用组合而非继承的手法, Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。
(同时继承又组合的,99%都是Decorator)、继承是为了接口、组合是为了实现
Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。
一个类里面放了不同的虚函数,他们是两个方向的,一个是平台实现,一个是业务抽象。
两个不同的变化方向带动的行为多态的实现也应该往不同方向走,不应该把他们放在一个类里。
将抽象部分(业务功能)与实现部分(平台实现)分离,使它们都可以独立地变化。
原来:
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(){}
};
//平台实现
//其实也不能只重写这一部分虚函数,因为上面整个都是纯虚函数
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(){
//==========
}
};
//业务抽象
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();
}
改为:
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(){}
};
//平台实现 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(){
//==========
}
};
//业务抽象 m
//类的数目:1+n+m
//他们这些子类都有成员MessagerImp* messagerImp;
//所以把这个成员提到了父类去
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、MessagerPerfect
实现类的继承:MobileMessagerImp、PCMessagerImp
如果存在四个变化非常强的维度,那么把另外三个再次打包成基类,在用三个指针指向它即可了
"对象创建"模式
通过“对象创建” 模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
依赖抽象,不应该依赖于实现细节
ISplitter* splitter = new BinarySplitter(filePath, number);
class MainForm : public Form
{
TextBox* txtFilePath;
TextBox* txtFileNumber;
ProgressBar* progressBar;
public:
void Button1_Click(){
ISplitter * splitter=
new BinarySplitter();//依赖具体类
splitter->split();
}
};
等号左边是依赖抽象,等号右边是依赖实现细节
如果是这样,MainForm 依赖 SplitterFactory, SplitterFactory依赖BinarySplitter,间接地,MainForm仍然要依赖BinarySplliter (编译时依赖,仍然绕不过)。
我们怎么改成运行时依赖?
把SplitterFactory里的Create函数改为虚函数,在定义一系列具体工厂去继承
在MainForm里定义 SplitterFactory* factory , 在构造函数里利用外接穿进来的指针初始化它。
class MainForm : public Form
{
SplitterFactory* factory;//工厂
public:
MainForm(SplitterFactory* factory){
this->factory=factory;
}
void Button1_Click(){
ISplitter * splitter=
factory->CreateSplitter(); //多态new
splitter->split();
}
};
MainForm再也没有依赖具体类了,只是依赖了抽象基类,至于MainForm以外的,不归我管
抽象类、工厂基类、具体类、具体工厂
//抽象类
class ISplitter{
public:
virtual void split()=0;
virtual ~ISplitter(){}
};
//工厂基类
class SplitterFactory{
public:
virtual ISplitter* CreateSplitter()=0;
virtual ~SplitterFactory(){}
};
//具体类
class BinarySplitter : public ISplitter{
};
class TxtSplitter: public ISplitter{
};
class PictureSplitter: public ISplitter{
};
class VideoSplitter: public ISplitter{
};
//具体工厂
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();
}
};
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟**(目的:解耦,手段:虚函数)**到子类。
Product -> ISplitter
ConcreteProduct -> BinarySp… VedioSp… ImageSp…
Creator -> SplitterFactory
ConcreteCreator -> BinarySplitterFactory、VedioSplitterFactory
在软件系统中,经常面临着“一系列相互依赖的对象”的创建工作;同时,由于需求的变化,往往存在更多系列对象的创建工作。
比如要创建一组connection、command、reader,如果你要创建SQL Server的,就必须这三个都是SQL Server的,不能加一个Oracle的进来,一组必须是保持一致的
如果使用Factory Method 方法:
//数据库访问有关的基类
class IDBConnection{
};
class IDBCommand{
};
class IDataReader{
};
//数据库访问有关抽象工厂的基类
class IDBConnectionFactory{
public:
virtual IDBConnection* CreateDBConnection()=0;
};
class IDBCommandFactory{
public:
virtual IDBCommand* CreateDBCommand()=0;
};
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()){
}
}
};
如果三个类特别有相关性,能否用一个工厂来创建?(高内聚)
class IDBFactory{
public:
virtual IDBConnection* CreateDBConnection()=0;
virtual IDBCommand* CreateDBCommand()=0;
virtual IDataReader* CreateDataReader()=0;
};
class SqlDBFactory:public IDBFactory{
public:
virtual IDBConnection* CreateDBConnection()=0;
virtual IDBCommand* CreateDBCommand()=0;
virtual IDataReader* CreateDataReader()=0;
};
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()){
}
}
};
提供一个接口(IDBFactory),让该接口负责创建一系列“相关或者相互依赖的对象”,无需指定它们具体的类。
红色表示稳定的,蓝色表示第一种变化,绿色表示第二种变化
蓝色:SQL Server的具体工厂、具体类A1、具体类B1
绿色:Oracle Server的具体工厂、具体类A2、具体类B2
如果没有应对“多系列对象构建”的需求变化,则没有必要使用Abstract Factory模式,这时候使用简单的工厂完全可以。
“系列对象”指的是在某一特定系列下的对象之间有相互依赖、或作用的关系。不同系列的对象之间不能相互依赖。
Abstract Factory模式主要在于应对“新系列”的需求变动。其缺点在于难以应对“新对象”的需求变动。
(你在抽象类内又加了新的虚函数,例如创建某DB的Writer、某DB的Listener…)
把工厂基类和抽象类合在一起了
通过克隆自己来创建对象
//抽象类
class ISplitter{
public:
virtual void split()=0;
virtual ISplitter* clone()=0; //通过克隆自己来创建对象(深克隆)
virtual ~ISplitter(){}
};
利用拷贝构造函数(需要提前把拷贝构造函数定义好)
//具体类
class BinarySplitter : public ISplitter{
public:
virtual ISplitter* clone(){
return new BinarySplitter(*this);
}
};
class TxtSplitter: public ISplitter{
public:
virtual ISplitter* clone(){
return new TxtSplitter(*this);
}
};
class PictureSplitter: public ISplitter{
public:
virtual ISplitter* clone(){
return new PictureSplitter(*this);
}
};
class VideoSplitter: public ISplitter{
public:
virtual ISplitter* clone(){
return new VideoSplitter(*this);
}
};
MainForm:原来里面是放Factory,现在改为放一个原型对象
放了原型对象只是帮你克隆用的,真正使用的时候是要创建新的对象的!
class MainForm : public Form
{
ISplitter* prototype;//原型对象
public:
MainForm(ISplitter* prototype){
this->prototype=prototype;
}
void Button1_Click(){
ISplitter * splitter=
prototype->clone(); //克隆原型
splitter->split();
}
};
当对象结构比较复杂的时候,工厂很难写,直接写原型更好
用工厂方法是很简单的几步就创建出来了,还是说很复杂
也是四种对象创建模式里的一种:
动机:
很像之前的Template method
如果在构造函数里调用虚函数, C++默认是静态绑定
(因为子类的构造函数先调用父类的构造函数,如果允许它动态绑定,它先调用子类override的虚函数,会表现为:子类的构造函数还没完成,就先调用了它其他的虚函数,违背对象基本伦理)
class House{
public:
void Init() //不能写成构造函数
{
this->BuildPart1();
for (int i = 0; i < 4; i++){
this->BuildPart2();
}
bool flag=this->BuildPart3();
if(flag){
this->BuildPart4();
}
this->BuildPart5();
}
virtual ~House(){}
protected:
virtual void BuildPart1()=0;
virtual void BuildPart2()=0;
virtual void BuildPart3()=0;
virtual void BuildPart4()=0;
virtual void BuildPart5()=0;
};
定义:将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)。
现在构建过程是确定了的,你现在只要定义子类继承House并且重写,然后动态调用即可。
当对象复杂时,你把Init函数和其他函数搅在一起显得不太好,因此拆分出一个新的类HouseDirector, 它里面放一个HouseBuilder的指针
Builder类里是放的单个的构建方法
把构建的函数的整体流程放到Director类里面
(Director 其实是可以和 Builder放一起的,根据复杂情况而定)
class House{
//....
};
class HouseBuilder {
public:
//通过Builder的GetResult得到结果
House* GetResult(){
return pHouse;
}
virtual ~HouseBuilder(){}
protected:
//这里塞了一个House的指针(可以去访问到House的内容了)
House* pHouse;
virtual void BuildPart1()=0;
virtual void BuildPart2()=0;
virtual void BuildPart3()=0;
virtual void BuildPart4()=0;
virtual void BuildPart5()=0;
};
class StoneHouse: public House{
};
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();
}
};
“对象性能”模式
保证一个类只存在一个实例,才能确保逻辑正确性以及良好的效率
把构造函数、拷贝构造函数都设置成私有的,让外界不能调用,内界调用。
声明一个静态变量和静态的成员函数
在对对象进行初始化时,如果考虑多线程直接初始化,是不安全的,一个已经进来了,另一个还在判断。
所以使用到了锁。但是对于只是想读取的线程,加锁是没有必要的代价很高
**-> 双检查锁(锁前检查一次,锁后检查一次)**锁前:避免了读取操作时的代价过高 锁后:避免了两个线程前后进来
如果只是锁后检查,代价过高
如果是是锁前检查,是不对的(见双检查锁里的注释)
//线程安全版本,但锁的代价过高
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; //a行
//这里为什么还要再判断一次?
//如果没有这个判断,两个线程一前一后进来a行,又会出现创建两次对象的情况
//第一个已经创建了,它等的拿到锁之后又创建了一遍
if (m_instance == nullptr) {
m_instance = new Singleton();
}
}
return m_instance;
}
常见的顺序:
先分配内存、再调用构造器、再把地址分配给m_instance
reorder之后:
先分配内存,再把地址给m_instance,再执行构造器
reorder之后,直接赋值了,另一个线程进来发现不是null,直接返回了,得到这个对象了,根本不能用
//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) {
// 保证不会reorder
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release);//释放内存fence
m_instance.store(tmp, std::memory_order_relaxed);
}
}
return tmp;
}
"对象性能"模式
运用共享技术有效地支持大量细粒度的对象
同一种Key,在对象池里只有一个对象
有就返回,没有就添加进去
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(){
//...
}
};
“ 接口隔离 ”模式
► 在组件构建过程中,某些接口之间直接的依赖常常会带来很多可题、 甚至根本无法实现。 采用添加一层间接(稳定)接口,来隔离 本来互相紧密关联的接口是一种常见的解决方案。(依赖倒置其实也是间接)
典型模式
Facade 加一层接口 ,外界只和接口打交道 内部也只和接口打交道
无论内部怎么变,外部交互的时候是不变的
把变化和稳定隔离开来
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();
//....
}
};
上面这样的方式不合适,可能因为某种原因(性能 / 安全控制 / 分布式),压根都拿不到RealSubject
class ISubject{
public:
virtual void process();
};
//Proxy的设计
class SubjectProxy: public ISubject{
// 可能这里也拿不到RealSubject的对象
public:
virtual void process(){
//对RealSubject的一种间接访问
//....
}
};
class ClientApp{
ISubject* subject;
public:
ClientApp(){
subject=new SubjectProxy();
}
void DoTask(){
//...
subject->process();
//....
}
};
直接调Subject是不合适的,通过替代调用Proxy
增加间接层,实现一些不为外界所知/累的功能
将一些现存的对象放在新的环境中使用
Adapter 类 继承自Target:我遵循你这个类定义的接口规范
Adapter 又还有组合,伸出来一个指针指向了Adaptee
Target -> 目标接口(新接口)
Adaptee -> 遗留接口(老接口)
Adapter 继承了 Target,也就是遵循了它接口的规范
拿一个旧的类塞到Adapter里面,当作面相新接口的实现
类似的,stack 和 queue 是直接把 deque放进去的(也是adapter,把一个老的接口转化成新的接口)
//目标接口(新接口)
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 OldClass{ //多继承
}
int main(){
IAdaptee* pAdaptee=new OldClass();
ITarget* pTarget=new Adapter(pAdaptee);
pTarget->process();
}
我们上面提到的是对象适配器,通过一个组合加继承实现的
类适配器是把组合的那个Adaptee通过protected继承来实现 (多继承)
public继承是公有接口,protected继承是实现继承,并没有继承接口,但是我用你的实现
如果这里只继承IAdaptee接口是不够的,因为它是虚的(纯虚接口)。所以我们需要继承一个明确的OldClass。
类适配器是定死的,没有灵活性了
//类适配器
class Adapter: public ITarget,
protected OldClass{ //多继承
}
多个对象互相关联,对象之间常常会维持一种复杂的引用关系,如果遇到一些需求的更改,这种直接的引用关系将面临不断的变化。
中介对象来封装变化,各个对象不需要显示相互引用(编译时依赖->运行时依赖)
Colleague之间没有相互依赖了,
而是他们去依赖Mediator,Mediator内部也在依赖Colleague
Mediator 和 Colleague 之间是双向依赖的关系
Fac;ade 解决的是系统外和系统内的隔离
Mediator 解决的是系统内各个对象之间的隔离
Facade 解决系统内和系统外
Proxy 解决两个可以直接依赖的对象(由于性能、安全性、分布式)必须隔离拆开、A访问B改为A访问Proxy,在Proxy内部再实现B的那些分布式、高性能、安全,完成对B的调用
Adapter 老接口和新接口不Match
“ 状态变化 ”模式
把状态相关的操作全部编码到一个状态对象里面了,代替使用枚举状态(那个不符合开闭原则)
虚函数的本质 -> 一个运行时的if-else
和Strategy的好处是异曲同工的 当状态增加的时候
下一个状态由你自己指定
NetworkProcessor是不需要变的,你新增的Wait你自己在内部定义好你的Operation123,你的下一个状态是谁,你自己指定好,至于你的下一个状态是谁我不关心,我只知道你有下一个状态。
允许一个对象在其内部状态改变时改变它的行为。从而使对象看起来似乎修改了其行为
修改前:
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(){
}
};
修改后:
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;
//...
}
};
只有一个方法的时候,和strategy是很像的
Context 是 Networkprocessor
State 是 NetworkState
ConcreteStateA/B 是 三种不同的状态:OpenState、CloseState、ConnectState
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可以将对象恢复到原先保存的状态。
(内存快照)-> 如果想实现高效的,需要序列化等技术
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);
}
Memento模式的核心:信息隐藏,即Originator需要向外界隐藏信息,保持其封装性。但同时有需要将状态保持到外界(Memento)。
深拷贝的内存快照。如果里面有指针,指针里还有指针… 具体在深拷贝的时候就很复杂,就使用序列化代替Memento
其实说白了就是用一种序列化的方式来实现深拷贝
“ 数据结构 ” 模式
把内部数据结构的访问封装进去
访问的时候:用多态的方式把树形结构的访问放在了树形结构的内部,而不是暴露在外面,外面的调用是一样的。在外面访问的时候,不论是根节点还是树节点还是叶子节点,我的处理是一样的。
将对象组合成树形结构以表示 ”部分-整体“ 的层次结构。Composite 使得用户对单个对象和组合对象的使用具有一致性 (稳定)
#include
#include
#include
#include
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(); //多态调用
//循环下来如果e还是树节点,就调用那个树节点的process方法
//如果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 leaf1("leaf1");
Leaf leaf2("leaf2");
root.add(&treeNode1);
treeNode1.add(&treeNode2);
treeNode2.add(&leaf1);
root.add(&treeNode3);
treeNode3.add(&treeNode4);
treeNode4.add(&leaf2);
//具有一致性,不论根节点,树节点,叶子节点,我的处理都是一致的
process(root);
process(leaf2);
process(treeNode3);
}
关于Add(Component)、Remove(Component)、GetChild(int)放在父类里还是子类里是有争议的
(叶子节点不可以Add、Remove、Get…)
我们上面实现的时候是通过给Composite加这个方法
但是未来要添加的时候,还得判断是不是Leaf,能不能用Add、… 也是不太好
个人理解放到下面是好的
注意在Composite里面的Operation 除了处理当前的子节点外还要处理它的子节点 (使用多态调用的方法)
注意这里的一对一和一对多
假如藏起来代码2
void process(){
//1. process current node
//例如对当前节点数值进行某种处理
隐藏这段
/*2. process leaf nodes
for (auto &e : elements)
e->process(); //多态调用
//循环下来如果e还是树节点,就调用那个树节点的process方法
//如果e是叶子节点,就调叶子节点的process
*/
}
那么在这里,就要实现,一会是一对一,一会是一对多。
要判断当前的Component是Leaf呢还是Composite呢?
如果是Composite,要取出它的子元素继续处理(一对多)
叶子节点,直接处理:一对一
这样还暴露了内部的数据结构
//客户程序
void Invoke(Component & c){
//...
c.process();
//...
}
如果现在这样,永远是一对一的关系,客户代码可以一致地处理对象和对象容器,无需关心是处理的单个对象还是组合对象的容器
" 数据结构 "模式
C++ STL 里已经有了迭代器
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;
}
}
泛型编程里的迭代器既可以++、–、还能+n
迭代器:通过迭代器来隔离算法和容器
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,知道有一个对象处理它为止。
#include
#include
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; }
//设置nextChain也可以用构造器实现
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(){
//建立了一个链表, h1指向h2, h2指向h3
Handler1 h1;
Handler2 h2;
Handler3 h3;
h1.setNextChain(&h2);
h2.setNextChain(&h3);
Reqest req("process task ... ", RequestType::REQ_HANDLER3);
h1.handle(req);
return 0;
}
逻辑是写在Handler里面,永远按链传播
运行时动态辨析哪一个处理者可以处理这个请求
” 行为变化 “ 模式
将一个请求(行为)封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
将命令/请求 对象化 就可以撤销 等等。
函数对象,可以重载**()**操作符
#include
#include
#include
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();
Command 是有接口规范的,比如在Base类里定义了是
virtual void execute(),而且它要有额外开销,有性能损失
函数对象的话,没有接口规范,但是更灵活,性能更高(它利用了模版,是编译时的绑定 因为用了模版 其实也是编译时多态)
函数对象应用更广泛,性能优先
在已经开发完成后,由于需求的变化,某些类层次结构常常需要增加新的行为(方法),如果直接在基类中做这样的更改,将会给子类带来很繁重的变更负担,甚至破坏原有设计。
Visitor 能预料到未来可能会给整个类层次结构添加新的操作,但是不知道要增加多少,增加什么操作。
double dispatch 二次多态辨析
表示作用于某个对象结构中的各元素的操作。使得可以在不改变(稳定)各元素的类的前提下定义(扩展)作用于这些元素的新操作(变化)。
添加新的操作都是扩展,满足开闭原则
//扩展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;
}
};
整体代码:
#include
using namespace std;
class Visitor;
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(){}
};
//==================================
//扩展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;
elementB.accept(visitor);// double dispatch
ElementA elementA;
elementA.accept(visitor);
return 0;
}
注意Visitor里有具体的VisitConcreteElementA/B/C的方法,因此,Element的子类也是固定的!
在定义Visitor的时候就定好了有几个方法(Element的子类)
这个子类常常是不能稳定地确定下来的
如果Element再加新的子类,那Visitor的基类也得改变,又打破了开闭原则
双重分发:
(1)调用accept,找子类的accept的实现 (第一次虚函数)
(2)进入accept后,又找到对应的visitor子类的实现,去调它的那个函数 (第二次虚函数)
给定一种语言,定义它的文法的一种表示,并定义一种解释器,这个解释器使用该表示来解释语言中的句子。
有终端表达式和中间节点表达式(非终端表达式)
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);
}
};
直接组合一个对象,带来高度的紧耦合
它们的内存模型都是被组合(继承)的在前面
如果放一个组合的对象,B就没有变化了,只能是它自己,不能是它的子类。如果传子类会产生对象切割
通过指针指向一个多态对象来达到松耦合