设计模式的相关知识,很多书籍和博客中都有详细总结,本文总结的目的:
1、将自己学习到的设计模式的知识按照自己的逻辑重新总结,方便查看和记忆。
2、方便让自己对设计模式中常用的知识有一个系统的认知。
设计模式
《⼤话设计模式》⼀书中提到 24 种设计模式,这 24 种设计模式没必要⾯⾯俱到,但⼀定要深⼊了解其中的⼏种,最好结合⾃⼰在实际开发过程中的例⼦进⾏深⼊的了解。
设计模式分类
设计模式分为三类:
-
创造型模式:单例模式、⼯⼚模式、建造者模式、原型模式
-
结构型模式:适配器模式、桥接模式、外观模式、组合模式、装饰模式、享元模式、代理模式
-
⾏为型模式:责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、 观察者模式、状态模式、策略模式、模板⽅法模式、访问者模式。
几种常见的设计模式
-
单例模式:保证⼀个类仅有⼀个实例,并提供⼀个访问它的全局访问点。
-
⼯⼚模式:包括简单⼯⼚模式、抽象⼯⼚模式、⼯⼚⽅法模式
-
简单⼯⼚模式:主要⽤于创建对象。⽤⼀个⼯⼚来根据输⼊的条件产⽣不同的类,然后根据不同类的虚函数得到不同的结果。
-
抽象⼯⼚模式:定义了⼀个创建⼀系列相关或相互依赖的接⼝,⽽⽆需指定他们的具体类。
-
-
观察者模式:定义了⼀种⼀对多的关系,让多个观察对象同时监听⼀个主题对象,主题对象发⽣变化时,会通知所有的观察者,使他们能够更新⾃⼰。
-
装饰模式:动态地给⼀个对象添加⼀些额外的职责,就增加功能来说,装饰模式⽐⽣成派⽣类更为灵活。
单例模式
单例模式的适用场景:
- 系统只需要⼀个实例对象,或者考虑到资源消耗的太⼤⽽只允许创建⼀个对象。
- 客户调⽤类的单个实例只允许使⽤⼀个公共访问点,除了该访问点之外不允许通过其它⽅式访问该实例(就是共有的静态⽅法)。
所以单例模式一般用在对实例数量有严格要求的地方,比如数据池,线程池,缓存,session回话等等。
构成的条件:
- 私有化它的构造函数,以防止外界创建单例类的对象;
- 使用类的私有静态指针变量指向类的唯一实例;
- 使用一个公有的静态方法获取该实例。
单例模式有两种:饿汉模式和懒汉模式
《C++ 单例模式》https://zhuanlan.zhihu.com/p/37469260
《析构函数声明为私有的作用》https://blog.****.net/jia_xiaoxin/article/details/3348045
饿汉模式
饿汉模式(线程安全):顾名思义,饿了就饥不择⻝了,所以在单例类定义的时候就进行实例化。
在最开始的时候静态对象就已经创建完成,设计方法是类中包含⼀个静态成员指针,该指针指向该类的⼀个对象,提供⼀个公有的静态成员方法,返回该对象指针,为了使得对象唯⼀,构造函数设为私有。由于在main函数之前初始化,所以没有线程安全的问题。
#include <iostream>
#include <algorithm>
using namespace std;
class SingleInstance {
public:
// 用户通过接口获取实例:使用 static 类成员函数
static SingleInstance* GetInstance() {
return &ins;
}
private:
static SingleInstance ins; // 注意此处不是指针
private:
//涉及到创建对象的函数都设置为private
SingleInstance(){}
SingleInstance() { std::cout<<"SingleInstance() 饿汉"<<std::endl; }
SingleInstance(const SingleInstance& other) {};
SingleInstance& operator=(const SingleInstance& other) {return *this; }
~SingleInstance(){};
};
SingleInstance SingleInstance::ins; // 静态成员函数定义并初始化
int main() {
//因为不能创建对象所以通过静态成员函数的⽅法返回静态成员变量
SingleInstance* ins = SingleInstance::GetInstance();
return 0;
}
//输出 SingleInstance() 饿汉
懒汉模式
懒汉(线程安全需要加锁):顾名思义,不到万不得已就不会去实例化类,也就是在第⼀次⽤到的类实例的时候才会去实例化。尽可能的晚的创建这个对象的实例,即在单例类第⼀次被引⽤的时候就将自己初始化,C++ 很多地方都有类型的思想,比如写时拷贝,晚绑定等。
原始版:
class SingleInstance
{
private:
static SingleInstance* instance; // 与饿汉模式相比,此处是指针
private:
SingleInstance() {};
~SingleInstance() {};
SingleInstance(const SingleInstance&);
SingleInstance& operator=(const SingleInstance&);
public:
static SingleInstance* getInstance()
{
if(instance == NULL)
instance = new SingleInstance();
return instance;
}
};
// init static member
SingleInstance* SingleInstance::instance = NULL;
这种原始的方法存在内存泄露的问题,有两种解决方法:
- 使用智能指针
- 使用静态的嵌套类对象
对于第二种解决方法,代码如下:
class SingleInstance
{
private:
static SingleInstance* instance;
private:
SingleInstance() { };
~SingleInstance() { };
SingleInstance(const SingleInstance&);
SingleInstance& operator=(const SingleInstance&);
private:
class Deletor {
public:
~Deletor() {
if(SingleInstance::instance != NULL)
delete SingleInstance::instance;
}
};
static Deletor deletor;
public:
static SingleInstance* getInstance() {
if(instance == NULL) {
instance = new SingleInstance();
}
return instance;
}
};
// init static member
SingleInstance* SingleInstance::instance = NULL;
在程序运行结束时,系统会调用静态成员deletor
的析构函数,该析构函数会删除单例的唯一实例。使用这种方法释放单例对象有以下特征:
- 在单例类内部定义专有的嵌套类。
- 在单例类内定义私有的专门用于释放的静态成员。
- 利用程序在结束时析构全局变量的特性,选择最终的释放时机。
这个代码在单线程环境下是正确无误的,但是当拿到多线程环境下时这份代码就会出现race condition, 要使其线程安全,能在多线程环境下实现单例模式,我们首先想到的是利用同步机制来正确的保护我们的shared data。
#include <pthread.h>
#include <iostream>
#include <algorithm>
using namespace std;
class SingleInstance {
public:
static SingleInstance* GetInstance() {
if (ins == nullptr) {
pthread_mutex_lock(&mutex);
if (ins == nullptr) {
ins = new SingleInstance();
}
pthread_mutex_unlock(&mutex);
}
return ins;
}
~SingleInstance(){};
//互斥锁
static pthread_mutex_t mutex;
private:
//涉及到创建对象的函数都设置为private
SingleInstance() { std::cout<<"SingleInstance() 懒汉"<<std::endl; }
SingleInstance(const SingleInstance& other) {};
SingleInstance& operator=(const SingleInstance& other) { return *this; }
//静态成员
static SingleInstance* ins;
};
//懒汉式 静态变量需要定义
SingleInstance* SingleInstance::ins = nullptr;
pthread_mutex_t SingleInstance::mutex;
int main(){
//因为不能创建对象所以通过静态成员函数的⽅法返回静态成员变量
SingleInstance* ins = SingleInstance::GetInstance();
delete ins;
return 0;
}
//输出 SingleInstance() 懒汉
工厂模式
⼀般情况下,工厂模式分为三种更加细分的类型:简单工厂、工厂⽅法和抽象工厂。
简单工厂模式
可以根据实际的参数不同返回不同的实例。同时在简单工厂模式中会定义⼀个类负责创建其他 类的实例,被创建的实例也通常具有共同的⽗类。简单工厂模式的实质是由⼀个工厂根据传⼊的参数,动态决定应该创建哪⼀个产品类(这些产品类继承⾃⼀个⽗类或接接口)的实例。
#include <iostream>
#include <pthread.h>
using namespace std;
//产品类(抽象类,不能实例化)
class Product{
public:
Product(){};
virtual void show()=0; //纯虚函数
};
class productA : public Product{
public:
productA(){};
void show(){ std::cout << "product A create!" << std::endl; };
~productA(){};
};
class productB : public Product{
public:
productB(){};
void show(){ std::cout << "product B create!" << std::endl; };
~productB(){};
};
class simpleFactory{ // ⼯⼚类
public:
simpleFactory(){};
Product* product(const string str){
if (str == "productA")
return new productA();
if (str == "productB")
return new productB();
return NULL;
};
};
int main(){
simpleFactory obj; // 创建⼯⼚
Product* pro; // 创建产品
pro = obj.product("productA");
pro->show(); // product A create!
delete pro;
pro = obj.product("productB");
pro->show(); // product B create!
delete pro;
return 0;
}
工厂模式⽬的就是代码解耦,如果我们不采⽤工厂模式,如果要创建产品 A、B,通常做法采⽤⽤ switch…case语句,那么想⼀想后期添加更多的产品进来,我们不是要添加更多的switch…case 吗?这样就很麻烦,⽽且也不符合设计模式中的开放封闭原则。
抽象工厂模式
为了进⼀步解耦,在简单工厂的基础上发展出了抽象工厂模式,即连工厂都抽象出来,实现了 进⼀步代码解耦。
#include <iostream>
#include <pthread.h>
using namespace std;
//产品类(抽象类,不能实例化)
class Product{
public:
Product(){}
virtual void show()=0; //纯虚函数
};
class Factory{//抽象类
public:
virtual Product* CreateProduct()=0;//纯虚函数
};
//产品A
class ProductA:public Product{
public:
ProductA(){}
void show(){ std::cout<<"product A create!"<<std::endl; };
};
//产品B
class ProductB:public Product{
public:
ProductB(){}
void show(){ std::cout<<"product B create!"<<std::endl; };
};
//⼯⼚类A,只⽣产A产品
class FactorA: public Factory{
public:
Product* CreateProduct(){
Product* product_ = nullptr;
product_ = new ProductA();
return product_;
}
};
//⼯⼚类B,只⽣产B产品
class FactorB: public Factory{
public:
Product* CreateProduct(){
Product* product_ = nullptr;
product_ = new ProductB();
return product_;
}
};
int main(){
Product* product_ = nullptr;
auto MyFactoryA = new FactorA();
product_ = MyFactoryA->CreateProduct();// 调⽤产品A的⼯⼚来⽣产A产品
product_->show();
delete product_;
auto MyFactoryB=new FactorB();
product_ = MyFactoryB->CreateProduct();// 调⽤产品B的⼯⼚来⽣产B产品
product_->show();
delete product_;
return 0;
}
//输出
//product A create! product B create!
观察者模式
观察者模式定义⼀种⼀(被观察类)对多(观察类)的关系,让多个观察对象同时监听⼀个 被观察对象,被观察对象状态发⽣变化时,会通知所有的观察对象,使他们能够更新⾃⼰的状态。 观察者模式中存在两种⻆⾊:
-
**观察者:**内部包含被观察者对象,当被观察者对象的状态发⽣变化时,更新⾃⼰的状态。(接收通知更新状态)
-
**被观察者:**内部包含了所有观察者对象,当状态发⽣变化时通知所有的观察者更新⾃⼰的状态。(发送通知)
应⽤场景:
-
当⼀个对象的改变需要同时改变其他对象,且不知道具体有多少对象有待改变时,应该考虑使⽤观察者模式;
-
⼀个抽象模型有两个⽅⾯,其中⼀⽅⾯依赖于另⼀⽅⾯,这时可以⽤观察者模式将这两者封装在独⽴的对象中使它们各⾃独⽴地改变和复⽤。
#include <iostream>
#include <string>
#include <list>
using namespace std;
class Subject;
//观察者 基类 (内部实例化了被观察者的对象sub)
class Observer {
protected:
string name;
Subject *sub;
public:
Observer(string name, Subject *sub) {
this->name = name;
this->sub = sub;
}
virtual void update() = 0;
};
class StockObserver : public Observer {
public:
StockObserver(string name, Subject *sub) : Observer(name, sub){}
void update();
};
class NBAObserver : public Observer {
public:
NBAObserver(string name, Subject *sub) : Observer(name, sub){}
void update();
};
//被观察者 基类 (内部存放了所有的观察者对象,以便状态发⽣变化时,给观察者发通知)
class Subject {
protected:
std::list<Observer *> observers;
public:
string action; //被观察者对象的状态
virtual void attach(Observer *) = 0;
virtual void detach(Observer *) = 0;
virtual void notify() = 0;
};
class Secretary : public Subject {
void attach(Observer *observer) {
observers.push_back(observer);
}
void detach(Observer *observer) {
list<Observer *>::iterator iter = observers.begin();
while (iter != observers.end()) {
if ((*iter) == observer) {
observers.erase(iter);
return;
}
++iter;
}
}
void notify() {
list<Observer *>::iterator iter = observers.begin();
while (iter != observers.end()) {
(*iter)->update();
++iter;
}
}
};
void StockObserver::update() {
cout << name << " 收到消息:" << sub->action << endl;
if (sub->action == "⽼板来了!") {
cout << "我⻢上关闭股票,装做很认真⼯作的样⼦!" << endl;
}
}
void NBAObserver::update() {
cout << name << " 收到消息:" << sub->action << endl;
if (sub->action == "⽼板来了!") {
cout << "我⻢上关闭 NBA,装做很认真⼯作的样⼦!" << endl;
}
}
int main()
{
Subject *BOSS = new Secretary();
Observer *xa = new NBAObserver("xa", BOSS);
Observer *xb = new NBAObserver("xb", BOSS);
Observer *xc = new StockObserver("xc", BOSS);
BOSS->attach(xz);
BOSS->attach(xb);
BOSS->attach(xc);
BOSS->action = "去吃饭了!";
BOSS->notify();
cout << endl;
BOSS->action = "⽼板来了!";
BOSS->notify();
return 0;
}
//输出
//product A create! product B create!
装饰器模式
装饰器模式(Decorator Pattern)允许向⼀个现有的对象添加新的功能,同时⼜不改变其结构。
这种类型的设计模式属于结构型模式,它是作为现有的类的⼀个包装。
如下代码中没有改变 Car 类的内部结构,还为其增加了新的功能,这就是装饰器模式的作⽤。
#include <iostream>
#include <list>
#include <memory>
using namespace std;
//抽象构件类 Transform (变形⾦刚)
class Transform{
public:
virtual void move() = 0;
};
//具体构件类Car
class Car : public Transform{
public:
Car(){
std::cout << "变形⾦刚是⼀辆⻋!" << endl;
}
void move(){
std::cout << "在陆地上移动。" << endl;
}
};
//抽象装饰类
class Changer : public Transform{
public:
Changer(shared_ptr<Transform> transform){
this->transform = transform;
}
void move(){
transform->move();
}
private:
shared_ptr<Transform> transform;
};
//具体装饰类Robot
class Robot : public Changer{
public:
Robot(shared_ptr<Transform> transform) : Changer(transform){
std::cout << "变成机器⼈!" << std::endl;
}
void say(){
std::cout << "说话!" << std::endl;
}
};
//具体装饰类AirPlane
class Airplane : public Changer{
public:
Airplane(shared_ptr<Transform> transform) : Changer(transform){
std::cout << "变成⻜机!" << std::endl;
}
void say(){
std::cout << "在天空⻜翔!" << std::endl;
}
};
int main(void){
shared_ptr<Transform> camaro = make_shared<Car>();
camaro->move();
std::cout << "--------------" << endl;
shared_ptr<Robot> bumblebee = make_shared<Robot>(camaro);
bumblebee->move();
bumblebee->say();
std::cout << "--------------" << endl;
shared_ptr<Airplane> bumblebee1 = make_shared<Airplane>(camaro);
bumblebee1->move();
bumblebee1->say();
return 0;
}
/*
输出
变形⾦刚是⼀辆⻋!
在陆地上移动。
--------------
变成机器⼈!
在陆地上移动。
说话!
--------------
变成⻜机!
在陆地上移动。
在天空⻜翔!
*/
装饰器模式的优点:
1、可以轻松对已存在的对象进行修改和包装,在被装饰者的前面或者后面添加自己的行为,而无需修改原对象。
2、可以动态、不限量地进行装饰,可以更灵活地扩展功能。
相对地,装饰器模式有很明显的缺点:
1、会加入大量的小类,即使只添加一个功能,也要额外创建一个类,使得程序更复杂。
2、增加代码复杂度,使用装饰器模式不但需要实例化组件,还要把组件包装到装饰者中。