目录
设计模式概述
单例模式
饿汉模式
懒汉模式
工厂模式
简单工厂模式
工厂方法模式
抽象工厂模式
观察者模式
设计模式概述
设计模式:一套反复被人使用、多数人知晓的、经过分类编目的代码设计经验的总结。一种固定的写代码的思维逻辑方式,一种设计类与类之间关系的抽象思维的定式。
设计模式的目的是应付客观情况的变化,提高代码的复用性和维护性。
设计模式的分类:
- 创建型模式(设计对象实例化的方式,5种):单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
- 结构型模式(描述如何组合类和对象获得更大的结构,7种):代理模式、装饰者模式、适配器模式、桥接模式、组合模式、外观模式、享元模式
- 行为型模式(描述类和对象的交互以及分配职责,11种):模板方法模式、命令模式、责任链模式、策略模式、中介者模式、观察者模式、备忘录模式、访问者模式、状态模式、解释器模式、迭代器模式
设计模式的七大设计原则:
- 单一职责原则:一个类应该只有一个引起它变化的原因,变化的方向隐含它的职责
- 开放封闭原则:对扩展开放,对更改封闭,类模块可扩展但不可修改
- 依赖倒置原则:高层(稳定)不依赖底层(变化),两者依赖抽象(稳定),抽象(稳定)不依赖细节(变化),细节依赖抽象(稳定)
- 里氏替换原则:子类必须能够替代他们的基类
- 接口隔离原则:一个接口只提供一种对外功能,接口应该小而完备,尽量私有化用户用不到的接口
- 优先组合而非继承原则:类的继承通常是“白箱复用”,类的组合通常是“黑箱复用”,继承在一定程序上破坏封装性,子类和父类的耦合度高
- 迪米特法则:对象应该对其他对象尽可能少的了解,各个模块相互调用时,提供一个统一的接口来实现
在实际设计模式选择中,并不是所有的设计原则都要满足,而是根据不同的应用场景尽可能多的满足设计原则。
单例模式
单例模式:在程序的生命周期内,一个类只能有一个实例,确保该类的唯一性。
单例模式分为饿汉模式和懒汉模式,区别在于创建实例的时间不同。
- 饿汉模式:系统运行时,在主程序之前就直接初始化创建实例,当需要时直接调用实例。饿汉模式不存在多线程的线程安全问题,但缺陷是若初始化数据过多会导致程序启动慢、多个单例创建并有依赖关系时无法控制。
- 懒汉模式:系统运行时,实例并不创建,只有当第一次需要使用这个实例的时候,才会初始化创建实例,这种方式需要考虑线程安全。
饿汉模式
#include <iostream>
#include <map>
using namespace std;
class Singleton
{
public:
static Singleton& get_instance()
{
return _st;
}
void insert(string name, int salary)
{
_info[name] = salary;
}
void print()
{
for (auto& e : _info)
{
cout << e.first << " : " << e.second << endl;
}
cout << endl;
}
private:
Singleton()
{}
Singleton(const Singleton& st) = delete;
Singleton& operator=(const Singleton& st) = delete;
private:
map<string, int> _info;
static Singleton _st;
};
Singleton Singleton::_st;
int main()
{
Singleton::get_instance().insert("张三", 12000);
Singleton& st1 = Singleton::get_instance();
st1.insert("李四", 30000);
st1.insert("王五", 45000);
st1.insert("赵六", 60000);
st1.print();
Singleton& st2 = Singleton::get_instance();
st2.print();
st2.insert("周七", 8000);
st2.insert("张三", 24000);
st1.print();
st2.print();
// st1、st2 表示同一个实例
return 0;
}
懒汉模式
第一种方式:加锁保证线程安全
#include <iostream>
#include <map>
#include <mutex>
using namespace std;
// RAII锁管理类
template<class Lock>
class LockGuard
{
public:
LockGuard(Lock& lk)
: _lk(lk)
{
_lk.lock();
}
~LockGuard()
{
_lk.unlock();
}
private:
Lock& _lk;
};
class Singleton
{
public:
static Singleton& get_instance()
{
// 双重验证
if (_pst == nullptr) // 判断是否需要加锁,提高效率
{
LockGuard<mutex> lock(_mtx);
//lock_guard<mutex> lock(_mtx); // 锁管理器可以直接使用库里面的
if (_pst == nullptr) // 判断是否已经创建实例,保证线程安全
{
_pst = new Singleton;
}
}
return *_pst;
}
void insert(string name, int salary)
{
_info[name] = salary;
}
void print()
{
for (auto& e : _info)
{
cout << e.first << " : " << e.second << endl;
}
cout << endl;
}
// 单例模式一般不考虑资源回收问题,都是在程序结束时自动回收资源
// 只有在需要在需要手动回收做数据处理的时候,再提供回收接口
void del_instance()
{
lock_guard<mutex> lock(_mtx);
if (_pst)
{
delete _pst;
_pst = nullptr;
}
}
private:
Singleton()
{}
Singleton(const Singleton& st) = delete;
Singleton& operator=(const Singleton& st) = delete;
private:
map<string, int> _info;
static Singleton* _pst;
static mutex _mtx;
};
Singleton* Singleton::_pst;
mutex Singleton::_mtx;
int main()
{
Singleton::get_instance().insert("张三", 12000);
Singleton& st1 = Singleton::get_instance();
st1.insert("李四", 30000);
st1.insert("王五", 45000);
st1.insert("赵六", 60000);
st1.print();
Singleton& st2 = Singleton::get_instance();
st2.print();
st2.insert("周七", 8000);
st2.insert("张三", 24000);
st1.print();
st2.print();
return 0;
}
第二种方式:静态局部对象保证线程安全
#include <iostream>
#include <map>
#include <mutex>
using namespace std;
class Singleton
{
public:
static Singleton& get_instance()
{
static Singleton st;
return st;
}
void insert(string name, int salary)
{
_info[name] = salary;
}
void print()
{
for (auto& e : _info)
{
cout << e.first << " : " << e.second << endl;
}
cout << endl;
}
private:
Singleton()
{}
Singleton(const Singleton& st) = delete;
Singleton& operator=(const Singleton& st) = delete;
private:
map<string, int> _info;
};
int main()
{
Singleton::get_instance().insert("张三", 12000);
Singleton& st1 = Singleton::get_instance();
st1.insert("李四", 30000);
st1.insert("王五", 45000);
st1.insert("赵六", 60000);
st1.print();
Singleton& st2 = Singleton::get_instance();
st2.print();
st2.insert("周七", 8000);
st2.insert("张三", 24000);
st1.print();
st2.print();
return 0;
}
C++11之前,静态局部变量的初始化不能保证线程安全,C++11之后,静态局部变量的初始化是线程安全的。
工厂模式
简单工厂模式
简单工厂模式属于类的创建型模式,又叫做静态工厂模式。通过专门定义一个类来负责创建其他类的示例,被创建的示例通常都具有共同的父类。
实现步骤:
- 提供一个工厂类:负责创建所有实例的内部逻辑,可被外界直接调用,创建所需的产品对象。
- 提供一个抽象产品类:简单工厂模式所创建的所有模式的父类,它负责描述所有实例所共有的公共接口。
- 提供多个具体产品类:简单工厂模式所创建的具体实例对象。
简单工厂类代码示例:实现运算器
#include <iostream>
using namespace std;
// 抽象产品类
class Operation
{
public:
double _lVal;
double _rVal;
virtual double get_result() = 0;
};
// 加减乘除具体产品类
class AddOperator : public Operation
{
double get_result()
{
return _lVal + _rVal;
}
};
class SubOperator : public Operation
{
double get_result()
{
return _lVal - _rVal;
}
};
class MulOperator : public Operation
{
double get_result()
{
return _lVal * _rVal;
}
};
class DivOperator : public Operation
{
double get_result()
{
return _lVal / _rVal;
}
};
// 工厂类
class OperatorFactory
{
public:
static Operation* create_operation(const char op)
{
switch (op)
{
case '+':
return new AddOperator();
case '-':
return new SubOperator();
case '*':
return new MulOperator();
case '/':
return new DivOperator();
default:
return nullptr;
}
}
};
int main()
{
Operation* add = OperatorFactory::create_operation('+');
add->_lVal = 10;
add->_rVal = 20;
cout << add->get_result() << endl;
add = OperatorFactory::create_operation('-');
add->_lVal = 10;
add->_rVal = 20;
cout << add->get_result() << endl;
return 0;
}
优点:
- 帮助封装,实现组件封装,面向接口编程
- 解耦合,客户端和具体实现类的解耦合
缺点:
- 可能增加客户端的复杂度
- 不方便扩展
工厂方法模式
工厂方法模式又被称为多态工厂模式。工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新产品。
工行方法模式和简单工厂模式在结构上相似。工厂方法类的核心是一个抽象工厂类,而简单工厂模式把核心放在一个具体类上,工厂方法类之所以又叫多态工厂类是因为具体工厂类都有共同的接口,或者有共同的抽象父类。当系统拓展需要添加新的产品对象时,仅仅需要添加一个具体对象和一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端,很好的符合了“开放-封闭”原则,而简单工厂模式在添加新产品对象后不得不修改工厂方法,拓展性不好。
实现步骤:
- 提供一个抽象工厂类:所有具体工厂类的父类
- 提供与产品对应的工厂类:负责实例化产品对象
- 提供一个抽象产品类:所有产品的父类
- 提供一个或多个产品类:工厂方法模式所创建的具体实例对象
工厂方法模式代码示例:建造飞机火箭
#include <iostream>
using namespace std;
// 抽象产品类
class AbstractProduct
{
public:
virtual void make_product() = 0;
virtual ~AbstractProduct()
{}
};
// 抽象工厂类
class AbstractFactory
{
public:
virtual AbstractProduct* create_product() = 0;
virtual ~AbstractFactory()
{}
};
// 具体产品类
class PlaneProduct : public AbstractProduct
{
public:
void make_product()
{
cout << "make a plane..." << endl;
}
};
class PlaneFactory : public AbstractFactory
{
AbstractProduct* create_product()
{
return new PlaneProduct;
}
};
class RocketProduct : public AbstractProduct
{
public:
void make_product()
{
cout << "make a rocket..." << endl;
}
};
class RocketFactory :public AbstractFactory
{
AbstractProduct* create_product()
{
return new RocketProduct;
}
};
int main()
{
AbstractFactory* factory = new PlaneFactory;
AbstractProduct* product = factory->create_product();
product->make_product();
factory = new RocketFactory();
product = factory->create_product();
product->make_product();
return 0;
}
优点:
- 需求改变时改动小
- 具体的创建示例过程与客户端分离
缺点:
- 新增产品时,工程量大
抽象工厂模式
抽象工厂模式是所有形态的工厂模式中最为抽象和一般性的,抽象工厂模式可以向客户端提供一个接口,使得客户端不必指定产品的具体类型的情况下,能够创建多个产品族的产品对象。
抽象工厂方法是针对一个产品族,使得易于交换产品系列,只需改变具体的工厂就可以使用不同的产品配置。当一个族中的产品对象被设计成一起工作且一个应用只是用同一族的对象,例如设计系统生成不同的UI界,按钮、边框等UI元素在一起使用,并且只能同属于一种风格,这很容易使用抽象工厂实现。
实现步骤:
- 提供一个抽象工厂类:声明一组创建一族产品的方法
- 提供一个具体工厂类:实现在抽象工厂创建产品的工厂方法
- 提供一个抽象产品类:抽象产品中声明了产品具有的业务方法
- 提供一个具体产品类:实现抽象产品接口中声明的业务方法
抽象工厂代码示例:鼠标键盘产品族
#include <iostream>
using namespace std;
// 抽象产品
class Product
{
public:
virtual void show() = 0;
virtual ~Product() {}
};
// 抽象产品族1:键盘
class Keyboard : public Product
{
public:
virtual ~Keyboard() {}
};
class LogiKeyboard : public Keyboard
{
void show()
{
cout << "罗技键盘" << endl;
}
};
class RazerKeyboard : public Keyboard
{
void show()
{
cout << "雷蛇键盘" << endl;
}
};
// 抽象产品族2:鼠标
class Mouse : public Product
{
public:
virtual ~Mouse() {}
};
class LogiMouse : public Mouse
{
void show()
{
cout << "罗技鼠标" << endl;
}
};
class RazerMouse : public Mouse
{
void show()
{
cout << "雷蛇鼠标" << endl;
}
};
// 抽象工厂
class AbstractFactory
{
public:
virtual Keyboard* create_keyboard() = 0;
virtual Mouse* create_mouse() = 0;
virtual ~AbstractFactory() {}
};
// 具体工程
class LogiFactory : public AbstractFactory
{
Keyboard* create_keyboard()
{
return new LogiKeyboard;
}
Mouse* create_mouse()
{
return new LogiMouse;
}
};
class RazerFactory : public AbstractFactory
{
Keyboard* create_keyboard()
{
return new RazerKeyboard;
}
Mouse* create_mouse()
{
return new RazerMouse;
}
};
int main()
{
AbstractFactory* factory = new LogiFactory;
Keyboard* keyboard = factory->create_keyboard();
Mouse* mouse = factory->create_mouse();
keyboard->show();
mouse->show();
delete keyboard;
delete mouse;
delete factory;
factory = new RazerFactory;
keyboard = factory->create_keyboard();
mouse = factory->create_mouse();
keyboard->show();
mouse->show();
delete keyboard;
delete mouse;
delete factory;
keyboard = nullptr;
mouse = nullptr;
factory = nullptr;
return 0;
}
优点:
- 抽象工厂封装了变化,封装了对象创建的具体细节
- 增加新的产品族很方便,无须修改已有系统
- 针对接口进行编程,而不是针对具体产品进行编程
缺点:
- 增加新的产品等级结构需对原系统做较大修改(违背“开放-封闭原则”)
观察者模式
观察者模式定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖都会收到通知并自动更新。
实现步骤:
- 抽象主题(抽象被观察者)角色:把所有观察者角色保存在一个集合里,每个主题都有任意数量的观察者。抽象主题提供一个接口,可以增加和删除对象。
- 具体主题(具体被观察者)角色:将所有状态存入具体观察者对象。在具体主题的内部发生改变时,给所有注册过的观察者发送通知。
- 抽象观察者角色:观察者的抽象类。定义了一个更新接口,使得在得到主题改通知时更新自己。
- 具体观察者角色:实现抽象观察者定义的接口。在得到主题更改通知时更新自身状态。
示例:气象系统有三个部分分别是气象站(获取实际气象数据的物理装置)、WeatherData对象(用来追踪气象站的数据并更新布告板)、布告板(显示当前天气状态给用户)。WeatherData对象知道如何跟物理气象站联系,以取得更新信息。WeatherData对象会随即更新三个布告板的显示:目前状况(温度、湿度、气压)、气象统计、天气预报。我们的程序是建立一个应用,利用WeatherData对象取得数据,并更新三个布告板:目前状况、气象统计和天气预报。
#include <iostream>
#include <list>
using namespace std;
class Observer
{
public:
virtual void update(float tempreature, float humudity, float pressure) = 0;
virtual ~Observer()
{}
};
class DisplayElement
{
public:
virtual void display()
{};
virtual ~DisplayElement()
{}
};
class Subject
{
public:
virtual void regsiter_observer(Observer* obs)
{};
virtual void remove_observer(Observer* obs)
{};
virtual void notify_observer() = 0;
virtual ~Subject()
{}
};
// WeatherData类:注册、删除、通知观察者
class WeatherData : public Subject
{
public:
void regsiter_observer(Observer* obs)
{
_observers.push_back(obs);
}
void remove_observer(Observer* obs)
{
_observers.remove(obs);
}
void notify_observer()
{
list<Observer*>::iterator it = _observers.begin();
while (it != _observers.end())
{
Observer* obs = *it;
obs->update(_tempreature, _humidity, _pressure);
++it;
}
}
void measurements_changed()
{
notify_observer();
}
void set_measurements(float t, float h, float p)
{
_tempreature = t;
_humidity = h;
_pressure = p;
measurements_changed();
}
private:
list<Observer*> _observers;
float _tempreature;
float _humidity;
float _pressure;
};
// 布告板:申请注册、实时更新和显示
class CurrentConditionsDisplay : public Observer, public DisplayElement
{
public:
CurrentConditionsDisplay(WeatherData* weatherData)
{
_weatherData = weatherData;
_weatherData->regsiter_observer(this);
}
void update(float tempreature, float humidity, float pressure)
{
_tempreature = tempreature;
_humidity = humidity;
_pressure = pressure;
display();
}
void display()
{
cout << "Current Conditions" << endl;
cout << "tempreature: " << _tempreature << endl;
cout << "humidity: " << _humidity << endl;
cout << "pressure: " << _pressure << endl;
cout << endl;
}
private:
float _tempreature;
float _humidity;
float _pressure;
Subject* _weatherData;
};
// 客户代码
int main()
{
WeatherData* testData = new WeatherData();
CurrentConditionsDisplay* testDisplay = new CurrentConditionsDisplay(testData);
testData->set_measurements(32.5, 65, 30.4);
testData->set_measurements(37.5, 75, 34.4);
testData->set_measurements(22.5, 45, 26.4);
// 修改数据,实现自动更新布告板
return 0;
}