设计模式是指在软件开发中,经过验证的,⽤于解决在特定环境下,重复出现的,特定问题的解决⽅案。个人认为,简单来说,就是套路。
设计模式不能滥用,如果没有明确的场景,就不要使用设计模式。在后面的了解可以知道,设计模式是需要有变化点和稳定点的,如果找不出稳定点或变化点,也不要使用。
模式设计原则
在讲解设计模式之前,要先了解模式设计的原则。这是因为,设计原则比设计模式更重要,设计模式是对设计原则的应用。所以,其实掌握了设计原则,设计模式的学习就会较为轻松。
当然,两者是相互印证的关系,而不是相互独立的,这一点需要注意。
1.依赖倒置原则
这是八个设计原则中最重要的一条。一句话说,就是变化点应该依赖稳定点。变化应该隔离出来。
具体说来,对于可能变化的点,应该抽离出一个不变的稳定点。例如,在生产者和消费者之间,抽离出一个不变的标准,这样,不论生产者和消费者的流程怎样变化,都要依赖这个不变的标准,能够将生产者和消费者之间的变化给隔离掉。
2.开放封闭原则
对扩展开放,对修改关闭。
这里提一下,扩展有两种方式——继承和组合。继承不用解释。组合是在类中声明指针而不是实例,可以起到类似晚绑定的效果。值得注意的是,在设计模式当中,使用组合的时候都是组合的抽象基类。
3.面向接口编程
不将变量类型声明为某个特定的具体类,⽽是声明为某个接⼝。这样做,可以减少系统中各部分的依赖关系,从⽽实现“⾼内聚、松耦合”的类型设计⽅案。
4.封装变化点
这是面向对象编程中最重要的一点。意思是将稳定点和变化点隔离出来。注意不是要消灭掉变化点,而是将变化点封装起来,限制在一个很小的范围里。
5.单一职责原则
简单理解,一个类就一个用处。
6.里氏替换原则
子类在实现的时候,一定也要实现父类的职责。一句话,父亲立的flag子类一定要实现。
7.接口隔离原则
这个也不解释,就是要正确使用限定词:public、protected、private和friend。
8.对象组合优于类继承
从上面的图片能看出来,继承耦合度⾼,组合耦合度低。一句话,继承不能换爹,组合可以换爹。
设计模式
还是那句话,使用设计模式一定要慎重,一定要都找到变化点和稳定点。如果没有稳定点,可以尝试用脚本语言。
另外,也不要一开始就使用设计模式,因为这样编程,会很不方便,而是应该从重构中慢慢转变为设计模式。重构的方法:静态转变为动态,早绑定转变为晚绑定,继承转变为组合,编译时依赖转变为运⾏时依赖。
紧耦合转变为松耦合;
好。下面开始介绍部分设计模式,其他没有提到的设计模式,可以根据其他文章,结合八条原则,琢磨出来。此外,单例模式另开一篇文章介绍。
1.模板方法
定义⼀个操作中的算法的⻣架 ,⽽将⼀些步骤延迟到⼦类中。在这个设计模式中,主要用到了单一职责原则、接口隔离原则和依赖倒置原则。使得⼦类可以不改变算法的结构即可重定义该算法的某些特定步骤。 这个设计模式对依赖倒置原则体现得较为明显,是用得最多的设计模式,也是很难误用的设计模式,是反向调用的典例。意思就是,根据框架去调用应用程序,而不是应用程序去调用框架。我们是在已有框架的基础上去写代码。一句话总结,通过固定算法⻣架来约束⼦类的⾏为。
下面贴出两版代码,分别是没有使用模板方法和使用了模板方法的代码,可以从中对比找出差别。
这是没有使用设计模式的代码
#include<iostream>
using namespace std;
class Base
{
public:
void fun1();
void fun3();
};
void Base::fun1()
{
cout<<"Base:fun1"<<endl;
}
void Base::fun3()
{
cout<<"Base:fun3"<<endl;
}
class Son1
{
public:
void fun2();
void fun3();
};
void Son1::fun2()
{
cout<<"Son2:fun2"<<endl;
}
void Son1::fun3()
{
cout<<"Son1:fun3"<<endl;
}
int main()
{
Base *b=new Base;
Son1 *s1=new Son1;
b->fun1();
b->fun3();
cout << "--------------" << endl;
s1->fun2();
s1->fun3();
return 0;
}
改进后
#include<iostream>
using namespace std;
class Base
{
public:
void fun();
protected:
virtual void fun1();
virtual void fun2();
virtual void fun3();
};
void Base::fun()
{
fun1();
fun2();
fun3();
}
void Base::fun1()
{
cout<<"Base:fun1"<<endl;
}
void Base::fun2()
{
cout<<"Base:fun2"<<endl;
}
void Base::fun3()
{
cout<<"Base:fun3"<<endl;
}
class Son1:public Base
{
protected:
virtual void fun1();
void fun3();//子类中的虚函数重写也可以不写virtual
};
void Son1::fun1()
{
cout<<"Son1:fun1"<<endl;
}
void Son1::fun3()
{
cout<<"Son1:fun3"<<endl;
}
class Son2:public Base
{
protected:
virtual void fun2();//依然建议重写虚函数时写明virtual,避免混淆
};
void Son2::fun2()
{
cout<<"Son2:fun2"<<endl;
}
int main()
{
Base *b=new Base;
Base *s1=new Son1;
Base *s2=new Son2;
b->fun();
cout<<"--------------"<<endl;
s1->fun();
cout<<"--------------"<<endl;
s2->fun();
return 0;
}
2.观察者模式
当⼀个对象的状态发⽣改变时,所有依赖于它的对象都得到通知并⾃动更新。它类似于发布—订阅模式。这样,就让观察者和被观察者之间的耦合度降低,达到了送耦合的效果。这里要注意,观察者和被观察者都不要依赖通知顺序。观察者模式在分布式架构中应用较多。一句话总结,就是触发了观察者和被观察者间的联动。
这里还是贴出两版代码来对比。
这是没有使用设计模式的代码
#include<iostream>
using namespace std;
class ObserveA
{
public:
void show(int key);
};
void ObserveA::show(int key)
{
cout<<"ObserveA:"<<key<<endl;
}
class ObserveB
{
public:
void show(int key);
};
void ObserveB::show(int key)
{
cout<<"ObserveB:"<<key<<endl;
}
class Center
{
public:
int getKey();
};
int Center::getKey()
{
return rand()%63;
}
int main()
{
ObserveA *oa=new ObserveA;
ObserveB *ob=new ObserveB;
Center *ce=new Center;
int key=ce->getKey();
oa->show(key);
ob->show(key);
return 0;
}
改进后的代码
#include<iostream>
#include<vector>
using namespace std;
class Observer
{
public:
virtual void show(int key)=0;
virtual ~Observer(){}//必须要写虚析构函数,否则子类没有释放
};
class ObserveA:public Observer
{
public:
virtual void show(int key);
};
void ObserveA::show(int key)
{
cout<<"ObserveA:"<<key<<endl;
}
class ObserveB:public Observer
{
public:
virtual void show(int key);
};
void ObserveB::show(int key)
{
cout<<"ObserveB:"<<key<<endl;
}
class Center
{
public:
void attach(Observer *ob);
void detach(Observer *ob);
void notify();
private:
int getKey();
vector<Observer *> obs;
};
void Center::attach(Observer *ob)
{
obs.push_back(ob);
}
void Center::detach(Observer *ob)
{
for(auto it=obs.begin();it!=obs.end();++it)
{
if((*it)==ob)
{
obs.erase(it);
return;
}
}
cout<<"Observer not found!"<<endl;
}
void Center::notify()
{
int key=getKey();
for(auto it=obs.begin();it!=obs.end();++it)
{
(*it)->show(key);
}
}
int Center::getKey()
{
return rand()&63;
}
int main()
{
Observer *oba=new ObserveA;
Observer *obb=new ObserveB;
Center *ce=new Center;
ce->attach(oba);
ce->attach(obb);
ce->notify();
cout<<"------------"<<endl;
ce->detach(oba);
ce->notify();
return 0;
}
3.策略模式
策略模式比较简单。主要是对if-else较多的情况使用。因为条件判断太多,会造成类太复杂不稳定,而且对于拓展也不友好。所以,抽象出来一个类,把这些条件判断的语句抽象成一个统一的接口,是一个解耦合的过程。主要是用到了单一职责原则和开放封闭原则。一句话,分离算法,选择实现。
还是先贴出没有使用设计模式的代码。
enum situations
{
s_a,
s_b,
s_c
};
class Display
{
public:
void fun();
Display()
{
s=s_a;
}
private:
situations s;
};
void Display::fun()
{
if(s==s_a)
{
cout<<"s_a"<<endl;
}else if(s==s_b)
{
cout<<"s_b"<<endl;
}else if(s==s_c)
{
cout<<"s_c"<<endl;
}
/*
if-else没完没了
*/
}
下面给出使用策略模式的代码
class Strategy
{
public:
virtual void fun();
virtual ~Strategy(){}//为了释放子类,不再赘述
};
class S_a
{
public:
void fun()
{
cout<<"s_a"<<endl;
}
};
class S_b
{
public:
void fun()
{
cout<<"s_b"<<endl;
}
};
这样就可以做到对扩展友好。对于扩展的类,可以写到不同的cpp文件中,这样代码看出来会更清爽一些。
值得注意的是,策略模式有个缺陷。如果扩展的类过多,那么会生成很多小类,影响开发。所以策略模式是针对if-else不那么多的情况。
4.责任链模式
责任链模式将对象连成⼀条链,并沿着这条链传递请求,直到有⼀个对象处理它为⽌。 责任链模式也可以消灭点if-else,也可以做到解耦合。一句话,分离职责,动态组合。
还是先看一下if-else的代码。
#include<iostream>
using namespace std;
struct Info
{
int key;
int condition;
};
class Request
{
public:
int handle(Info *in);
};
int Request::handle(Info *in)
{
if(in->condition<=5)
{
return in->key*5;
}else if(in->condition<=10)
{
return in->key*10;
}
return in->key*7;
}
int main()
{
Request quest;
Info in={3,8};
int key=quest.handle(&in);
cout<<key<<endl;
return 0;
}
使用责任链模式后
#include<iostream>
using namespace std;
struct Info
{
int key;
int condition;
};
class Handler
{
public:
virtual ~Handler(){}//不要忘了
bool setNext(Handler *hand);
int handle(Info *in);
protected:
virtual bool canHandle(Info *in)=0;
Handler *getNext();
virtual int calc(Info *in)=0;
private:
Handler *next;
};
bool Handler::setNext(Handler *hand)
{
next=hand;
return true;
}
Handler *Handler::getNext()
{
return next;
}
int Handler::handle(Info *in)
{
if(canHandle(in))
{
return calc(in);
}else if(getNext())
{
return getNext()->handle(in);
}
cout<<"handle:no next"<<endl;
return -1;
}
class Request1:public Handler
{
protected:
virtual bool canHandle(Info *in);
virtual int calc(Info *in);
};
bool Request1::canHandle(Info *in)
{
if(in->condition<5)
{
return true;
}
return false;
}
int Request1::calc(Info *in)
{
return in->key*5;
}
class Request2:public Handler
{
protected:
virtual bool canHandle(Info *in);
virtual int calc(Info *in);
};
bool Request2::canHandle(Info *in)
{
if(in->condition<10)
{
return true;
}
return false;
}
int Request2::calc(Info *in)
{
return in->key*10;
}
class Request3:public Handler
{
protected:
virtual bool canHandle(Info *in);
virtual int calc(Info *in);
};
bool Request3::canHandle(Info *in)
{
return true;
}
int Request3::calc(Info *in)
{
return in->key*7;
}
int main()
{
Request1 quest1;
Request2 quest2;
Request3 quest3;
quest1.setNext(&quest2);
quest2.setNext(&quest3);
Info in={3,8};
int key=quest1.handle(&in);
cout<<key<<endl;
return 0;
}
责任链模式主要体现了单一职责原则和依赖倒置原则。此外,责任链模式还可以与模板模式结合,把子类中相同的操作的都抽象出来,这里就不再抽象了。在这里还要多说一句,不建议使用责任链,因为责任链较为过时,C++中也有相应的结构可以替代责任链。
另外,责任链扩展一下就是功能链,责任链是处理了就返回,不再继续处理,而功能链强调的是,⼀个请求依次经由功能链中的⼦处理流程处理,一个子流程了还会继续向下传递。功能链的例子是nginx中十一个状态的判断与处理。
5.装饰器模式
装饰器模式把⼀系列复杂的功能分散到每个装饰器当中,⼀般⼀个装饰器只实现⼀个功能,实现复⽤装饰器的功能。装饰器模式易与责任链模式混淆。主要记住两点区别,一是装饰器基类有一个默认值,不是责任链中的抽象类;二是责任链通过自己设计的接口传递指针,而装饰器通过构造函数传递指针。装饰器类似功能链,自己处理完还要交给下一个。一句话总结,装饰器就是动态组合。体现了单一职责原则和开放封闭原则。
还是先贴出if-else代码。
#include<iostream>
using namespace std;
struct Context
{
int base;
int isExtra;
};
class Sum
{
public:
int calc(Context *ctx);
private:
int extra1(Context *ctx);
int extra2(Context *ctx);
int extra3(Context *ctx);
};
int Sum::calc(Context *ctx)
{
int base=ctx->base;
int e=ctx->isExtra;
if(e>=1)
{
base+=extra1(ctx);
}
if(e>=2)
{
base+=extra2(ctx);
}
if(e>=3)
{
base+=extra3(ctx);
}
return base;
}
int Sum::extra1(Context *ctx)
{
return ctx->base;
}
int Sum::extra2(Context *ctx)
{
return ctx->base*2;
}
int Sum::extra3(Context *ctx)
{
return ctx->base*3;
}
int main()
{
Context ctx={5,4};
Sum s;
cout<<s.calc(&ctx)<<endl;
return 0;
}
使用装饰器后。
#include<iostream>
using namespace std;
struct Context
{
int base;
int isExtra;
};
class Base
{
public:
Base(Base *b=NULL){}
virtual ~Base(){}
virtual int calc(Context *ctx)
{
return 1000;
}
protected:
Base *base;
};
class Extra1:public Base
{
public:
Extra1(Base *b){base=b;}
virtual int calc(Context *ctx);
};
int Extra1::calc(Context *ctx)
{
return ctx->base+base->calc(ctx);
}
class Extra2:public Base
{
public:
Extra2(Base *b){base=b;}
virtual int calc(Context *ctx);
};
int Extra2::calc(Context *ctx)
{
return ctx->base*2+base->calc(ctx);
}
int main()
{
Context ctx={5,4};
Base *b=new Base;
Extra1 *e1=new Extra1(b);
Extra2 *e2=new Extra2(e1);
cout<<e2->calc(&ctx)<<endl;
return 0;
}
从代码中可以看出来,装饰器使用了递归,先一层层进去,调用函数之后再一层层出来。
6.工厂方法模式
工厂方法模式主要解决的是创建过程复杂,并且对外想要隐藏这些接口的问题。举一个例子,对于线程池而言,不需要用户去了解到底需要创建多少个线程,这里就可以用到工厂方法模式,去屏蔽这些细节。一句话总结,就是延迟到子类中去实现。
#include<iostream>
using namespace std;
struct Context
{
int key;
int count;
};
class Base
{
public:
virtual int fun(Context *ctx)=0;
virtual ~Base(){}
};
class Son1:public Base
{
public:
virtual int fun(Context *ctx);
};
int Son1::fun(Context *ctx)
{
++ctx->count;
return ctx->key+1;
}
class Son2:public Base
{
public:
virtual int fun(Context *ctx);
};
int Son2::fun(Context *ctx)
{
++ctx->count;
return ctx->key+2;
}
int main()
{
int type=1;
Context ctx={3,4};
if(type==1)
{
cout<<"------1------"<<endl;
Base *s1=new Son1;
cout<<s1->fun(&ctx)<<endl;
cout<<ctx.count<<endl;
}else if(type==2)
{
cout<<"------2------"<<endl;
Base *s2=new Son2;
cout<<s2->fun(&ctx)<<endl;
cout<<ctx.count<<endl;
}
return 0;
}
这是一版没有使用工厂方法模式的代码。这里还是有很多if-else的。
#include<iostream>
using namespace std;
struct Context
{
int key;
int count;
};
class Base
{
public:
virtual int fun(Context *ctx)=0;
virtual ~Base(){}
};
class Son1:public Base
{
public:
virtual int fun(Context *ctx);
};
int Son1::fun(Context *ctx)
{
++ctx->count;
return ctx->key+1;
}
class Son2:public Base
{
public:
virtual int fun(Context *ctx);
};
int Son2::fun(Context *ctx)
{
++ctx->count;
return ctx->key+2;
}
class SonFactory
{
public:
virtual Base *form()=0;
};
class Son1Factory:public SonFactory
{
public:
Base *form()
{
Base *b=new Son1;
return b;
}
};
class Son2Factory:public SonFactory
{
public:
Base *form()
{
Base *b=new Son2;
return b;
}
};
class FunFactory
{
public:
FunFactory(SonFactory *factory)
{
this->factory=factory;
}
~FunFactory()
{
if(factory)
{
delete factory;
factory=NULL;
}
}
int func(Context *ctx)
{
Base *b=factory->form();
return b->fun(ctx);
}
SonFactory *factory;
};
int main()
{
Context ctx={3,4};
FunFactory *sf=new FunFactory(new Son2Factory);
cout<<sf->func(&ctx)<<endl;
return 0;
}
这里需要再强调一下模式设计原则的重要性。如果写的代码一开始就符合模式设计原则,那么在以后代码的迭代中,可以不用修改原先的代码,扩展较为轻松。这里第一版的代码就没有动,直接在下面加了工厂方法模式。
这一版代码封装了类,去掉了if-else,依赖了抽象。当然,还有可以改进的空间,可以用模板方法,把变化点放到protected中,让子类去扩展。
#include<iostream>
using namespace std;
struct Context
{
int key;
int count;
};
class Base
{
public:
virtual int fun(Context *ctx)=0;
virtual ~Base(){}
};
class Son1:public Base
{
public:
virtual int fun(Context *ctx);
};
int Son1::fun(Context *ctx)
{
++ctx->count;
return ctx->key+1;
}
class Son2:public Base
{
public:
virtual int fun(Context *ctx);
};
int Son2::fun(Context *ctx)
{
++ctx->count;
return ctx->key+2;
}
class SonFactory
{
public:
SonFactory()
{
base=NULL;
}
~SonFactory()
{
if(base)
{
delete base;
base=NULL;
}
}
int func(Context *ctx)
{
if(base==NULL)
{
base=form();
}
return base->fun(ctx);
}
Base *base;
protected:
virtual Base *form()=0;
};
class Son1Factory:public SonFactory
{
protected:
Base *form()
{
Base *b=new Son1;
return b;
}
};
class Son2Factory:public SonFactory
{
protected:
Base *form()
{
Base *b=new Son2;
return b;
}
};
int main()
{
Context ctx={3,4};
SonFactory *sf=new Son2Factory;
cout<<sf->func(&ctx)<<endl;
delete sf;
return 0;
}
这里想说的是,如果算法骨架固定,可以把骨架上移到基类。
7.抽象工厂模式
抽象工厂模式与工厂方法模式差不多但是有一些区别。工厂方法模式是创建一个对象,而抽象工厂模式是创建一系列的,而且是相关的或者相互依赖的对象。所以,抽象工厂模式需要把它们之间的依赖也屏蔽掉。
#include<iostream>
using namespace std;
struct Context
{
int key;
int count;
};
class Base
{
public:
virtual int fun(Context *ctx)=0;
virtual ~Base(){}
};
class Son1:public Base
{
public:
virtual int fun(Context *ctx);
};
int Son1::fun(Context *ctx)
{
++ctx->count;
return ctx->key+1;
}
class Son2:public Base
{
public:
virtual int fun(Context *ctx);
};
int Son2::fun(Context *ctx)
{
++ctx->count;
return ctx->key+2;
}
class InBase
{
public:
virtual bool fun(Context *ctx,int key)=0;
virtual ~InBase(){}
};
class InSon1:public InBase
{
public:
virtual bool fun(Context *ctx,int key)
{
++ctx->count;
ctx->key=key;
cout << "InSon1:fun" << endl;
return true;
}
};
class InSon2:public InBase
{
public:
virtual bool fun(Context *ctx,int key)
{
++ctx->count;
ctx->key+=key;
cout << "InSon2:fun" << endl;
return true;
}
};
class SonFactory
{
public:
SonFactory()
{
base=NULL;
inbase=NULL;
}
~SonFactory()
{
if(base)
{
delete base;
base=NULL;
}
if(inbase)
{
delete inbase;
inbase=NULL;
}
}
int func(Context *ctx)
{
if(base==NULL)
{
base=form();
}
return base->fun(ctx);
}
bool inFunc(Context *ctx,int key)
{
if(inbase==NULL)
{
inbase=inForm();
}
return inbase->fun(ctx,key);
}
Base *base;
InBase *inbase;
protected:
virtual Base *form()=0;
virtual InBase *inForm()=0;
};
class Son1Factory:public SonFactory
{
protected:
Base *form()
{
Base *b=new Son1;
return b;
}
InBase *inForm()
{
InBase *b=new InSon1;
return b;
}
};
class Son2Factory:public SonFactory
{
protected:
Base *form()
{
Base *b=new Son2;
return b;
}
InBase *inForm()
{
InBase *b=new InSon2;
return b;
}
};
int main()
{
Context ctx={3,4};
SonFactory *sf=new Son2Factory;
cout<<sf->func(&ctx)<<endl;
cout<<"----1----"<<endl;
sf->inFunc(&ctx,9);
cout<<sf->func(&ctx)<<endl;
delete sf;
return 0;
}
8.适配器模式
适配器将⼀个类的接⼝转换成客户希望的另⼀个接⼝,主要用于处理接口不兼容的问题。一般使用在两个项目合并或是原先接口已定义好但是调用者用了不同的调用方式的时候。另外,适配器模式通过继承的方式去扩展了原来稳定的接口,去组合新加的功能。一句话总结,转换匹配,复⽤功能。
#include<iostream>
using namespace std;
class Base
{
public:
int calc(int a,int b)
{
return a+b;
}
};
//基类只有加,子类却要加减乘除
class Son:public Base
{
public:
int add(int a,int b)
{
return calc(a,b);
}
int miu(int a,int b)
{
return calc(a,-b);
}
int mul(int a,int b)
{
if(a==0||b==0)
{
return 0;
}
int c=abs(b);
int res=a;
for(int i=2;i<=c;++i)
{
res=calc(res,a);
}
if(b<0)
{
return calc(0,-res);
}
return res;
}
int div(int a,int b)
{
if(b==0)
{
cout<<"/0"<<endl;
return INT_MAX;
}
int res=0;
int c=abs(b);
while(mul(c,res)<=a)
{
++res;
}
--res;
if(b<0)
{
return calc(0,-res);
}
return res;
}
};
int main()
{
Son s;
cout<<s.add(7,5)<<endl;
cout<<s.miu(7,5)<<endl;
cout<<s.mul(7,5)<<endl;
cout<<s.div(7,5)<<endl;
return 0;
}
9.代理模式
代理模式为其他对象提供⼀种代理以控制对这对象的访问。代理模式一般有以下几个用处。
一是,代理的如果是远程对象,需要对远程对象进行控制访问,就可以在分布式系统中构造一个代理类,可以不用去修改具体的类,避免影响接口的纯粹性。
二是虚代理,可以用于延迟加载。举一个例子,对于一些数据访问,可以先只加载基础数据,点详情页再加载详情。
三是保护代理,代理对象前后需要做一些操作,这与工厂方法模式有点类似。
一句话总结一下,隔离变化点,控制对象访问。
class ISubject {
public:
virtual void Handle() = 0;
virtual ~ISubject() {}
};
// 真实类,该类在当前进程,也可能在其他进程当中
class RealSubject : public ISubject {
public:
virtual void Handle() {
// 只完成功能相关的操作,不做其他模块的判断
}
};
// 本地,在当前进程当中 只会在某个模块中使用
class Proxy1 : public ISubject {
public:
Proxy1(ISubject *subject) : _subject(subject) {}
virtual void Handle() {
// 在访问 RealSubject 之前做一些处理
//if (不满足条件)
// return;
_subject->Handle();
count++;
// 在访问 RealSubject 之后做一些处理
}
private:
ISubject* _subject;
static int count;
};
int Proxy1::count = 0;//静态成员变量需要初始化
// 在分布式系统当中 skynet actor
class Proxy2 : public ISubject {
public:
virtual void Handle() {
// 在访问 RealSubject 之前做一些处理
// 发送到数据到远端 网络处理 同步非阻塞 ntyco c协程
//IResult * val = rpc->call("RealSubject", "Handle");
// 在访问 RealSubject 之后做一些处理
}
private:
/*void callback(IResult * val) {
// 在访问 RealSubject 之后做一些处理
}*/
};
注意与适配器区分。适配器的接口可能都与基类不一样,实现形式是继承稳定的函数,把变化交给组合类去扩展新的功能。代理模式只是对稳定接口的隔离。
再讲一下代理、适配器、装饰器的区别与联系。三者相同点都是在不改变原有类的基础上通过聚合的方式进行扩展。不同点在于它们的扩展方式有很大不同,适配器是转换,装饰器是增强,代理模式是控制。
最后再啰嗦几句。一是上面所有的例子其实都很简单,不代表真实应用场景,只是体现相应的思想,不要以为实际应用中很简单,特别是觉得一些设计模式没有必要,那是因为没有在具体复杂的场景下去使用。另外,还要再提一下模式设计原则。如果在开发过程中不能准确判断变化点,也要在开发时遵循设计原则。因为在符合模式设计原则的情况下,扩展代码非常方便。所以,当对代码越来越熟悉之后,知道如何使用设计模式了之后,扩展代码会非常容易。