[TOC]
單例模式
保證一個類僅有一個例項,並提供一個該例項的全域性訪問點
在軟體系統中,經常有這樣一些特殊的類,必須保證他們 在系統中只存在一個例項,才能確保它們的邏輯正確性, 以及良好的效率
應用場景:
DBPool 、讀取配置檔案
單例模式分類:
- 1、懶漢式 – 需要使用單例的時候,才進行初始化
- 2、餓漢式 – 未呼叫單例的時候,已經進行初始化
寫一個單例模式的demo
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;
//設計執行緒的個數
#define PTHREAD_NUM 20
//懶漢式 餓漢式 單例模式的選型
#define SINGELTON_SELECTOR 0
//單例模式
#if SINGELTON_SELECTOR
//懶漢式 -- 呼叫的時候才初始化
class Singleton{
private:
Singleton(){
cout<<"Singleton construct 1111\n";
}
~Singleton(){
cout<<"Singleton destruct 1111\n";
}
//禁止拷貝構造
Singleton(const Singleton &si) = delete;
//禁止等號賦值
Singleton & operator=(const Singleton &si) = delete;
public:
static Singleton * getInstance(){
static Singleton m_singleton;
return &m_singleton;
}
};
#else
//餓漢式 -- 呼叫之前就已經初始化好,呼叫的時候直接返回地址
class Singleton{
private:
Singleton(){
cout<<"Singleton construct 2222\n";
}
~Singleton(){
cout<<"Singleton destruct 2222\n";
}
//禁止拷貝構造
Singleton(const Singleton &si) = delete;
//禁止等號賦值
Singleton & operator=(const Singleton &si) = delete;
static Singleton m_singleton;
public:
static Singleton * getInstance(){
return &m_singleton;
}
};
Singleton Singleton::m_singleton;
#endif
//定義一個互斥鎖,保證只有一個執行緒在列印 單例變數的地址
static mutex m;
void print_address()
{
Singleton* singleton = Singleton::getInstance();
m.lock();
cout<<singleton<<endl;
m.unlock();
}
//測試單例模式
void test_singleton()
{
thread threads[PTHREAD_NUM];
for(auto &t : threads)
t = thread(print_address);
for(auto &t : threads)
t.join();
}
int main(int argc,char * argv[])
{
cout<<"main\n";
test_singleton();
}
工廠模式
定義一個用於建立物件的介面,讓子類決定例項化哪一個類。
Factory Method使得一個類的例項化延遲(目的:解耦,手段:虛擬函式)到子類
在軟體系統中,經常面臨著建立物件的工作;由於需求的 變化,需要建立的物件的具體型別經常變化
使用工廠模式提供一種“封裝機制”來避免客戶程式和這種“具 體物件建立工作”的緊耦合 來解決這個問題
應用場景:
- 資料匯出,匯出為Excel,文字,XML
- 支付介面,可能對應不同的支付閘道器
寫一個工廠模式的demo
#include <iostream>
using namespace std;
//工廠模式 -- 模擬一個簡s單檔案的解析方式
//定義一個產品的概念
class Parse_file{
public:
Parse_file(){}
//定義虛解構函式,防止父類指標指向子類物件後,釋放記憶體出現記憶體洩露的問題
virtual ~Parse_file(){}
//定義一個介面,子類負責實現
virtual bool myparse(string data) = 0;
};
//定義實際的產品 text方式解析
class text_parse : public Parse_file{
public:
text_parse(){}
virtual ~text_parse(){}
virtual bool myparse(string data){
cout<<"以 text 的方式儲存 資料"<<data<<endl;
return true;
}
};
//定義實際的產品 xml方式解析
class xml_parse : public Parse_file{
public:
xml_parse(){}
virtual ~xml_parse(){}
virtual bool myparse(string data){
cout<<"以 xml 的方式儲存 資料"<<data<<endl;
return true;
}
};
//定義實際的產品 json方式解析
class json_parse : public Parse_file{
public:
json_parse(){}
virtual ~json_parse(){}
virtual bool myparse(string data){
cout<<"以 json 的方式儲存 資料"<<data<<endl;
return true;
}
};
//定義實際的產品 protobuf方式解析
class protobuf_parse : public Parse_file{
public:
protobuf_parse(){}
virtual ~protobuf_parse(){}
virtual bool myparse(string data){
cout<<"以 protobuf 的方式儲存 資料"<<data<<endl;
return true;
}
};
//定義工廠來生產產品
class factory{
public:
factory(){}
virtual ~factory(){}
//定義工廠的解析方法
//便於子類繼承
virtual bool myparse(int type,string data){
Parse_file * pp = nullptr;
pp = parse_method(type);
int ret = false;
if(pp){
pp->myparse(data);
delete pp;
ret = true;
}
else{
cout<<"no parse function\n";
}
return ret;
}
protected:
//便於子類繼承
virtual Parse_file * parse_method(int type){
Parse_file * pp = nullptr;
if(type == 1){
pp = new text_parse();
}else if(type == 2){
pp = new xml_parse();
}
return pp;
}
};
//擴充套件工廠
class factory2 : public factory{
public:
factory2(){}
virtual ~factory2(){}
protected:
//便於子類繼承
virtual Parse_file * parse_method(int type){
Parse_file * pp = nullptr;
if(type == 3){
pp = new json_parse();
}else if(type == 4){
pp = new protobuf_parse();
}
else{
pp = factory::parse_method(type);
}
return pp;
}
};
int main()
{
factory * fac = new factory();
fac->myparse(1,"資料");
fac->myparse(2,"資料");
fac->myparse(3,"資料");
fac->myparse(4,"資料");
cout<<"\n\n-----------------\n\n";
factory * fac2 = new factory2();
fac2->myparse(1,"資料");
fac2->myparse(2,"資料");
fac2->myparse(3,"資料");
fac2->myparse(4,"資料");
return 0;
}
效果
釋出訂閱模式與觀察者模式
釋出訂閱模式和觀察者模式是同一個東西嗎? NONONO
- 觀察者模式裡,只有兩個角色 —— 觀察者 + 被觀察者
- 釋出訂閱模式裡 —— 觀察者 + 中間經紀人 +被觀察者
觀察者模式中的推模型和拉模型:
推模型:
目標物件主動向觀察者推送目標的詳細資訊,不 管觀察者是否需要,推送的資訊通常是目標物件的全部或 部分資料,相當於廣播通訊。
拉模型:
目標物件在通知觀察者的時候,只傳遞少量的信 息。如果觀察者需要更具體的資訊,由觀察者主動到目標 物件中獲取,相當於是觀察者從目標物件中拉資料。一般 這種模型的實現中,會把目標物件通過update方法傳遞給 觀察者,這樣在觀察者需要獲取資料的時候,就可以通過 這個引用來獲取了。
應用場景:
公眾號通知,淘寶通知,知乎通知,微信通知等等。
寫一個觀察者模式的demo
//觀察者模式,需要弄明白 何為觀察者,何為目標
//以我們用手機看報紙為例, 我們 是觀察者, 報紙是目標
//接下來我們來模擬一下觀察者模式
#include <iostream>
#include <list>
using namespace std;
class subject;
//定義抽象的觀察者
class observer{
public:
observer(){}
virtual ~observer(){}
virtual void update(subject * sub) = 0;//讀摘要
virtual void update(string content) = 0;//讀內容
};
//定義一個抽象的目標
class subject{
public:
subject(){}
virtual ~subject(){}
//設定內容
virtual int setContent(string content)=0;
//得到具體內容 -- 用於推模型
virtual string getContent()=0;
//得到摘要 -- 用於拉模型
virtual string getSummary()=0;
//訂閱
virtual void attach(observer * ob){
oblist.push_back(ob);
}
//取消訂閱
virtual void detach(observer * ob){
oblist.remove(ob);
}
//通知所有訂閱者 -- 推模型
virtual void notifyAllobserver(string content) {
for(auto &a : oblist){
a->update(content);
}
}
//通知所有訂閱者 -- 拉模型
virtual void notifyAllobserver(){
for(observer * reader : oblist){
reader->update(this);
}
}
private:
list<observer *> oblist;
};
//定義具體的 觀察者,讀者
class reader: public observer
{
public:
reader(){}
virtual ~reader(){}
//拉模型
virtual void update(subject * sub){
cout<<getName()<<"正在閱讀的內容是:"<<sub->getContent()<<endl;
}
//推模型
virtual void update(string content){
cout<<getName()<<"正在閱讀的內容是:"<<content<<endl;
}
string getName(){return m_name;}
void setName(string name){m_name = name;}
private:
string m_name;
};
//定義具體的目標,推送新聞資訊
class newspaper:public subject
{
public:
newspaper(){};
virtual ~newspaper(){}
//設定內容
virtual int setContent(string content){
m_content = content;
notifyAllobserver(); //預設是拉模型,就想給你推送一個摘要一樣
return 1;
}
//得到具體內容 -- 用於推模型
virtual string getContent(){
return m_content;
}
//得到摘要 -- 用於拉模型
virtual string getSummary(){
return "摘要";
}
private:
string m_content;
};
int main(int argc,char *argv[])
{
//定義報紙主題
newspaper *subject = new newspaper();
//定義讀者
reader * r1 = new reader();
r1->setName("adele");
reader * r2 = new reader();
r2->setName("Bob");
reader * r3 = new reader();
r3->setName("ceilina");
//設定內容
//報紙開始加入訂閱者
subject->attach(r1);
subject->setContent("今天多雲");
cout << "\n----------華麗的分割線 \n"<<endl;
subject->attach(r2);
subject->setContent("今天晴天");
cout << "\n----------華麗的分割線 \n"<<endl;
subject->attach(r3);
subject->setContent("over");
cout<<"-------end-----\n";
return 0;
}
效果
作者:小魔童哪吒
本作品採用《CC 協議》,轉載必須註明作者和本文連結