1、模板模式
概念
对于某一个业务逻辑(算法实现)在不同的对象中有不同的细节实现,但是逻辑(算法)的框架(或通用的应用算法)是相同的。Template 提供了这种情况的一个实现框架。Template 模式是采用继承的方式实现这一点:将逻辑(算法)框架放在抽象基类中,并定义好细节的接口,子类中实现细节。
实现一种反向控制的效果,符合依赖倒置原则,父类调用了子类的操作,子类实现父类申明的接口,控制权在父类。
局限性是,它的复用性不强,它采用了继承这一强制约束关系。
角色
- 抽象的模板类,定义通用的接口
- 若干个子类,各自实现父类定义的接口
代码实现
假设喝各种饮料是一种通用的业务逻辑,都有这么几个步骤,烧水、冲泡、倒入杯中、加小料,于是我设计一个含有这四个步骤的抽象类———喝饮料的模板,再去实现它的子类,也就是具体的喝不同饮料的过程。
#include <iostream>
using namespace std;
class DrinkTemplate{
public:
virtual void BoildWater()=0;
virtual void Brew()=0;
virtual void PourInCup()=0;
virtual void AddSomething()=0;
void Make(){
BoildWater();
Brew();
PourInCup();
AddSomething();
}
};
class Coffee:public DrinkTemplate{
virtual void BoildWater(){
cout<<"special coffee water"<<endl;
}
virtual void Brew(){
cout<<"brew coffee"<<endl;
}
virtual void PourInCup(){
cout<<"pour coffee"<<endl;
}
virtual void AddSomething(){
cout<<"add coffee"<<endl;
}
};
class Tea:public DrinkTemplate{
virtual void BoildWater(){
cout<<"special tea water"<<endl;
}
virtual void Brew(){
cout<<"brew tea"<<endl;
}
virtual void PourInCup(){
cout<<"pour tea"<<endl;
}
virtual void AddSomething(){
cout<<"add tea"<<endl;
}
};
int main(){
Tea* tea=new Tea;
tea->Make();
Coffee* coffee=new Coffee;
coffee->Make();
return 0;
}
2、策略模式
概念
策略模式是对算法的封装。处理一个问题的时候可能有多种算法,这些算法的接口(输入参数,输出参数等)都是一致的,那么可以考虑采用Strategy 模式对这些算法进行封装,在基类中定义一个函数接口就可以了。
优点:
算法可以自由切换。
避免使用多重条件判断。
扩展性良好。
缺点:
策略类会增多。
所有策略类都需要对外暴露
角色
- Strategy 策略基类,定义通用的接口
- ConcreteStrategy 若干个具体策略,继承自策略基类,各自实现基类声明的接口。
- Context 具体使用策略的对象,拥有一个策略基类的指针。
代码实现
模拟角色使用武器,首先定义一个使用武器的基类,其中声明了使用武器这个函数,然后定义几种具体武器,均继承自使用武器基类,重写其中的使用武器函数。定义一个角色类,角色类中拥有一个使用武器类的指针和相关的操作该指针的函数。
#include<iostream>
using namespace std;
class WeapenStrategy{
public:
virtual void UseWeapon()=0;
};
class Knife:public WeapenStrategy{
public:
virtual void UseWeapon(){
cout<<"use Knife"<<endl;
}
};
class Gun:public WeapenStrategy{
public:
virtual void UseWeapon(){
cout<<"use Gun"<<endl;
}
};
class Character{
public:
WeapenStrategy* pWeapon;
void setWeapon(WeapenStrategy* pWeapon){
this->pWeapon=pWeapon;
}
void ThrowWeapon(){
this->pWeapon->UseWeapon();
}
};
int main(){
Character* c=new Character;
WeapenStrategy* knife=new Knife;
WeapenStrategy* gun=new Gun;
c->setWeapon(knife);
c->ThrowWeapon();
c->setWeapon(gun);
c->ThrowWeapon();
delete c,knife,gun;
}
3、观察者模式
概念
建立一个一(Subject)对多(Observer)的依赖关系,并且做到当“一”变化的时候,依赖这个“一”的多也能够同步改变。最常见的一个例子就是:对同一组数据进行统计分析时候,我们希望能够提供多种形式的表示(例如以表格进行统计显示、柱状图统计显示、百分比统计显示等)。指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
优点:
观察者和被观察者是抽象耦合的。
建立一套触发机制。
缺点:
如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
角色
- 一个被观察的对象的抽象类,提供添加、删除、通知观察者的函数 。
- 若干个被观察对象的具体实现类,重写父类中的所有函数,并且还拥有一个存储观察者的容器。
- 一个观察者的抽象类,其中有一个更新函数。
- 若干个观察者的具体实现类, 重写各自的更新函数。
代码实现
模拟英雄打怪兽,我首先定义一个英雄的抽象类,其中声明一个更新的函数,再定义若干个具体的英雄类,重写更新函数。定义一个怪兽类的抽象类,其中声明了添加英雄、删除英雄、通知这几个函数,定义具体的怪兽类,重写怪兽抽象类中的函数,并且还拥有一个存储英雄抽象类的指针的容器,在通知的函数中遍历其中的英雄指针,调用他们的更新函数。
#include<iostream>
#include<list>
using namespace std;
class AbstructHero{
public:
virtual void Update()=0;
};
class HeroA:public AbstructHero{
public:
HeroA(){
cout<<"heroA"<<endl;
}
virtual void Update(){
cout<<"HeroA update"<<endl;
}
};
class HeroB:public AbstructHero{
public:
HeroB(){
cout<<"HeroB"<<endl;
}
virtual void Update(){
cout<<"heroB update"<<endl;
}
};
class AbstructBoss{
public:
virtual void addHero(AbstructHero* hero)=0;
virtual void deleteHero(AbstructHero* hero)=0;
virtual void notify()=0;
};
class BossA:public AbstructBoss{
public:
virtual void addHero(AbstructHero* hero){
plist.push_back(hero);
}
virtual void deleteHero(AbstructHero* hero){
plist.remove(hero);
}
virtual void notify(){
for(auto it=plist.begin(); it!=plist.end();++it){
(*it)->Update();
}
}
list<AbstructHero*> plist;
};
int main(){
AbstructHero *ha=new HeroA;
AbstructHero *hb=new HeroB;
AbstructBoss *bossA=new BossA;
bossA->addHero(ha);
bossA->addHero(hb);
bossA->deleteHero(hb);
bossA->notify();
return 0;
}
4、备忘录模式
概念
我们对一些关键性的操作肯定需要提供诸如撤销(Undo)的操作。那这个后悔药就是 Memento 模式提供的。
Memento 模式的关键就是要在不破坏封装行的前提下,捕获并保存一个类的内部状态,这样就可以利用该保存的状态实施恢复操作。
优点:
给用户提供了一种可以恢复状态的机制,可以使用户能够比较方便地回到某个历史的状态。
实现了信息的封装,使得用户不需要关心状态的保存细节。
缺点:消耗资源。如果类的成员变量过多,势必会占用比较大的资源,而且每一次保存都会消耗一定的内存。
角色
- 一个备忘录类,拥有一份某个时刻原件中的内容
- 一个原件类,拥有一份某种内容,提供返回内容、设置内容、返回一份备忘录、根据备忘录还原内容几种操作
- 一个备忘录管理类,拥有一个存储备忘录的容器,提供添加和返回备忘录的功能。
代码实现
模拟一个带节点还原功能的记事本,原件和备忘录类中保存的内容为字符串类型。
#include<string>
#include<iostream>
#include<vector>
using namespace std;
class Menmoto{
public:
explicit Menmoto(const string state):state(state){
}
string getState()const{
return state;
}
private:
string state;
};
class Originator{
public:
string getState()const{
return state;
}
void setState(string state){
this->state=state;
}
Menmoto SaveStateMenmoto(){
return Menmoto(state);
}
void getStateFromMenmoto(Menmoto menmoto){
state=menmoto.getState();
}
private:
string state;
};
class Caretaker{
public:
void add(Menmoto menmoto){
mementoList.push_back(menmoto);
}
Menmoto get(int index){
return mementoList[index];
}
private:
vector<Menmoto>mementoList;
};
int main(){
Originator originator;
Caretaker caretaker;
originator.setState("state1,num=100");
caretaker.add(originator.SaveStateMenmoto());
cout<<"current state: "<<originator.getState()<<endl;
originator.setState("state2,num=200");
caretaker.add(originator.SaveStateMenmoto());
cout<<"current state: "<<originator.getState()<<endl;
cout<<"back to state1"<<endl;
originator.getStateFromMenmoto(caretaker.get(0));
cout<<"current state:"<<originator.getState();
return 0;
}