C++之23種經典設計模式(一)
- C++之23種經典設計模式(一)
-
- 單例模式(Singleton)
- 原型模式(Prototype)
- 工廠模式(Factory)
C++之23種經典設計模式(一)
在偶然的機會裡,還是決定整理一下有關程式中最為經典的GOF23種設計模式,友善以後自己查閱,此部落格在于整理,下面是我參考的連結:https://blog.csdn.net/nsjim/article/details/92713881
單例模式(Singleton)
定義:
指一個類隻有一個執行個體,且該類能自行建立這個執行個體的一種模式。
特點
單例模式有 3 個特點:
單例類隻有一個執行個體對象;
該單例對象必須由單例類自行建立,其構造函數是私有的;
單例類對外提供一個通路該單例的全局通路點;
代碼示例:
懶漢式單例模式:
懶漢式的特點是延遲加載,比如配置檔案,采用懶漢式的方法,顧名思義,懶漢麼,很懶的,配置檔案的執行個體直到用到的時候才會加載。。。。。。
class CSingleton
{
public:
static CSingleton* GetInstance()
{
if ( m_pInstance == NULL )
m_pInstance = new CSingleton();
return m_pInstance;
}
private:
CSingleton(){};
static CSingleton * m_pInstance;
class CGarbo //它的唯一工作就是在析構函數中删除CSingleton的執行個體
{
public:
~CGarbo()
{
if(CSingleton::m_pInstance)
delete CSingleton::m_pInstance;
}
};
static CGarbo Garbo; //定義一個靜态成員變量,程式結束時,系統會自動調用它的析構函數
};
餓漢式:
餓漢式的特點是一開始就加載了,如果說懶漢式是“時間換空間”,那麼餓漢式就是“空間換時間”,因為一開始就建立了執行個體,是以每次用到的之後直接傳回就好了。
#include "pch.h"
#include "iostream"
using namespace std;
class CSingleton
{
public:
static CSingleton& GetInstance();
protected:
CSingleton();
~CSingleton();
private:
static CSingleton _instance;
private:
//CLock m_lkMsBsGPSInfoStartFlag;
bool m_bMsBsGPSInfoStartFlag; //
public:
bool GetMsBsGPSInfoStart();
bool SetMsBsGPSInfoStart(bool bIsStart);
};
CSingleton CSingleton::_instance;
CSingleton::CSingleton() : m_bMsBsGPSInfoStartFlag(false)
{
std::cout << "enter CSingleton::CSingleton() " << endl;
}
CSingleton::~CSingleton()
{
std::cout << "enter CSingleton::~CSingleton() " << endl;
}
CSingleton& CSingleton::GetInstance()
{
std::cout << "CSingleton::GetInstance()" << endl;
return _instance;
}
bool CSingleton::SetMsBsGPSInfoStart(bool bIsStart)
{
m_bMsBsGPSInfoStartFlag = bIsStart;
return true;
}
int main()
{
return 0;
}
};
程式輸出結果:
上述代碼沒有調用GetInstance,但是也調用了構造跟析構函數,因為單例中的_instance是
static CSingleton _instance;
在執行以下代碼的時候,會去調用它的構造函數,這就是餓漢單例的模式,在你還沒使用的時候就為你建立好這個單例。
CSingleton CSingleton::_instance;
在餓漢式的單例類中,其實有兩個狀态,單例未初始化和單例已經初始化。假設單例還未初始化,有兩個線程同時調用GetInstance方法,這時執行 m_pInstance == NULL 肯定為真,然後兩個線程都初始化一個單例,最後得到的指針并不是指向同一個地方,不滿足單例類的定義了,是以餓漢式的寫法會出現線程安全的問題!在多線程環境下,要對其進行修改。
多線程下的單例模式
這裡要處理的是懶漢模式
class Singleton
{
private:
static Singleton* m_instance;
Singleton(){}
class CGarbo //它的唯一工作就是在析構函數中删除CSingleton的執行個體
{
public:
~CGarbo()
{
if(CSingleton::m_pInstance)
delete CSingleton::m_pInstance;
}
};
static CGarbo Garbo; //定義一個靜态成員變量,程式結束時,系統會自動調用它的析構函數
public:
static Singleton* getInstance();
};
Singleton* Singleton::getInstance()
{
if(NULL == m_instance)
{
Lock();//借用其它類來實作,如boost
if(NULL == m_instance)
{
m_instance = new Singleton;
}
UnLock();
}
return m_instance;
}
單例模式的原文連結:
https://blog.csdn.net/zhanghuaichao/article/details/79459130
原型模式(Prototype)
定義
原型(Prototype)模式的定義如下:用一個已經建立的執行個體作為原型,通過複制該原型對象來建立一個和原型相同或相似的新對象。
特點
原型執行個體指定了要建立的對象的種類。用這種方式建立對象非常高效,根本無須知道對象建立的細節。
應用場景
對象之間相同或相似,即隻是個别的幾個屬性不同的時候。
對象的建立過程比較麻煩,但複制比較簡單的時候。
想必很多人都玩過魔獸争霸3。裡面有一個非常厲害的英雄叫做劍聖。這個英雄攻擊力算是最高的,而且有個聽起來很叼的魔法,“劍刃風暴”,這個魔法其實效果一般,不大實用。
劍聖有一個分身的功能。這個分身各個方面和劍聖都一模一樣,但是沒有攻擊力,也沒有魔法。
如何實作這個分身的功能呢?使用原型模式。原型模式就是這麼一個道理,通過現有的東西,用Clone再複制拷貝出一個或者多個。
使用原型模式理由有三:
1 這個分身是遊戲過程中動态生成的。劍聖使用了這個功能就動态生成分身。
2 這個分身幾乎就是劍聖的拷貝。如果重新new一個劍聖不符合遊戲規則。玩過War3的人都知道一個種族隻能有一個劍聖吧,如果可以有3個劍聖,讓其它族怎麼玩。
3 重新建立一個劍聖很麻煩,要初始化各種參數,如等級啊,攻擊力啊,等等各個參數,而且這些參數要和原型一緻。直接通過原型模式實作則簡單。
4 便于擴充。如果分身有30%原劍聖的攻擊力,那麼我們直接修改Clone方法即可。對外的接口不用變更。
原文連結:
https://blog.csdn.net/faithzzf/article/details/78062779
代碼示例:
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class Person
{
public:
string name;
int age;
Person(string _name, int _age) :name(_name), age(_age) {}
virtual ~Person() {}
virtual void showMe()
{
cout << "I am " << name << ", and " << age << endl;
}
virtual Person *clone()
{
return new Person(*this);
}
};
class Boy :public Person
{
public:
Boy(string _name, int _age) :Person(_name, _age)
{}
~Boy() {}
virtual void showMe() override
{
Person::showMe();
cout << "I am a boy" << endl;
}
virtual Person *clone() override
{
return new Boy(*this);
}
};
class Girl :public Person
{
public:
Girl(string _name, int _age) :Person(_name, _age)
{}
~Girl() {}
virtual void showMe() override
{
Person::showMe();
cout << "I am a Girl" << endl;
}
virtual Person *clone() override
{
return new Girl(*this);
}
};
int main()
{
//建立一個boy a
Person *a = new Boy(string("Ming"), 28);
a->showMe();
//建立一個Girl b
Person *b = new Girl(string("Li"), 28);
b->showMe();
//克隆a--不使用原型模式的寫法
shared_ptr<Person> cloneA(new Boy(*dynamic_cast<Boy *>(a)));
cloneA->showMe();
//克隆b--使用原型模式的寫法
shared_ptr<Person> cloneB(b->clone());
cloneB->showMe();
delete b;
b = nullptr;
delete a;
a = nullptr;
delete cloneC;
return 0;
}
工廠模式(Factory)
定義
工廠方法(FactoryMethod)模式的定義:定義一個建立産品對象的工廠接口,将産品對象的實際建立工作推遲到具體子工廠類當中。這滿足建立型模式中所要求的“建立與使用相分離”的特點。
我們把被建立的對象稱為“産品”,把建立産品的對象稱為“工廠”。如果要建立的産品不多,隻要一個工廠類就可以完成,這種模式叫“簡單工廠模式”,它不屬于 GoF 的 23 種經典設計模式,它的缺點是增加新産品時會違背“開閉原則”。
優點
使用者隻需要知道具體工廠的名稱就可得到所要的産品,無須知道産品的具體建立過程;
在系統增加新的産品時隻需要添加具體産品類和對應的具體工廠類,無須對原工廠進行任何修改,滿足開閉原則;
缺點
每增加一個産品就要增加一個具體産品類和一個對應的具體工廠類,這增加了系統的複雜度。
應用場景
客戶隻知道建立産品的工廠名,而不知道具體的産品名。如 TCL 電視工廠、海信電視工廠等。
建立對象的任務由多個具體子工廠中的某一個完成,而抽象工廠隻提供建立産品的接口。
客戶不關心建立産品的細節,隻關心産品的品牌。
工廠模式也分為三種模式:
-
第一為簡單工廠模式
簡單工廠模式,工廠類是建立産品的,它決定建立哪一種産品,就像上司決定采用那種技術方案樣。舉個例子,現在有寶馬車和奔馳車兩種車需要生産,但是隻有一個工廠,且隻能在同一時間生産一種車,這時就有工廠決定生産那種車了。例子雖然不是十分恰當,但是會其意即可。我們直接看UML類圖和代碼吧。
代碼示例:
#include <iostream>
using namespace std;
enum CarType { BENZ, BMW };
class Car//車類
{
public:
virtual void createdCar(void) = 0;
virtual ~Car() {}
};
class BenzCar : public Car //奔馳車
{
public:
BenzCar()
{
cout << "Benz::Benz()" << endl;
}
virtual void createdCar(void)
{
cout << "BenzCar::createdCar()" << endl;
}
~BenzCar()
{
cout << "BenzCar::DestroyCar" << std::endl;
}
};
class BmwCar : public Car //寶馬車
{
public:
BmwCar()
{
cout << "Bmw::Bmw()" << endl;
}
virtual void createdCar(void)
{
cout << "BmwCar::createdCar()" << endl;
}
~BmwCar()
{
cout << "BmwCar::DestroyCar" << std::endl;
}
};
class CarFactory //車廠
{
public:
Car* createSpecificCar(CarType type)
{
switch (type)
{
case BENZ://生産奔馳車
return (new BenzCar());
break;
case BMW://生辰寶馬車
return (new BmwCar());
break;
default:
return NULL;
break;
}
}
};
int main(int argc, char** argv)
{
CarFactory carfac;
Car* specificCarA = carfac.createSpecificCar(BENZ);//看到網上衆多示例在new後沒有delete,感覺不是特别嚴謹
Car* specificCarB = carfac.createSpecificCar(BMW);
delete specificCarA;
delete specificCarB;
return 0;
}
-
工廠方法模式
工廠方法模式:不再隻由一個工廠類決定那一個産品類應當被執行個體化,這個決定權被交給子類去做。當有新的産品(新型汽車)産生時,隻要按照抽象産品角色、抽象工廠角色提供的方法來生成即可(新車型可以用一個新類繼承建立産品即可),那麼就可以被客戶使用,而不必去修改任何已有的代 碼。可以看出工廠角色的結構也是符合開閉原則。如下面UML類圖:
#include <iostream>
using namespace std;
class Car//車類
{
public:
virtual void createdCar(void) = 0;
virtual ~Car() {}
};
class BenzCar : public Car //奔馳車
{
public:
BenzCar()
{
cout << "Benz::Benz()" << endl;
}
virtual void createdCar(void)
{
cout << "BenzCar::createdCar()" << endl;
}
~BenzCar()
{
cout << "BenzCar::DestroyCar()" << endl;
}
};
class BmwCar : public Car //寶馬車
{
public:
BmwCar()
{
cout << "Bmw::Bmw()" << endl;
}
virtual void createdCar(void)
{
cout << "BmwCar::createdCar()" << endl;
}
~BmwCar()
{
cout << "BmwCar::DestroyCar()" << endl;
}
};
class Factory//車廠
{
public:
virtual Car* createSpecificCar(void) = 0;
virtual ~Factory() {}
};
class BenzFactory : public Factory//奔馳車廠
{
public:
virtual Car* createSpecificCar(void)
{
return (new BenzCar());
}
~BenzFactory() { cout << "BenzFactory"; }
};
class BmwFactory : public Factory//寶馬車廠
{
public:
virtual Car* createSpecificCar(void)
{
return (new BmwCar());
}
~BmwFactory() { cout << "BmwFactory"; }
};
int main(int argc, char** argv)
{
shared_ptr<Factory> factoryBenz(new BenzFactory());
Car* specificCarA = factoryBenz->createSpecificCar();
shared_ptr<Factory> factoryBmw(new BmwFactory());
Car* specificCarB = factoryBmw->createSpecificCar();
delete specificCarA;
specificCarA = nullptr;
delete specificCarB;
specificCarB = nullptr;
return 0;
}
3.抽象工廠類
在上面的工廠方法模式基礎上,有需要生産高配版的奔馳和寶馬,那工廠方法模式就有點鞭長莫及了,這就又有抽象工廠模式,UML類圖如下:
代碼如下:
#include <iostream>
using namespace std;
class Car//車類
{
public:
virtual void createdCar(void) = 0;
virtual ~Car() {}
};
class BenzCar : public Car //奔馳車
{
public:
BenzCar()
{
cout << "Benz::Benz()" << endl;
}
virtual void createdCar(void)
{
cout << "BenzCar::createdCar()" << endl;
}
~BenzCar()
{
}
};
class BmwCar : public Car //寶馬車
{
public:
BmwCar()
{
cout << "Bmw::Bmw()" << endl;
}
virtual void createdCar(void)
{
cout << "BmwCar::createdCar()" << endl;
}
~BmwCar() {}
};
class HighCar //高配版車型
{
public:
virtual void createdCar(void) = 0;
virtual ~HighCar() {}
};
class HighBenzCar : public HighCar //高配奔馳車
{
public:
HighBenzCar()
{
cout << "HighBenzCarBenz::Benz()" << endl;
}
virtual void createdCar(void)
{
cout << "HighBenzCar::createdCar()" << endl;
}
~HighBenzCar() {}
};
class HighBmwCar : public HighCar //高配寶馬車
{
public:
HighBmwCar()
{
cout << "HighBmwCar::Bmw()" << endl;
}
virtual void createdCar(void)
{
cout << "HighBmwCar::createdCar()" << endl;
}
~HighBmwCar() {}
};
class Factory//車廠
{
public:
virtual Car* createSpecificCar(void) = 0;
virtual HighCar* createdSpecificHighCar(void) = 0;
virtual ~Factory() {}
};
class BenzFactory : public Factory//奔馳車廠
{
public:
virtual Car* createSpecificCar(void)
{
return (new BenzCar());
}
virtual HighCar* createdSpecificHighCar(void)
{
return (new HighBenzCar());
}
~BenzFactory() {}
};
class BmwFactory : public Factory//寶馬車廠
{
public:
virtual Car* createSpecificCar(void)
{
return (new BmwCar());
}
virtual HighCar* createdSpecificHighCar(void)
{
return (new HighBmwCar());
}
~BmwFactory() {}
};
int main(int argc, char** argv)
{
Factory* factory = new BenzFactory();
Car* specificCar = factory->createSpecificCar();
HighCar* spcificHighCar = factory->createdSpecificHighCar();
delete factory;
delete specificCar;
delete spcificHighCar;
return 0;
}