共计 6627 个字符,预计需要花费 17 分钟才能阅读完成。
设计模式
设计模式:是指在软件开发中,通过验证的,用于解决特定环境下、反复重现的、特定问题的解决方案
咱们要留神:不要为了套用设计模式而应用设计模式,在业务遇到问题时,要自然而然想到设计模式作为一种解决方案
为什么要学设计模式
- 设计模式曾经成为软件开发人员的规范词汇
- 学习设计模式是集体技术能力进步的路径
- 不必反复造轮子
- 各种源码当中充斥着各种设计模式
观察者模式
定义对象间的一种一对多(变换)的依赖关系,以便当一个对象(subject)的状态产生扭转时,所有依赖于它的对象都失去告诉并且自动更新
应用场景
当用户订阅了某种音讯,当音讯有扭转的时候,就会告诉用户音讯状态的扭转,并且执行用户对应音讯扭转所须要的行为
要点
- 应用面向对象的形象,observer 模式使咱们能够独立的扭转指标与观察者,从而使得两者之间的依赖能够达到松耦合。
- 指标指定发送告诉时,无需指定观察者,告诉会主动流传
- 观察者能够决定是否须要订阅告诉,指标对象对此无所不知。
推模式和拉模式
- 推模式:指标对象向观察者推送指标的详细信息,不论观察者是否须要,相当于计算机网络中的播送。
- 拉模式:指标在告诉观察者的时候只传递大量信息。如果观察者须要更具体的信息,应该是观察者本身向指标对象获取。
源码
#include <iostream> | |
#include <list> | |
#include <string> | |
using namespace std; | |
class Subject; | |
class Observer | |
{ | |
public: | |
Observer() {} | |
virtual ~Observer() {} | |
virtual void update(Subject *sj) = 0; | |
virtual void update(string content) = 0; | |
}; | |
class Subject | |
{ | |
public: | |
Subject() {} | |
virtual ~Subject() {} | |
virtual string getcontent() = 0; | |
virtual string getAbstractContent() = 0; | |
void attach(Observer *ob) | |
{observers.push_back(ob); | |
} | |
void detach(Observer *ob) | |
{observers.remove(ob); | |
} | |
virtual void notifyObservers() | |
{for (Observer *ob : observers) | |
{ob->update(this); // 拉模型,具体数据让观察者本人去取 | |
} | |
} | |
virtual void notifyObservers(string content) | |
{for (Observer *ob : observers) | |
{ob->update(content); // 推模型 数据是由被观察者抉择 | |
} | |
} | |
private: | |
list<Observer *> observers; | |
}; | |
class Reader : public Observer | |
{ | |
public: | |
Reader(string name) : _readername(name) {} | |
virtual ~Reader() {} | |
virtual void update(Subject *sj) | |
{cout << _readername << "开始浏览整个" << sj->getcontent() << endl; | |
} | |
virtual void update(string content) | |
{cout << _readername << "开始浏览报纸简介" << endl;} | |
private: | |
string _readername; | |
}; | |
class paper : public Subject | |
{ | |
public: | |
paper() {} | |
virtual ~paper() {} | |
void setcontent(string content) | |
{this->content = content;} | |
virtual string getcontent() | |
{return content;} | |
virtual string getAbstractContent() | |
{return "摘要";} | |
private: | |
string content; | |
}; | |
int main() | |
{ | |
paper newpaper; | |
newpaper.setcontent("今日头条"); | |
Reader read1("user1"), read2("user2"); | |
newpaper.attach(&read1); // 订阅 | |
newpaper.attach(&read2); // 订阅 | |
newpaper.notifyObservers(); | |
return 0; | |
} |
工厂模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使得一个类的实例化提早(目标:解耦,伎俩:虚函数)到子类。
动机
- 在软件系统中,常常面临创建对象的工作。因为需要的变换,须要创立的对象的具体类型常常变换。
- 如何应答这种变动?如何绕过惯例的对象创立办法,提供一种 ” 封装机制 ” 来防止客户程序和这种 ” 具体对象创立工作 ” 的紧耦合。
应用场景
- 数据导出各种格局
- 领取接口,可能对应不同的领取网关:支付宝,财付通,网银在线等等。
要点总结
- factory method 模式用于隔离对象的使用者和具体类型之间的耦合关系。面对一个常常变动的具体类型,紧耦合关系会导致软件的软弱
- factory method 模式通过面对对象的形式,将所要创立的具体对象工作提早到子类,从而实现一种扩大的策略,较好的解决了耦合关系。
- factory method 模式解决了单个模式的需要变动。毛病在于要求创立形式 / 参数雷同。
代码
#include <iostream> | |
using namespace std; | |
class ExportFileProduct | |
{ | |
public: | |
ExportFileProduct() {} | |
virtual ~ExportFileProduct() {} | |
virtual bool Export(string data) = 0; | |
}; | |
class ExportTextProduct : public ExportFileProduct | |
{ | |
public: | |
ExportTextProduct() {} | |
virtual ~ExportTextProduct(){}; | |
virtual bool Export(string data) | |
{cout << "导出数据 [" << data << "] 保留成文本的形式" << endl; | |
return true; | |
} | |
}; | |
class ExportDBProduct : public ExportFileProduct | |
{ | |
public: | |
ExportDBProduct() {} | |
virtual ~ExportDBProduct(){}; | |
virtual bool Export(string data) | |
{cout << "导出数据 [" << data << "] 保留成数据库的形式" << endl; | |
return true; | |
} | |
}; | |
class ExportJsonProduct : public ExportFileProduct | |
{ | |
public: | |
ExportJsonProduct() {} | |
virtual ~ExportJsonProduct() {} | |
virtual bool Export(string data) | |
{cout << "导出数据:[" << data << "]保留 Json 的形式" << endl; | |
return true; | |
} | |
}; | |
class ExportFactory | |
{ | |
public: | |
ExportFactory() {} | |
virtual ~ExportFactory() {} | |
bool Export(int type, string data) | |
{ExportFileProduct *product = factoryMethod(type); | |
bool ret = false; | |
if (product) | |
{ret = product->Export(data); | |
} | |
else | |
{cout << "没有对应的类型" << endl;} | |
return ret; | |
} | |
protected: | |
virtual ExportFileProduct *factoryMethod(int type) | |
{ | |
ExportFileProduct *product; | |
if (type == 1) | |
{product = new ExportTextProduct(); | |
} | |
else if (type == 2) | |
{product = new ExportDBProduct(); | |
} | |
else if (type == 3) | |
{product = new ExportJsonProduct(); | |
} | |
return product; | |
} | |
}; |
单例模式
保障一个类只有一个实例,并提供一个该实例的全局拜访点
动机
- 在软件系统中,常常有这样一些非凡的类,必须确保他们在零碎中只存在一个实例,能力确保他们的逻辑正确性,以及良好的效率。
- 绕过惯例的结构器,提供一种机制来保障一个类只有一个实例。
- 类设计者的责任 而不是使用者的责任
常见写法
饿汉式单例
程序开始运行时就创立单例
class Singleton4 | |
{ | |
private: | |
Singleton4() = default; | |
Singleton4(const Singleton4 &s) = delete; | |
Singleton4 &operator=(Singleton4 &s) = delete; | |
static Singleton4 _singleton; | |
private: | |
static Singleton4 *getinstance() | |
{return &_singleton;} | |
}; | |
Singleton4 Singleton4::_singleton; |
懒汉式单例
应用单例时才开始创立
双锁型单例模式
/* 双查看锁,但因为内存读写 reorder 不平安 因为 C ++ 创建对象时,会执行 1、分配内存,2 调用结构,3 赋值操作三步操作,然而古代 CPU 和编译器高并发下可能会进行乱序重排操作,因此创建对象 new CSingleton 的第 2 步可能会晚于第 3 步进行指令调用,因此导致呈现未定义的的行为。*/ | |
class Singleton3 | |
{ | |
private: | |
static Singleton3 *_singleton; | |
static mutex _mutex; | |
Singleton3() = default; | |
Singleton3(const Singleton3 &s) = delete; | |
Singleton3 &operator=(const Singleton3 &s) = delete; | |
class GarbageCollector | |
{ | |
public: | |
~GarbageCollector() | |
{cout << "~GarbageCollector()\n"; | |
if (Singleton3::_singleton) | |
{ | |
cout << "free singleton"; | |
delete Singleton3::_singleton; | |
Singleton3::_singleton = nullptr; | |
} | |
} | |
}; | |
static GarbageCollector _gc; // 模仿 gc 来回收单例 | |
public: | |
static Singleton3 *getinstance() | |
{//Singleton* tmp = m_instance.load(std::memory_order_relaxed); | |
//std::atomic_thread_fence(std::memory_order_acquire);// 获取内存 fence 能够使得高并发下不会呈现内存读写 reorderdd | |
if (_singleton == nullptr) | |
{_mutex.lock(); // 对象的 new 不是原子操作 1、分配内存,2 调用结构,3 赋值操作,到第 3 步的时候才是 m_singleton 非空 | |
// 1、分配内存,2 赋值操作 3 调用结构,到第 2 步的时候才是 m_singleton 非空 | |
if (_singleton == nullptr) | |
{_singleton = new Singleton3(); | |
} | |
_mutex.unlock();} | |
return _singleton; | |
} | |
}; | |
Singleton3 *Singleton3::_singleton = nullptr; | |
Singleton3::GarbageCollector Singleton3::_gc; | |
mutex Singleton3::_mutex; |
在高并发下双锁型单例模式可能因为内存读写 reorder 造成隐患
线程平安型单例
class Singleton2 | |
{ | |
private: | |
static Singleton2 *_singleton; | |
static mutex _mutex; | |
Singleton2() = default; | |
Singleton2(const Singleton2 &s) = delete; | |
Singleton2 &operator=(const Singleton2 &s) = delete; | |
class GarbageCollector | |
{ | |
public: | |
~GarbageCollector() | |
{cout << "~GarbageCollector()\n"; | |
if (Singleton2::_singleton) | |
{ | |
cout << "free singleton"; | |
delete Singleton2::_singleton; | |
Singleton2::_singleton = nullptr; | |
} | |
} | |
}; | |
static GarbageCollector _gc; // 模仿 gc 来回收单例 | |
public: | |
static Singleton2 *getinstance() | |
{_mutex.lock(); // 加锁的粒度大,效率较低,对高并发的拜访 | |
if (_singleton == nullptr) | |
{_singleton = new Singleton2(); | |
} | |
_mutex.unlock(); | |
return _singleton; | |
} | |
}; | |
Singleton2 *Singleton2::_singleton = nullptr; | |
Singleton2::GarbageCollector Singleton2::_gc; | |
mutex Singleton2::_mutex; |
加锁之后在并发高的场景,效率很低,个别不举荐这种写法
线程不平安懒汉式
class Singleton1 | |
{ | |
private: | |
static Singleton1 *_singleton; | |
Singleton1() = default; | |
Singleton1(const Singleton1 &s) = delete; | |
Singleton1 &operator=(const Singleton1 &s) = delete; | |
class GarbageCollector | |
{ | |
// 线程不平安 | |
public: | |
~GarbageCollector() | |
{cout << "~GarbageCollector()\n"; | |
if (Singleton1::_singleton) | |
{ | |
cout << "free singleton"; | |
delete Singleton1::_singleton; | |
Singleton1::_singleton = nullptr; | |
} | |
} | |
}; | |
static GarbageCollector _gc; // 模仿 gc 来回收单例 | |
public: | |
static Singleton1 *getinstance() | |
{if (_singleton == nullptr) | |
{_singleton = new Singleton1(); | |
} | |
return _singleton; | |
} | |
}; | |
Singleton1 *Singleton1::_singleton = nullptr; | |
Singleton1::GarbageCollector Singleton1::_gc; |
局部变量的懒汉式
举荐写法
class Singleton | |
{ | |
private: | |
Singleton(){}; | |
Singleton(const Singleton &s) = delete; | |
Singleton &operator=(Singleton &s) = delete; | |
public: | |
static Singleton *getinstance() | |
{ | |
static Singleton _singleton; | |
return &_singleton; | |
} | |
}; |
有一些坑,如果应用隐式构造函数导致部分动态变量不平安,读者能够自行编译成汇编 看看该局部变量有没有加锁
要点总结
- singleton 模式中实例结构器能够设置为 protected 以容许子类派生
- singleton 模式个别不要反对拷贝构造函数和 clone 接口,因为有可能导致多个实例对象,与 singleton 模式的初衷违反。
- 隐式构造函数导致函数部分动态变量不平安。
正文完