上一篇文章我們介紹了設計模式的七種面向對象設計原則,本篇文章我們將介紹設計模式中創建型模式的單例與多例模式。在瞭解單例模式之前我們先來了解一下設計模式的分類有哪些呢?
設計模式的分類有哪些?
設計模式是在軟件開發中,經過驗證的,用於解決在特定環境下、重複出現的或者特定問題的解決方案。而這些都是前輩們經過大量的實踐總結出來的寶貴經驗,學習和領會其中的設計思想,能讓我們在面對相同問題時可以直接使用現有的解決方案,從而避免重複創造輪子。典型的設計模式根據目的可以分爲以下三大類:
-
創建型模式:用於描述“怎樣創建對象”,它的主要特點是“將對象的創建與使用分離”。GoF 中提供了單例、原型、工廠方法、抽象工廠、建造者等 5 種創建型模式。
-
結構型模式:它分爲類結構型模式和對象結構型模式,前者採用繼承機制來組織接口和類,後者釆用組合或聚合來組合對象。由於組合關係或聚合關係比繼承關係耦合度低,滿足“合成複用原則”,所以對象結構型模式比類結構型模式具有更大的靈活性。GoF 中提供了代理、適配器、橋接、裝飾、外觀、享元、組合等 7 種結構型模式。
-
行爲型模式:描述多個類或對象之間怎樣相互協作共同完成單個對象都無法單獨完成的任務,它涉及算法與對象間職責的分配。
行爲型模式分爲類行爲模式和對象行爲模式,前者採用繼承機制來在類間分派行爲,後者採用組合或聚合在對象間分配行爲。由於組合關係或聚合關係比繼承關係耦合度低,滿足“合成複用原則”,所以對象行爲模式比類行爲模式具有更大的靈活性。用於描述類或對象之間怎樣相互協作共同完成單個對象都無法單獨完成的任務,以及怎樣分配職責。GoF 中提供了模板方法、策略、命令、職責鏈、狀態、觀察者、中介者、迭代器、訪問者、備忘錄、解釋器等 11 種行爲型模式。
單例模式
單例模式(Singleton Pattern)是一個比較簡單的模式,其定義如下:Ensure a class has only one instance, and provide a global point of access to it.(確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例。)
在應用這個模式時,單例對象的類必須保證只有一個實例存在。許多時候整個系統只需要擁有一個的全局對象,這樣有利於我們協調系統整體的行爲。比如在某個服務器程序中,該服務器的配置信息存放在一個文件中,這些配置數據由一個單例對象統一讀取,然後服務進程中的其他對象再通過這個單例對象獲取這些配置信息。這種方式簡化了在複雜環境下的配置管理。
單例模式的優點:
-
在單例模式中,活動的單例只有一個實例,對單例類的所有實例化得到的都是相同的一個實例。這樣就 防止其它對象對自己的實例化,確保所有的對象都訪問一個實例
-
單例模式具有一定的伸縮性,類自己來控制實例化進程,類就在改變實例化進程上有相應的伸縮性。
-
提供了對唯一實例的受控訪問。
-
由於在系統內存中只存在一個對象,因此可以 節約系統資源,當 需要頻繁創建和銷燬的對象時單例模式無疑可以提高系統的性能。
-
允許可變數目的實例。
-
避免對共享資源的多重佔用。
單例模式的缺點:
-
不適用於變化的對象,如果同一類型的對象總是要在不同的用例場景發生變化,單例就會引起數據的錯誤,不能保存彼此的狀態。
-
由於單例模式中沒有抽象層,因此單例類的擴展有很大的困難。
-
單例類的職責過重,在一定程度上違背了“單一職責原則”。
-
濫用單例將帶來一些負面問題,如爲了節省資源將數據庫連接池對象設計爲的單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;如果實例化的對象長時間不被利用,系統會認爲是垃圾而被回收,這將導致對象狀態的丟失。
使用單例模式的注意事項:
-
使用時不能用反射模式創建單例,否則會實例化一個新的對象
-
使用懶單例模式時注意線程安全問題
-
.餓單例模式和懶單例模式構造方法都是私有的,因而是不能被繼承的,有些單例模式可以被繼承(如登記式模式)
單例模式的適用場景:
單例模式只允許創建一個對象,因此節省內存,加快對象訪問速度,因此對象需要被公用的場合適合使用,如多個模塊使用同一個數據源連接對象等等。如:
-
需要頻繁實例化然後銷燬的對象。
-
創建對象時耗時過多或者耗資源過多,但又經常用到的對象。
-
有狀態的工具類對象。
-
頻繁訪問數據庫或文件的對象。
單例模式的使用場景:
-
網站的計數器,一般也是採用單例模式實現,否則難以同步。
-
應用程序的日誌應用,一般都可以用單例模式實現,這一般是由於共享的日誌文件一直處於打開狀態,因爲只能有一個實例去操作,否則內容不好追加。
-
Web應用的配置對象的讀取,一般也應用單例模式,這個是由於配置文件是共享的資源。
-
數據庫連接池的設計一般也是採用單例模式,因爲數據庫連接是一種數據庫資源。數據庫軟件系統中使用數據庫連接池,主要是節省打開或者關閉數據庫連接所引起的效率損耗,這種效率上的損耗還是非常昂貴的,因爲可以用單例模式來維護,就可以大大降低這種損耗。
-
多線程的線程池的設計一般也是採用單例模式,這是由於線程池要方便對池中的線程進行控制。
注意:單例模式的意義在於生成一個對象,重複對其使用,從而節省了new操作的資源,但是對於PHP來說,頁面跳轉之後,會釋放掉內存中的對象,這樣似乎就沒什麼意義了??只能說在單個頁面中,重複使用的一個對象資源時,這個單例還是很有用的。
PHP的單例模式
class Singleton
{
/**
* @var Singleton
*/
private static $instance;
public static function getInstance() {
if(!self::$instance instanceof self){
self::$instance = new self();
return self::$instance;
}else{
return self::$instance;
}
}
/**
* 不允許從外部調用以防止創建多個實例
* 要使用單例,必須通過 Singleton::getInstance() 方法獲取實例
*/
private function __construct() {
}
/**
* 防止實例被克隆(這會創建實例的副本)
*/
private function __clone() {
}
/**
* 防止反序列化(這將創建它的副本)
*/
private function __wakeup() {
}
}
PHP的多例模式
多例模式 是指存在一個類有多個相同實例,而且該實例都是該類本身。這個類叫做多例類。(實際上是單例模式的推廣)其特點是:
-
多例類可以有多個實例。
-
多例類必須自己創建、管理自己的實例,並向外界提供自己的實例。
多例模式的應用
-
2 個數據庫連接器,比如一個是 MySQL ,另一個是 SQLite
-
多個記錄器(一個用於記錄調試消息,一個用於記錄錯誤)
final class Multiton { const INSTANCE_1 = '1'; const INSTANCE_2 = '2'; /** * @var 實例數組 */ private static $instances = []; /** * 這裏私有方法阻止用戶隨意的創建該對象實例 */ private function __construct() { } public static function getInstance(string $instanceName): Multiton { if (!isset(self::$instances[$instanceName])) { self::$instances[$instanceName] = new self(); } return self::$instances[$instanceName]; } /** * 該私有對象阻止實例被克隆 */ private function __clone() { } /** * 該私有方法阻止實例被序列化 */ private function __wakeup() { } }
以上我們介紹了創建型模式中的單例與多例模式,下一篇將會介紹創建型模式中的原型模式。
感謝閱讀!
喜歡本文的朋友,歡迎關注“isevena”