單例模式絕對是在常用以及面試常問設計模式中排名首位的,一方面它夠簡單,三言兩語就能說明白,另一方面,它又夠復雜,它的實作不僅僅只有一種形式,而且在Java等異步語言中還要考慮多執行緒加鎖的問題,所以在面試時,千萬不要以為面試官出單例模式的問題就放松了,這個模式真的是可深可淺,也極其能體現一個開發者的水平,因為只要作業過一段時間,不可避免的就會接觸到這個模式,
Gof類圖及解釋
GoF定義:保證一個類僅有一個實體,并提供一個訪問它的全域訪問點,
GoF類圖

代碼實作
class Singleton
{
private static $uniqueInstance;
private $singletonData = 'https://www.cnblogs.com/zyblog-coder/p/單例類內部資料';
private function __construct()
{
// 構造方法私有化,外部不能直接實體化這個類
}
public static function GetInstance()
{
if (self::$uniqueInstance == null) {
self::$uniqueInstance = new Singleton();
}
return self::$uniqueInstance;
}
public function SingletonOperation(){
$this->singletonData = 'https://www.cnblogs.com/zyblog-coder/p/修改單例類內部資料';
}
public function GetSigletonData()
{
return $this->singletonData;
}
}
沒錯,核心就是這樣一個單例類,沒別的了,讓靜態變數保存實體化后的自己,當需要這個物件的時候,呼叫GetInstance()方法獲得全域唯一的一個物件,
$singletonA = Singleton::GetInstance();
echo $singletonA->GetSigletonData(), PHP_EOL;
$singletonB = Singleton::GetInstance();
if ($singletonA === $singletonB) {
echo '相同的物件', PHP_EOL;
}
$singletonA->SingletonOperation(); // 這里修改的是A
echo $singletonB->GetSigletonData(), PHP_EOL;
客戶端的呼叫,我們會發現$singletonA和$singletonB是完全一樣的物件,
- 沒錯,從代碼中就可以看出,單例最大的用途就是讓我們的物件全域唯一,
- 那么全域唯一有什么好處呢?有些類的創建開銷很大,而且并不需要我們每次都使用新的物件,完全可以一個物件進行復用,它們并沒有需要變化的屬性或狀態,只是提供一些公共服務,比如資料庫操作類、網路請求類、日志操作類、配置管理服務等等
- 曾經有過面試官問過,單例在PHP中到底是不是唯一的?如果在一個行程下,也就是一個fpm下,當然是唯一的,nginx同步拉起的多個fpm中那肯定就不是唯一的啦,一個行程一個嘛!
- 單例模式的優點:對唯一實體的受控訪問;縮小命名空間;允許對操作和表示的精化;允許可變數目的實體;比類操作更靈活,
- Laravel中在IoC容器部分使用了單例模式,關于容器部分的內容我們會在將來的Laravel系列文章中講解,我們可以在Illuminate\Container\Container類中找到singleton方法,它呼叫了bind方法中的getClosure方法,繼續追蹤會發現他們最侄訓呼叫Container的make或build方法來進行實體化類,不管是make還是build方法,他們都會有單例的判斷,也就是判斷類是否被實體化過或者在容器中已存在,build中的if (!$reflector->isInstantiable()),
公司越來越大,但我們的全部公司的花名冊都只有一份(單例類),保存在我們的OA系統中,怕的就是各個部門擁有各自己的花名冊后會產生混亂,比如更新不及時漏掉了其他部門新入職或者離職的員工,其他部門在需要的時候,可以去查看全部的花名冊,也可以在全部花名冊的基礎上建立修改自己部門的部分,但是在OA系統中,其實他們修改的還是那一份總的花名冊中的內容,大家維護的其實都是保存在OA系統服務器中的那唯一一份真實的花名冊
完整代碼:https://github.com/zhangyue0503/designpatterns-php/blob/master/21.singleton/source/singleton.php
實體
既然上面說過資料庫操作類和網路請求類都很喜歡用單例模式,那么我們就來實作一個Http請求類的單例模式的開發,記得在很早前做Android的時候,還沒有現在這么多的框架,Http請求都是自己封裝的,網上的教程中大部分也都是采取的單例模式,
快取類圖

完整原始碼:https://github.com/zhangyue0503/designpatterns-php/blob/master/21.singleton/source/singleton-http.php
<?php
class HttpService{
private static $instance;
public function GetInstance(){
if(self::$instance == NULL){
self::$instance = new HttpService();
}
return self::$instance;
}
public function Post(){
echo '發送Post請求', PHP_EOL;
}
public function Get(){
echo '發送Get請求', PHP_EOL;
}
}
$httpA = new HttpService();
$httpA->Post();
$httpA->Get();
$httpB = new HttpService();
$httpB->Post();
$httpB->Get();
var_dump($httpA == $httpB);
說明
- 是不是依然很簡單,這里就不多說這種形式的單例了,我們說說另外幾種形式的單例
- 在Java等靜態語言中,靜態變數可以直接new物件,在宣告$instance的時候直接給他賦值,比如 private static $instance = new HttpService();,這樣可以省略掉GetInstance()方法,但是這個靜態變數不管用不用都會直接實體化出來占用記憶體,這種單例就叫做餓漢式單例模式,
- 我們的代碼和例子很明顯不是餓漢式的,這種形式叫做懶漢式,你要主動的來用GetInstance()獲取,我才會創建物件,
- 懶漢式在多執行緒的應用中,如java多執行緒或者PHP中使用swoole之后,會出現重復創建的問題,而且這多次創建的都不是同一個物件了,這時一般會使用雙重檢測來來確保全域還是只有唯一的一個物件,具體代碼大家可以去自己找一下,餓漢式不會有問題,餓漢式本身就已經給靜態屬性賦值了,不會再改變,具體可以參考靜態類相關文章(公眾號內查詢《PHP中的static》或掘金https://juejin.im/post/5cb5b2926fb9a068664c1ac5),
- 還有一種方式是靜態內部類的創建方式,這種平常就不多見了,它的資源利用率高,將靜態變數放在方法內,使靜態變數成為方法內的變數而不是類中的變數,它可以讓單例物件呼叫自身的靜態方法和屬性,
下期看點
是不是突然發現單例真的沒有想象中的那么簡單啊,還有這么多我不知道的東西,一個人從知道自己知道到知道自己不知道就是上升了一個臺階,再下去就是不知道自己知道了,到了這個階段的碼農那可都是高手中的高手了,單例模式就是這樣一個經典常用的超級模式,為什么叫超級模式呢?因為它和工廠兩大模式真的可以說是面試必備題,不學可不行哦!下一個登場的是狀態模式,從名字就可以看出,和類的狀態有關,但具體是干嘛的呢?我們下回見,
關注公眾號:【硬核專案經理】獲取最新文章
添加微信/QQ好友:【xiaoyuezigonggong/149844827】免費得PHP、專案管理學習資料
知乎、公眾號、抖音、頭條搜索【硬核專案經理】
B站ID:482780532
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/245521.html
標籤:PHP
上一篇:組合與繼承
