單例模式
單例模式保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。通常我們可以讓一個全局變量使得一個對象被訪問,但它不能阻止你實例化多個對象。一個最好的辦法是,讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例可以被創建,並且它可以提供一個訪問該實例的方法。
也就是說,很多時候我們需要全局的對象,如一個工程中,數據庫訪問對象只有一個,這時,可以考慮使用單例模式。單例模式比全局對象好還包括:單例類可以繼承,如下例中的C++代碼。
單例模式的關鍵點在於:構造函數私有,靜態的GetInstance。
另外,在C++中必須注意內存的釋放。C++、Java、C#中還要注意多線程時的同步問題,另外在多線程可以以合適的方式保證共享變量僅初始化一次
以下列出懶漢式的單例模式
C++實現:
class Singleton { public: static Singleton * GetInstance() { if(NULL == m_pInstance) m_pInstance = new Singleton(); return m_pInstance; } static void Release() //必須,否則會導致內存泄露 { if(NULL != m_pInstance) { delete m_pInstance; m_pInstance = NULL; } } protected: Singleton() { cout<<"C++ Singleton"<<endl; }; static Singleton * m_pInstance; }; Singleton* Singleton::m_pInstance = NULL; class SingleDraw:public Singleton { public: static SingleDraw* GetInstance() { if(NULL == m_pInstance) m_pInstance = new SingleDraw(); return (SingleDraw*)m_pInstance; } protected: SingleDraw() { cout<<"C++ SingleDraw"<<endl; } }; int main() { SingleDraw* s1 = SingleDraw::GetInstance(); SingleDraw* s2 = SingleDraw::GetInstance(); s2->Release(); return 0; }
Java實現(這里僅給出類實現部分):
public class Singleton{ private static Singleton instance=NULL; private Singleton(){ System.out.println("Java Singleton"); } public static Singleton getInstance(){ if(instance == NULL) instance = new Singleton(); return instance; } }
還有另一種形式: 餓漢單例,也就是,類被加載的時候就已經創建好了單例,相比懶漢式只是加載new的地方發生了改變,這里代碼重略
餓漢式和懶漢式的比較:
1 餓漢式單例,由於類被加載的時候就將自己實例化,所以,從資源利用的角度來說,餓漢式單例比懶漢式單例效率更差
2 懶漢式單例在實例化的時候,必須處理好多個線程同時引用造成的訪問限制問題.也就是,很有可能有兩個線程同時去調用了這個獲取單例的方法,造成了單例被創建了多次,懶漢式單例模式線程安全問題:假如現在有兩個線程A和線程B,線程A執行到 this.singletonPattern = new SingletonPattern(),正在申請內存分配,可能需要0.001微秒,就在這0.001微秒之內,線程B執行到if(this.singletonPattern == null),你說這個時候這個判斷條件是true還是false?是true,那然后呢?線程B也往下走,於是乎就在內存中就有兩個SingletonPattern的實例了。所以,在編寫懶漢式單例模式時,應注意線程安全問題(由全局變量及靜態變量引起的),這里可以用互斥同步的方式去解決。(面試可考的地方)
3 餓漢式單例類可以在Java 語言內實現, 但不易在C++ 內實現,因為靜態初始化在C++ 里沒有固定的順序,因而靜態的m_instance 變量的初始化與類的加載順序沒有保證,可能會出問題。這就是為什么GoF 在提出單例類的概念時,舉的例子是懶漢式的。他們的書影響之大,以致Java 語言中單例類的例子也大多是懶漢式的
工廠模式
首先需要說一下工廠模式。工廠模式根據抽象程度的不同分為三種:簡單工廠模式(也叫靜態工廠模式)、這里所講述的工廠方法模式、以及抽象工廠模式。工廠模式是編程中經常用到的一種模式。它的主要優點有:
- 可以使代碼結構清晰,有效地封裝變化。在編程中,產品類的實例化有時候是比較復雜和多變的,通過工廠模式,將產品的實例化封裝起來,使得調用者根本無需關心產品的實例化過程,只需依賴工廠即可得到自己想要的產品。
- 對調用者屏蔽具體的產品類。如果使用工廠模式,調用者只關心產品的接口就可以了,至於具體的實現,調用者根本無需關心。即使變更了具體的實現,對調用者來說沒有任何影響。
- 降低耦合度。產品類的實例化通常來說是很復雜的,它需要依賴很多的類,而這些類對於調用者來說根本無需知道,如果使用了工廠方法,我們需要做的僅僅是實例化好產品類,然后交給調用者使用。對調用者來說,產品所依賴的類都是透明的。
C++實現:
#include <iostream> using namespace std; class IProduct { public: virtual void productMethod() = 0; }; class Product:public IProduct { public: void productMethod() { cout<<"C++ productMethod call"<<endl; } };
class IFactory { public: virtual IProduct* CreateProduct() = 0; };
class ConcreateFactory:public IFactory { public: IProduct* CreateProduct() { IProduct* p = new Product(); return p; } }; int main() { IFactory* pFactory = new ConcreateFactory(); IProduct* p = pFactory->CreateProduct(); p->productMethod(); delete p; return 1; }
Java實現
interface IProduct { public void productMethod(); }
class Product implements IProduct { public void productMethod() { System.out.println("Java productMethod call"); } }
interface IFactory { public IProduct createProduct(); }
class Factory implements IFactory { public IProduct createProduct() { return new Product(); } }
public class Client { public static void main(String[] args) { IFactory factory = new Factory(); IProduct prodect = factory.createProduct(); prodect.productMethod(); } }