1.观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都要得到通知并自动更新。
主要解决:一个对象更新,其它对象也要更新。
如何解决:目标类通知函数通知所有观察者自动更新。
关键代码:在目标类中增加一个ArrayList来存放观察者们。
//数据模型为目标类,视图为观察者类。当数据模型发生改变时,通知视图类更新
class View;
class DataModel //目标抽象类 数据模型
{
public:
virtual ~DataModel(){}
virtual void add(View* view) = 0;
virtual void remove(View* view) = 0;
virtual void notify() = 0; //通知函数
};
class View //观察者抽象类 视图
{
public:
virtual ~View(){ cout << "~View()" << endl; }
virtual void update() = 0;
};
class IntModel:public DataModel //具体的目标类, 整数模型
{
public:
~IntModel()
{
clear();
}
void add(View* view)
{
auto iter = std::find(m_list.begin(), m_list.end(), view); //判断是否重复添加
if(iter == m_list.end())
{
m_list.push_back(view);
}
}
void remove(View* view)
{
auto iter = m_list.begin();
for(;iter != m_list.end(); iter++)
{
if(*iter == view)
{
delete *iter; //释放内存
m_list.erase(iter); //删除元素
break;
}
}
}
void notify() //通知观察者更新
{
auto iter = m_list.begin();
for(; iter != m_list.end(); iter++)
{
(*iter)->update();
}
}
private:
void clear()
{
if(!m_list.empty())
{
auto iter = m_list.begin();
for(;iter != m_list.end(); iter++) //释放内存
{
delete *iter;
}
}
}
private:
list<View*> m_list;
};
class TreeView:public View //具体的观察者类 视图
{
public:
TreeView(string name):m_name(name),View(){}
~TreeView(){ cout << "~TreeView()" << endl; }
void update()
{
cout << m_name.data() << " : Update" << endl;
}
private:
string m_name;
};
int main()
{
View* v1 = new TreeView("view1");
View* v2 = new TreeView("view2");
View* v3 = new TreeView("view3");
View* v4 = new TreeView("view4");
DataModel* model = new IntModel;
model->add(v1);
model->add(v2);
model->add(v3);
model->add(v2);
model->add(v4);
model->notify();
cout << "----------" << endl;
model->remove(v2);
model->notify();
delete model;
return 0;
}
2.代理模式:为其它对象提供一种代理以控制对这个对象的访问。
主要解决:在直接访问对象时带来的问题,比如:要访问的对象在远程服务器上。在面向对象系统中,有些对象由于某些原因,直接访问会给使用者或系统带来很多麻烦,可以在访问此对象时加上一个对此对象的访问层。
如何解决:增加中间代理层。
关键代码:实现与被代理类组合。
class Gril
{
public:
Gril(string name = "gril"):m_string(name){}
string getName()
{
return m_string;
}
private:
string m_string;
};
class Profession
{
public:
virtual ~Profession(){}
virtual void profess() = 0;
};
class YoungMan:public Profession
{
public:
YoungMan(Gril gril):m_gril(gril){}
void profess()
{
cout << "Young man love " << m_gril.getName().data() << endl;
}
private:
Gril m_gril;
};
class ManProxy:public Profession
{
public:
ManProxy(Gril gril):m_man(new YoungMan(gril)){}
void profess()
{
cout << "I am Proxy" << endl;
m_man->profess();
}
private:
YoungMan* m_man;
};
int main(int argc, char *argv[])
{
Gril gril("hei");
Profession* proxy = new ManProxy(gril);
proxy->profess();
delete proxy;
return 0;
}
3.模板模式:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
主要解决:多个子类有相同的方法,并且逻辑相同,细节有差异。
如何解决:对重要,复杂的算法,将核心算法设计为模板方法,周边细节由子类实现,重构时,经常使用的方法,将相同的代码抽象到父类,通过钩子函数约束行为。
关键代码:在抽象类实现通用接口,细节变化在子类实现。
缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
class Computer
{
public:
void product()
{
installCpu();
installRam();
installGraphicsCard();
}
protected:
virtual void installCpu() = 0;
virtual void installRam() = 0;
virtual void installGraphicsCard() = 0;
};
class ComputerA:public Computer
{
protected:
void installCpu() override
{
cout << "ComputerA install Inter Core i5" << endl;
}
void installRam() override
{
cout << "ComputerA install 2G Ram" << endl;
}
void installGraphicsCard() override
{
cout << "ComputerA install Gtx940 GraphicsCard" << endl;
}
};
class ComputerB:public Computer
{
protected:
void installCpu() override
{
cout << "ComputerB install Inter Core i7" << endl;
}
void installRam() override
{
cout << "ComputerB install 4G Ram" << endl;
}
void installGraphicsCard() override
{
cout << "ComputerB install Gtx960 GraphicsCard" << endl;
}
};
4.单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已存在单例,如果有则返回,没有则创建。
关键代码:构造函数是私有的。
单例大约有两种实现方法:懒汉与饿汉。
懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化,所以上边的经典方法被归为懒汉实现;
饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行实例化。
特点与选择:
由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
在访问量较小时,采用懒汉实现。这是以时间换空间。
//懒汉式一般实现:非线程安全,getInstance返回的实例指针需要delete
class Singleton
{
public:
static Singleton* getInstance();
~Singleton(){}
private:
static Singleton* m_pSingleton;
Singleton(){}
Singleton(const Singleton& obj) = delete; //明确拒绝
Singleton& operator=(const Singleton& obj) = delete; //明确拒绝
};
Singleton* Singleton::m_pSingleton = NULL;
Singleton* Singleton::getInstance()
{
if(m_pSingleton == NULL)
{
m_pSingleton = new Singleton;
}
return m_pSingleton;
}
//END
//懒汉式:加lock,线程安全
std::mutex mt;
class Singleton
{
public:
static Singleton* getInstance();
private:
Singleton(){}
Singleton(const Singleton&) = delete; //明确拒绝
Singleton& operator=(const Singleton&) = delete; //明确拒绝
static Singleton* m_pSingleton;
};
Singleton* Singleton::m_pSingleton = NULL;
Singleton* Singleton::getInstance()
{
if(m_pSingleton == NULL)
{
mt.lock();
m_pSingleton = new Singleton();
mt.unlock();
}
return m_pSingleton;
}
//END
//返回一个reference指向local static对象
//多线程可能存在不确定性:任何一种non-const static对象,不论它是local或non-local,在多线程环境下“等待某事发生”都会有麻烦。解决的方法:在程序的单线程启动阶段手工调用所有reference-returning函数。
class Singleton
{
public:
static Singleton& getInstance();
private:
Singleton(){}
Singleton(const Singleton&) = delete; //明确拒绝
Singleton& operator=(const Singleton&) = delete; //明确拒绝
};
Singleton& Singleton::getInstance()
{
static Singleton singleton;
return singleton;
}
//END
//饿汉式:线程安全,注意delete
class Singleton
{
public:
static Singleton* getInstance();
private:
Singleton(){}
Singleton(const Singleton&) = delete; //明确拒绝
Singleton& operator=(const Singleton&) = delete; //明确拒绝
static Singleton* m_pSingleton;
};
Singleton* Singleton::m_pSingleton = new Singleton();
Singleton* Singleton::getInstance()
{
return m_pSingleton;
}
//END
5.适配器模式:将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的哪些类可以一起工作。
主要解决:主要解决在软件系统中,常常要将一些”现存的对象”放到新的环境中,而新环境要求的接口是现对象不能满足的。
何时使用: 1、系统需要使用现有的类,而此类的接口不符合系统的需要。 2、想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。 3、通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
如何解决:继承或依赖(推荐)。
关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
缺点:1、过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
//使用复合,对象模式
class Deque //双端队列,被适配类
{
public:
void push_back(int x)
{
cout << "Deque push_back:" << x << endl;
}
void push_front(int x)
{
cout << "Deque push_front:" << x << endl;
}
void pop_back()
{
cout << "Deque pop_back" << endl;
}
void pop_front()
{
cout << "Deque pop_front" << endl;
}
};
class Sequence //顺序类,目标类
{
public:
virtual void push(int x) = 0;
virtual void pop() = 0;
};
class Stack:public Sequence //栈, 适配类
{
public:
void push(int x)
{
m_deque.push_back(x);
}
void pop()
{
m_deque.pop_back();
}
private:
Deque m_deque;
};
class Queue:public Sequence //队列,适配类
{
public:
void push(int x)
{
m_deque.push_back(x);
}
void pop()
{
m_deque.pop_front();
}
private:
Deque m_deque;
};
//END
6.策略模式:是指定义一系列的算法,把它们一个个封装起来,并且使它们可以互相替换。使得算法可以独立于使用它的客户而变化,也就是说这些算法所完成的功能是一样的,对外接口是一样的,只是各自现实上存在差异。
主要解决:在有多种算法相似的情况下,使用 if…else 所带来的复杂和难以维护。
何时使用:一个系统有许多许多类,而区分它们的只是他们直接的行为。
如何解决:将这些算法封装成一个一个的类,任意地替换。
关键代码:实现同一个接口。
缺点: 1、策略类会增多。 2、所有策略类都需要对外暴露。
//传统策略模式实现
class Hurt
{
public:
virtual void redBuff() = 0;
};
class AdcHurt:public Hurt
{
public:
void redBuff()
{
cout << "Adc hurt" << endl;
}
};
class ApcHurt:public Hurt
{
public:
void redBuff()
{
cout << "Apc hurt" << endl;
}
};
//方法1:传入一个指针参数
class Soldier
{
public:
Soldier(Hurt* hurt):m_hurt(hurt)
{
}
~Soldier()
{
}
void beInjured()
{
m_hurt->redBuff();
}
private:
Hurt* m_hurt;
};
//方法2:传入一个参数标签
typedef enum
{
adc,
apc
}HurtType;
class Master
{
public:
Master(HurtType type)
{
switch(type)
{
case adc:
m_hurt = new AdcHurt;
break;
case apc:
m_hurt = new ApcHurt;
break;
default:
m_hurt = NULL;
break;
}
}
~Master()
{
}
void beInjured()
{
if(m_hurt != NULL)
{
m_hurt->redBuff();
}
else
{
cout << "Not hurt" << endl;
}
}
private:
Hurt* m_hurt;
};
//方法3:使用模板类
template <typename T>
class Tank
{
public:
void beInjured()
{
m_hurt.redBuff();
}
private:
T m_hurt;
};
//END
//使用函数指针实现策略模式
void adcHurt(int num)
{
cout << "adc hurt:" << num << endl;
}
void apcHurt(int num)
{
cout << "apc hurt:" << num << endl;
}
//普通函数指针
class Aid
{
public:
typedef void (*HurtFun)(int);
Aid(HurtFun fun):m_fun(fun)
{
}
void beInjured(int num)
{
m_fun(num);
}
private:
HurtFun m_fun;
};
//使用std::function , 头文件:#include<functional>
class Bowman
{
public:
typedef std::function<void(int)> HurtFunc;
Bowman(HurtFunc fun):m_fun(fun)
{
}
void beInjured(int num)
{
m_fun(num);
}
private:
HurtFunc m_fun;
};
//END
7.工厂模式:简单工厂模式、工厂方法模式、抽象工厂模式
1)、简单工厂模式:主要特点是需要在工厂类中做判断,从而创造相应的产品,当增加新产品时,需要修改工厂类。
typedef enum
{
T80 = 1,
T99
}TankType;
class Tank
{
public:
virtual void message() = 0;
};
class Tank80:public Tank
{
public:
void message()
{
cout << "Tank80" << endl;
}
};
class Tank99:public Tank
{
public:
void message()
{
cout << "Tank99" << endl;
}
};
class TankFactory
{
public:
Tank* createTank(TankType type)
{
switch(type)
{
case 1:
return new Tank80();
case 2:
return new Tank99();
default:
return NULL;
}
}
};
2)、工厂方法模式:是指定义一个创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到其子类。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
缺点:每增加一种产品,就需要增加一个对象工厂。相比简单工厂模式,工厂方法模式需要定义更多的类。
class Tank
{
public:
virtual void message() = 0;
};
class Tank80:public Tank
{
public:
void message()
{
cout << "Tank80" << endl;
}
};
class Tank99:public Tank
{
public:
void message()
{
cout << "Tank99" << endl;
}
};
class TankFactory
{
public:
virtual Tank* createTank() = 0;
};
class Tank80Factory:public TankFactory
{
public:
Tank* createTank()
{
return new Tank80();
}
};
class Tank99Factory:public TankFactory
{
public:
Tank* createTank()
{
return new Tank99();
}
};
3)、抽象工厂模式:提供一个创建一系列相关或相互依赖的对象接口,而无需指定它们的具体类。
主要解决:主要解决接口选择的问题。
何时使用:系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。
如何解决:在一个产品族里面,定义多个产品。
关键代码:在一个工厂里聚合多个同类产品。
缺点:产品族扩展非常困难,要增加一个系列的某一产品,既要在抽象的 Creator 里加代码,又要在具体的里面加代码。
class Tank
{
public:
virtual void message() = 0;
};
class Tank80:public Tank
{
public:
void message()
{
cout << "Tank80" << endl;
}
};
class Tank99:public Tank
{
public:
void message()
{
cout << "Tank99" << endl;
}
};
class Plain
{
public:
virtual void message() = 0;
};
class Plain80: public Plain
{
public:
void message()
{
cout << "Plain80" << endl;
}
};
class Plain99: public Plain
{
public:
void message()
{
cout << "Plain99" << endl;
}
};
class Factory
{
public:
virtual Tank* createTank() = 0;
virtual Plain* createPlain() = 0;
};
class Factory80:public Factory
{
public:
Tank* createTank()
{
return new Tank80();
}
Plain* createPlain()
{
return new Plain80();
}
};
class Factory99:public Factory
{
public:
Tank* createTank()
{
return new Tank99();
}
Plain* createPlain()
{
return new Plain99();
}
};