目錄
- 常見設計模式
- 如何保證單例模式只有一個實例
- 單例模式中的懶漢與餓漢模式
- OOP設計模式的五項原則
- 單例模式中的懶漢加載,如果并發訪問該怎么做
常見設計模式
單例模式:
單例模式主要解決了一個全局使用的類頻繁的創建和銷毀的問題。
單例模式下確保某一個類只有一個實例,并且自行實例化并向整個系統提供這個實例。
單例模式有三要素:
1、某個類只能有一個實例
2、它必須自行創建這個實例
3、它必須自行向整個系統提供這個實例
優點:
1、內存里只有一個實例,減少了內存的開銷,尤其是頻繁的創建和銷毀實例
2、避免了對資源的多重占用。
缺點:
沒有接口,不能繼承,與單一職責原則沖突,一個類應該只關心內部邏輯,而不關心外面怎么來實例化它。
使用場景:
1、要求生產唯一序列號
2、創建一個對象消耗的資源過多,如I/O與數據庫的鏈接等。
工廠模式:
主要解決接口選擇的問題。該模式下定義一個創建對象的接口,讓子類自己決定實例化哪一個工廠類,
也就是讓創建過程延遲到子類進行。
工廠模式的優點:
解耦,代碼復用,更改功能容易。
觀察者模式:
定義對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,讓所有依賴于它的對象都得到通知并且自動更新。
觀察者模式中分為觀察者和被觀察者,當被觀察者發生裝填改變時,觀察者會受到通知。
主要是為了解決對象狀態改變給其他對象通知的問題,實現:類似于觀察者在被觀察者那邊注冊了一個回調函數。
裝飾器模式:
對于已經存在的某些類進行裝飾,以此來擴展一些功能,從而動態的為一個對象增加新的功能。
裝飾器模式是一種用于代替繼承的技術,無需通過繼承增加子類就能擴展對象的新功能。
優點:裝飾類和被裝飾類可以獨立發展,不會相互耦合,裝飾模式是繼承的一個替代模式,裝飾模式可以動態擴展一個實現類的功能。
缺點:多層裝飾比較復雜
使用場景:1、擴展一個類的功能 2、動態增加功能、動態撤銷功能。
如何保證單例模式只有一個實例
1、將該類的構造方法定義為私有方法,這樣其他處的代碼無法通過調用構造函數來實例化該類的對象,只有通過類提供的靜態方法來得到該類的唯一實例。
2、在該類內部提供一個靜態方法,當調用這個方法時,如果類持有的引用不為空,就返回這個引用。如果類的引用為空,就創建這個類的實例。
C++的實現有兩種,一種通過局部靜態變量,利用其只初始化一次的特點,返回對象。另外一種,則是定義全局的指針,getInstance判斷該指針是否為空,為空時才實例化對象。
單例模式中的懶漢與餓漢模式
懶漢模式:在類加載的時候不被初始化。
像一個懶漢一樣,需要用到創建實例了的程序再去創建實例,不需要創建實例程序就不去創建實例,這是一個時間換空間的做法,同時體現了懶漢本性。
實現方法:定義一個單例類,使用類的私有靜態指針變量指向類的唯一實例,并用一個公有的靜態方法獲取該實例。
#include <iostream>
#include <stdlib.h>
using namespace std;class singleton //實現單例模式的類
{
private:singleton() //私有的構造函數,這樣就不能再其他地方創建該實例{}static singleton* instance; //定義一個唯一指向實例的指針,并且是私有的static int b;
public:static singleton* GetInstance() //定義一個公有函數,可以獲取這個唯一實例{if (instance == NULL) //判斷是不是第一次使用instance = new singleton;return instance;}static void show(){cout << b << endl;}
};
int singleton::b = 10; //靜態成員變量在類外進行初始化,它是整個類的一部分并不屬于某個對象
singleton* singleton::instance = NULL;
int main()
{singleton* a1 = singleton::GetInstance();cout << a1 << endl;a1->show();singleton* a2 = singleton::GetInstance();cout << a2 << endl;a2->show();system("pause");return 0;
}
實例的兩個對象的地址都是一樣的
懶漢模式的singleton類有以下特點:
1、 他有一個指向唯一實例的靜態指針,并且是私有的
2、它有一個公有的函數,可以獲取這個唯一的實例,并且在需要的時候創建該實例
3、它的構造函數是私有的,這樣就不能從別處創建該類的實例
但是這存在一個缺點,也就是說它在單線程下是正確的,但是在多線程情況下,如果兩個線程同時首次調用GetInstance()方法,那么就會同時監測到instance為NULL,則兩個線程會同時構造一個實例給instance,這樣就會發生錯誤。
餓漢模式
單例類定義的時候就進行實例化
在餓漢模式中,實例對象儲存在全局數據區,所以要用static來修飾,所以對于餓漢模式來說,是線程安全的,因為在線程創建之前實例就已經創建好了。
#include <iostream>
#include <stdlib.h>
using namespace std;class singleton
{
private:static singleton* instance; //這是我們的單例對象,它是一個類對象的指針singleton(){cout << "創建一個單例對象" << endl;}~singleton(){cout << "析構掉一個單例對象" << endl;}
public:static singleton* getinstance();static void deleteInstance(); //用來銷毀實例
};
//下面這個靜態成員變量在類加載的時候就已經初始化好了
singleton* singleton::instance = new singleton();
singleton* singleton::getinstance()
{return instance; //直接返回inatance
}void Singleton::deleteInstance(){delete instance;
}
int main()
{cout << "we get the instance" << endl;singleton* a1 = singleton::getinstance();singleton* a2 = singleton::getinstance();singleton* a3 = singleton::getinstance();cout << "we destroy the instance" << endl;Singleton::deleteInstance();system("pause");return 0;
}
全局數據區中,存儲的并不是一個實例對象,而是一個實例對象的指針,它是一個地址而已,我們真正占有資源的實例對象是存在堆中,我們需要手動的去調用delete釋放申請的資源。
餓漢模式:在類加載時就完成了初始化,但是加載比較慢,獲取對象比較快。
餓漢模式是線程安全的,在類創建好一個靜態對象提供給系統使用。
OOP設計模式的五項原則
1、單一職責原則
避免將相同的職責分散到不同的類中。避免一個類承擔太多職責。減少類的耦合,提高類的復用性。
2、接口隔離原則
使用多個專門的接口,而不是使用單一的接口。
3、開放——封閉原則
open:模塊的行為必須是開放的,支持擴展的。
closed:模塊在擴展時不應該影響已有的程序模塊
4、替換原則
子類型必須能夠替換掉他們的父類型、并且出現在父類能夠出現的地方
5、依賴倒置原則
上層模塊不應該依賴于下層模塊,他們應該共同依賴于一個抽象。
單例模式中的懶漢加載,如果并發訪問該怎么做
使用鎖機制,防止多次訪問。
第一次判斷為空不加鎖。
若為空,則進行加鎖判斷是否為空,若為空則生成對象。