工厂模式
在工厂模式中,我们在创建对象时不会对客户端暴漏创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。工厂模式作为一种创建模式,一般在创建复杂对象时,考虑使用;在创建简单对象时,建议直接new完成一个实例对象的创建
简单工厂模式
主要特点是需要在工厂类中做判断,从而创造相应的产品,当增加新产品时,需要修改工厂类。使用简单工厂模式,我们只需要知道具体的产品型号就可以创建一个产品。
缺点:工厂类中集中了所有产品类的创建逻辑,如果产品量较大,会使工厂类变得非常臃肿
/*
关键代码:创建过程在工厂类中完成。
*/
#include <iostream>
using namespace std;
//定义产品类型信息
typedef enum
{
Tank_Type_56,
Tank_Type_96,
Tank_Type_Num
}Tank_Type;
//抽象产品类
class Tank
{
public:
virtual const string& type() = 0;
};
//具体的产品类
class Tank56 : public Tank
{
public:
Tank56():Tank(),m_strType("Tank56")
{
}
const string& type() override
{
cout << m_strType.data() << endl;
return m_strType;
}
private:
string m_strType;
};
//具体的产品类
class Tank96 : public Tank
{
public:
Tank96():Tank(),m_strType("Tank96")
{
}
const string& type() override
{
cout << m_strType.data() << endl;
return m_strType;
}
private:
string m_strType;
};
//工厂类
class TankFactory
{
public:
//根据产品信息创建具体的产品类实例,返回一个抽象产品类
Tank* createTank(Tank_Type type)
{
switch(type)
{
case Tank_Type_56:
return new Tank56();
case Tank_Type_96:
return new Tank96();
default:
return nullptr;
}
}
};
int main()
{
TankFactory* factory = new TankFactory();
Tank* tank56 = factory->createTank(Tank_Type_56);
tank56->type();
Tank* tank96 = factory->createTank(Tank_Type_96);
tank96->type();
delete tank96;
tank96 = nullptr;
delete tank56;
tank56 = nullptr;
delete factory;
factory = nullptr;
return 0;
}
工厂方法模式
定义一个创建对象的接口,其子类去具体实现这个接口以完成具体的工作。如果需要增加新的产品类,只需要扩展一个相应的工厂类即可。
缺点:产品类数据较多时,需要实现大量的工厂类,这无疑增加了代码量。
/*
关键代码:创建过程在其子类执行。
*/
#include <iostream>
using namespace std;
//产品抽象类
class Tank
{
public:
virtual const string& type() = 0;
};
//具体的产品类
class Tank56 : public Tank
{
public:
Tank56():Tank(),m_strType("Tank56")
{
}
const string& type() override
{
cout << m_strType.data() << endl;
return m_strType;
}
private:
string m_strType;
};
//具体的产品类
class Tank96 : public Tank
{
public:
Tank96():Tank(),m_strType("Tank96")
{
}
const string& type() override
{
cout << m_strType.data() << endl;
return m_strType;
}
private:
string m_strType;
};
//抽象工厂类,提供一个创建接口
class TankFactory
{
public:
//提供创建产品实例的接口,返回抽象产品类
virtual Tank* createTank() = 0;
};
//具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
class Tank56Factory : public TankFactory
{
public:
Tank* createTank() override
{
return new Tank56();
}
};
//具体的创建工厂类,使用抽象工厂类提供的接口,去创建具体的产品实例
class Tank96Factory : public TankFactory
{
public:
Tank* createTank() override
{
return new Tank96();
}
};
int main()
{
TankFactory* factory56 = new Tank56Factory();
Tank* tank56 = factory56->createTank();
tank56->type();
TankFactory* factory96 = new Tank96Factory();
Tank* tank96 = factory96->createTank();
tank96->type();
delete tank96;
tank96 = nullptr;
delete factory96;
factory96 = nullptr;
delete tank56;
tank56 = nullptr;
delete factory56;
factory56 = nullptr;
return 0;
}
抽象工厂模式
抽象工厂模式提供创建一系列相关或相互依赖对象的接口,而无需指定他们具体的类。当存在多个产品系列,而客户端只是用一个系列时,可以考虑使用抽象工厂模式。
缺点:当增加一个新系列的产品时,不仅需要实现集体的产品类,还需要增加一个新的创建接口,扩展相对困难。
/*
* 关键代码:在一个工厂里聚合多个同类产品。
* 以下代码以白色衣服和黑色衣服为例,白色衣服为一个产品系列,黑色衣服为一个产品系列。
* 白色上衣搭配白色裤子,黑色上衣搭配黑色裤字。每个系列的衣服由一个对应的工厂创建,这样一个工厂创建的衣服能保证衣服为同一个系列。
*/
//抽象上衣类
class Coat
{
public:
virtual const string& color() = 0;
};
//黑色上衣类
class BlackCoat : public Coat
{
public:
BlackCoat():Coat(),m_strColor("Black Coat")
{
}
const string& color() override
{
cout << m_strColor.data() << endl;
return m_strColor;
}
private:
string m_strColor;
};
//白色上衣类
class WhiteCoat : public Coat
{
public:
WhiteCoat():Coat(),m_strColor("White Coat")
{
}
const string& color() override
{
cout << m_strColor.data() << endl;
return m_strColor;
}
private:
string m_strColor;
};
//抽象裤子类
class Pants
{
public:
virtual const string& color() = 0;
};
//黑色裤子类
class BlackPants : public Pants
{
public:
BlackPants():Pants(),m_strColor("Black Pants")
{
}
const string& color() override
{
cout << m_strColor.data() << endl;
return m_strColor;
}
private:
string m_strColor;
};
//白色裤子类
class WhitePants : public Pants
{
public:
WhitePants():Pants(),m_strColor("White Pants")
{
}
const string& color() override
{
cout << m_strColor.data() << endl;
return m_strColor;
}
private:
string m_strColor;
};
//抽象工厂类,提供衣服创建接口
class Factory
{
public:
//上衣创建接口,返回抽象上衣类
virtual Coat* createCoat() = 0;
//裤子创建接口,返回抽象裤子类
virtual Pants* createPants() = 0;
};
//创建白色衣服的工厂类,具体实现创建白色上衣和白色裤子的接口
class WhiteFactory : public Factory
{
public:
Coat* createCoat() override
{
return new WhiteCoat();
}
Pants* createPants() override
{
return new WhitePants();
}
};
//创建黑色衣服的工厂类,具体实现创建黑色上衣和白色裤子的接口
class BlackFactory : public Factory
{
Coat* createCoat() override
{
return new BlackCoat();
}
Pants* createPants() override
{
return new BlackPants();
}
};
策略模式
策略模式是指定义一系列的算法,把它们单独封装起来,并且使他们可以相互替换,使得算法可以独立于使用它们的客户端而变化,也就是说这些算法所完成的功能类型是一样的,对外接口也是一样的,只是不同的策略为角色引起环境变化表现出不同的行为。相对于使用大量的if…else,使用策略模式可以减低复杂度,是的代码更容易维护。
缺点:可能需要定义大量的策略类,并且这些策略类都要提供给客户端。
传统的策略模式
/*
* 关键代码:实现同一个接口。
* 以下代码实例中,以游戏角色不同的攻击方式为不同的策略,游戏角色即为执行不同策略的环境角色。
*/
#include <iostream>
using namespace std;
//抽象策略类,提供一个接口
class Hurt
{
public:
virtual void blood() = 0;
};
//具体的策略实现类,具体实现接口, Adc持续普通攻击
class AdcHurt : public Hurt
{
public:
void blood() override
{
cout << "Adc hurt, Blood loss" << endl;
}
};
//具体的策略实现类,具体实现接口, Apc技能攻击
class ApcHurt : public Hurt
{
public:
void blood() override
{
cout << "Apc Hurt, Blood loss" << endl;
}
};
//环境角色类, 游戏角色战士,传入一个策略类指针参数。
class Soldier
{
public:
Soldier(Hurt* hurt):m_pHurt(hurt)
{
}
//在不同的策略下,该游戏角色表现出不同的攻击
void attack()
{
m_pHurt->blood();
}
private:
Hurt* m_pHurt;
};
//定义策略标签
typedef enum
{
Hurt_Type_Adc,
Hurt_Type_Apc,
Hurt_Type_Num
}HurtType;
//环境角色类, 游戏角色法师,传入一个策略标签参数。
class Mage
{
public:
Mage(HurtType type)
{
switch(type)
{
case Hurt_Type_Adc:
m_pHurt = new AdcHurt();
break;
case Hurt_Type_Apc:
m_pHurt = new ApcHurt();
break;
default:
break;
}
}
~Mage()
{
delete m_pHurt;
m_pHurt = nullptr;
cout << "~Mage()" << endl;
}
void attack()
{
m_pHurt->blood();
}
private:
Hurt* m_pHurt;
};
//环境角色类, 游戏角色弓箭手,实现模板传递策略。
template<typename T>
class Archer
{
public:
void attack()
{
m_hurt.blood();
}
private:
T m_hurt;
};
int main()
{
Archer<ApcHurt>* arc = new Archer<ApcHurt>;
arc->attack();
delete arc;
arc = nullptr;
return 0;
}
使用函数指针的策略模式
#include <iostream>
#include <functional>
void adcHurt()
{
std::cout << "Adc Hurt" << std::endl;
}
void apcHurt()
{
std::cout << "Apc Hurt" << std::endl;
}
//环境角色类, 使用传统的函数指针
class Soldier
{
public:
typedef void (*Function)();
Soldier(Function fun): m_fun(fun)
{
}
void attack()
{
m_fun();
}
private:
Function m_fun;
};
//环境角色类, 使用std::function<>
class Mage
{
public:
typedef std::function<void()> Function;
Mage(Function fun): m_fun(fun)
{
}
void attack()
{
m_fun();
}
private:
Function m_fun;
};
int main()
{
Soldier* soldier = new Soldier(apcHurt);
soldier->attack();
delete soldier;
soldier = nullptr;
return 0;
}
适配器模式
适配器模式可以将一个类的接口转换成客户端希望的另一个接口,是的原来由于接口不兼容而不能在一起工作的那些类可以在一起工作。通俗的讲就是当我们已经有了一些类,而这些类不能满足新的需求,此时就可以考虑是否能将现有的类适配成可以满足新需求的类。适配器类需要继承或依赖现有的类,实现想要的目标接口。
缺点:过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是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) override
{
m_deque.push_front(x);
}
//从堆栈中删除顶部元素
void pop() override
{
m_deque.pop_front();
}
private:
Deque m_deque;
};
//队列,先进先出,适配类
class Queue:public Sequence
{
public:
//将元素添加到队列尾部
void push(int x) override
{
m_deque.push_back(x);
}
//从队列中删除顶部元素
void pop() override
{
m_deque.pop_front();
}
private:
Deque m_deque;
};
使用继承实现适配器模式
//双端队列,被适配类
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, private Deque
{
public:
void push(int x)
{
push_front(x);
}
void pop()
{
pop_front();
}
};
//队列,先进先出,适配类
class Queue:public Sequence, private Deque
{
public:
void push(int x)
{
push_back(x);
}
void pop()
{
pop_front();
}
};
单例模式
单例模式顾名思义,保证一个类仅可以实例化一个对象,并提供一个可以访问它的全局接口。实现单例模式必须注意以下几点:1.单例类只能有一个实例化对象,2.单例类必须自己提供一个实例化对象,3.单例类必须提供一个可以访问唯一实例化对象的接口。
懒汉单例模式
不到万不得已就不会去实例化类,也就是说在第一次用到类实例化的时候才会去实例化一个对象。在访问量较小,甚至可能不回去访问的情况下,采用懒汉实现,这是以时间换空间。
非线程安全的懒汉单例模式
/*
* 关键代码:构造函数是私有的,不能通过赋值运算,拷贝构造等方式实例化对象。
*/
//懒汉式一般实现:非线程安全,getInstance返回的实例指针需要delete
class Singleton
{
public:
static Singleton* getInstance();
~Singleton(){}
private:
Singleton(){} //构造函数私有
Singleton(const Singleton& obj) = delete; //明确拒绝
Singleton& operator=(const Singleton& obj) = delete; //明确拒绝
static Singleton* m_pSingleton;
};
Singleton* Singleton::m_pSingleton = NULL;
Singleton* Singleton::getInstance()
{
if(m_pSingleton == NULL)
{
m_pSingleton = new Singleton;
}
return m_pSingleton;
}
线程安全的懒汉单例模式
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();
if(m_pSingleton == NULL)
{
m_pSingleton = new Singleton();
}
mt.unlock();
}
return m_pSingleton;
}
饿汉单例模式
在单例类定义的时候就进行实例化。在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是空间换时间。
//饿汉式:线程安全,注意一定要在合适的地方去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;
}
观察者模式
定义对象间一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都要得到通知并自动更新。观察者模式从根本上讲必须包含两个角色:观察者和被观察对象。
被观察对象自身应该包含一个容器来存放观察者对象,当被观察者自身发生改变时通知容器内所有的观察者对象自动更新。
观察者对象可以注册到被观察者中,完成注册后可以检测被观察者的变化,接收被观察者的通知。当然观察者也可以被注销掉,停止对被观察者的监控。
/*
* 关键代码:在目标类中增加一个ArrayList来存放观察者们。
*/
#include <iostream>
#include <list>
#include <memory>
using namespace std;
class View;
//被观察者抽象类 数据模型
class DataModel
{
public:
virtual ~DataModel(){}
virtual void addView(View* view) = 0;
virtual void removeView(View* view) = 0;
virtual void notify() = 0; //通知函数
};
//观察者抽象类 视图
class View
{
public:
virtual ~View(){ cout << "~View()" << endl; }
virtual void update() = 0;
virtual void setViewName(const string& name) = 0;
virtual const string& name() = 0;
};
//具体的被观察类, 整数模型
class IntDataModel:public DataModel
{
public:
~IntDataModel()
{
m_pViewList.clear();
}
virtual void addView(View* view) override
{
shared_ptr<View> temp(view);
auto iter = find(m_pViewList.begin(), m_pViewList.end(), temp);
if(iter == m_pViewList.end())
{
m_pViewList.push_front(temp);
}
else
{
cout << "View already exists" << endl;
}
}
void removeView(View* view) override
{
auto iter = m_pViewList.begin();
for(; iter != m_pViewList.end(); iter++)
{
if((*iter).get() == view)
{
m_pViewList.erase(iter);
cout << "remove view" << endl;
return;
}
}
}
virtual void notify() override
{
auto iter = m_pViewList.begin();
for(; iter != m_pViewList.end(); iter++)
{
(*iter).get()->update();
}
}
private:
list<shared_ptr<View>> m_pViewList;
};
//具体的观察者类 表视图
class TableView : public View
{
public:
TableView() : m_name("unknow"){}
TableView(const string& name) : m_name(name){}
~TableView(){ cout << "~TableView(): " << m_name.data() << endl; }
void setViewName(const string& name)
{
m_name = name;
}
const string& name()
{
return m_name;
}
void update() override
{
cout << m_name.data() << " update" << endl;
}
private:
string m_name;
};
int main()
{
/*
* 这里需要补充说明的是在此示例代码中,View一旦被注册到DataModel类之后,DataModel解析时会自动解析掉内部容器中存储的View对象,
* 因此注册后的View对象不需要在手动去delete,再去delete View对象会出错。
*/
View* v1 = new TableView("TableView1");
View* v2 = new TableView("TableView2");
View* v3 = new TableView("TableView3");
View* v4 = new TableView("TableView4");
IntDataModel* model = new IntDataModel;
model->addView(v1);
model->addView(v2);
model->addView(v3);
model->addView(v4);
model->notify();
cout << "-------------\n" << endl;
model->removeView(v1);
model->notify();
delete model;
model = nullptr;
return 0;
}