什么是單例模式
顧名思義火窒,就是只有一個(gè)實(shí)例硼补。作為對(duì)象的創(chuàng)建模式,單例模式確保某一個(gè)類只有一個(gè)實(shí)例熏矿,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例已骇。
為什么使用單例模式
- PHP的應(yīng)用主要在于數(shù)據(jù)庫(kù)應(yīng)用离钝,一個(gè)應(yīng)用中會(huì)存在大量的數(shù)據(jù)庫(kù)操作,在使用面向?qū)ο蟮姆绞介_(kāi)發(fā)時(shí)褪储,如果使用單例模式卵渴,
則可以避免大量的 new 操作消耗的資源,還可以減少數(shù)據(jù)庫(kù)連接這樣就不容易出現(xiàn) too many connections 情況鲤竹。 - 如果系統(tǒng)中需要有一個(gè)類來(lái)全局控制某些配置信息浪读,那么使用單例模式可以很方便的實(shí)現(xiàn)。
- 在一次頁(yè)面請(qǐng)求中辛藻,便于進(jìn)行調(diào)試碘橘,因?yàn)樗械拇a(例如數(shù)據(jù)庫(kù)操作類db)都集中在一個(gè)類中,我們可以在類中設(shè)置鉤子吱肌,輸出日志痘拆,從而避免到處 var_dump,echo氮墨。
單例模式結(jié)構(gòu)圖
單例模式的實(shí)現(xiàn)
- 私有化一個(gè)屬性用于存放唯一的一個(gè)實(shí)例
- 私有化構(gòu)造方法纺蛆,私有化克隆方法,用來(lái)創(chuàng)建并只允許創(chuàng)建一個(gè)實(shí)例
- 公有化靜態(tài)方法规揪,用于向系統(tǒng)提供這個(gè)實(shí)例
代碼實(shí)現(xiàn)
class Singleton
{
// 存放實(shí)例
private static $_instance = null;
// 私有化構(gòu)造方法
private function __construct()
{
echo '單例模式的實(shí)例被構(gòu)造了';
}
// 私有化克隆方法
private function __clone() {}
// 公有化獲取實(shí)例方法
public static function getInstance()
{
if (!(self::$_instance instanceof Singleton)) {
self::$_instance = new Singleton();
}
return self::$_instance;
}
}
$singleton = Singleton::getInstance();
優(yōu)點(diǎn):
- 因?yàn)殪o態(tài)方法可以在全局范圍內(nèi)被訪問(wèn)桥氏,當(dāng)我們需要一個(gè)單例模式的對(duì)象時(shí),只需調(diào)用getInstance方法猛铅,獲取先前實(shí)例化的對(duì)象识颊,無(wú)需重新實(shí)例化。
- 由于單例模式在內(nèi)存中只有一個(gè)實(shí)例奕坟,減少內(nèi)存開(kāi)支祥款,特別是一個(gè)對(duì)象需要頻繁地創(chuàng)建銷(xiāo)毀時(shí),而且創(chuàng)建或銷(xiāo)毀時(shí)性能又無(wú)法優(yōu)化月杉,單例模式就非常明顯了
- 由于單例模式只生成一個(gè)實(shí)例刃跛,所以,減少系統(tǒng)的性能開(kāi)銷(xiāo)苛萎,當(dāng)一個(gè)對(duì)象產(chǎn)生需要比較多的資源時(shí)桨昙,如讀取配置,產(chǎn)生其他依賴對(duì)象時(shí)腌歉,則可以通過(guò)在應(yīng)用啟動(dòng)時(shí)直接產(chǎn)生一個(gè)單例對(duì)象蛙酪,然后永久駐留內(nèi)存的方式來(lái)解決。
- 單例模式可以避免對(duì)資源的多重占用翘盖,例如一個(gè)寫(xiě)文件操作桂塞,由于只有一個(gè)實(shí)例存在內(nèi)存中,避免對(duì)同一個(gè)資源文件的同時(shí)寫(xiě)操作
- 單例模式可以在系統(tǒng)設(shè)置全局的訪問(wèn)點(diǎn)馍驯,優(yōu)化和共享資源訪問(wèn)阁危,例如玛痊,可以設(shè)計(jì)一個(gè)單例類,負(fù)責(zé)所有數(shù)據(jù)表的映射處理狂打。
缺點(diǎn):
- 單例模式一般沒(méi)有接口擂煞,擴(kuò)展很困難,若要擴(kuò)展趴乡,除了修改代碼基本上沒(méi)有第二種途徑可以實(shí)現(xiàn)对省。
- 單例對(duì)象如果持有Context,那么很容易引發(fā)內(nèi)存泄漏晾捏,此時(shí)需要注意傳遞給單例對(duì)象的Context最好是Application Context官辽。
使用 Trait 關(guān)鍵字實(shí)現(xiàn)類似于繼承單例類的功能
Trait Singleton
{
//存放實(shí)例
private static $_instance = null;
//私有化克隆方法
private function __clone() {}
//公有化獲取實(shí)例方法
public static function getInstance()
{
$class = __CLASS__;
if (!(self::$_instance instanceof $class)) {
self::$_instance = new $class();
}
return self::$_instance;
}
}
class DB
{
private function __construct()
{
echo __CLASS__ . PHP_EOL;
}
}
class DBHandle extends DB
{
use Singleton;
private function __construct()
{
echo '單例模式的實(shí)例被構(gòu)造了';
}
}
$handle = DBHandle::getInstance();
// 注意若父類方法為public,則子類只能為pubic粟瞬,若父類為private同仆,子類為public,protected裙品,private都可以俗批。
單例模式特點(diǎn)(三私一公)
- 私有的構(gòu)造方法(防止類外實(shí)例化)
- 私有的克隆方法(防止通過(guò)克隆生成對(duì)象)
- 私有的靜態(tài)屬性(保存類的實(shí)例)
- 公有的靜態(tài)方法(調(diào)取這個(gè)類相當(dāng)一個(gè)接口)
補(bǔ)充,大多數(shù)書(shū)籍介紹單例模式市怎,都會(huì)講三私一公岁忘,公有化靜態(tài)方法作為提供對(duì)象的接口,私有屬性用于存放唯一一個(gè)單例對(duì)象区匠。私有化構(gòu)造方法干像,私有化克隆方法保證只存在一個(gè)單例。
但實(shí)際上驰弄,雖然我們無(wú)法通過(guò) new 關(guān)鍵字和 clone 出一個(gè)新的對(duì)象麻汰,但我們?nèi)粝氲玫揭粋€(gè)新對(duì)象。還是有辦法的戚篙,那就是通過(guò)序列化和反序列化得到一個(gè)對(duì)象五鲫。私有化 sleep() 和 wakeup() 方法依然無(wú)法阻止通過(guò)這種方法得到一個(gè)新對(duì)象〔砝蓿或許真得要阻止位喂,你只能去 __wakeup 添加刪除一個(gè)實(shí)例的代碼,保證反序列化增加一個(gè)對(duì)象乱灵,你就刪除一個(gè)塑崖。不過(guò)這樣貌似有點(diǎn)怪異。