首頁  >  文章  >  後端開發  >  一起聊聊PHP中的單例模式

一起聊聊PHP中的單例模式

青灯夜游
青灯夜游轉載
2021-07-26 19:05:244008瀏覽

在之前的文章《深入淺析PHP中的模板方法模式》中我們介紹了PHP中的模板方法模式,下面本篇文章帶大家了解一下PHP設計模式中的單例模式。

一起聊聊PHP中的單例模式

單例模式絕對是在常用以及面試常問設計模式中排名首位的。一方面它夠簡單,三言兩語就能說明白。另一方面,它又夠複雜,它的實作不只一種形式,在Java等非同步語言中還要考慮多執行緒加鎖的問題。所以在面試時,千萬不要以為面試官出單例模式的問題就放鬆了,這個模式真的是可深可淺,也極其能體現一個開發者的水平。因為只要工作過一段時間,不可避免的就會接觸到這個模式。

Gof類別圖及解釋

GoF定義:保證一個類別只有一個實例,並提供一個存取它的全域存取點。

GoF類別圖

一起聊聊PHP中的單例模式

#程式碼實作

class Singleton
{
    private static $uniqueInstance;
    private $singletonData = '单例类内部数据';

    private function __construct()
    {
        // 构造方法私有化,外部不能直接实例化这个类
    }

    public static function GetInstance()
    {
        if (self::$uniqueInstance == null) {
            self::$uniqueInstance = new Singleton();
        }
        return self::$uniqueInstance;
    }

    public function SingletonOperation(){
        $this->singletonData = '修改单例类内部数据';
    }

    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;

客戶端的調用,我們會發現一起聊聊PHP中的單例模式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請求都是自己封裝的,網路上的教學大部分也都是採取的單例模式。

快取類別圖

一起聊聊PHP中的單例模式

#完整原始碼: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 &#39;发送Post请求&#39;, PHP_EOL;
    }

    public function Get(){
        echo &#39;发送Get请求&#39;, PHP_EOL;
    }
}

$httpA = new HttpService();
$httpA->Post();
$httpA->Get();

$httpB = new HttpService();
$httpB->Post();
$httpB->Get();

var_dump($httpA == $httpB);

說明

#
  • 是不是還是很簡單,這裡就不多說這種形式的單例了,我們說說另外幾種形式的單例
  • 在Java等靜態語言中,靜態變數可以直接new對象,在宣告一起聊聊PHP中的單例模式

    instance = new HttpService();。這樣可以省略掉GetInstance()方法,但是這個靜態變數不管用不用都會直接實例化出來佔用記憶體。這種單例就叫做餓漢式單例模式。

  • 我們的程式碼和範例很明顯不是餓漢式的,這種形式叫做懶漢式。你要主動的來用GetInstance()獲取,我才會建立物件。
  • 懶漢式在多執行緒的應用程式中,如java多執行緒或PHP中使用swoole之後,會出現重複建立的問題,而且這多次建立的都不是同一個物件了。這時一般會使用雙重偵測來確保全域還是只有唯一的一個物件。具體程式碼大家可以去自己找。餓漢式不會有問題,餓漢式本身就已經給靜態屬性賦值了,不會再改變。
  • 還有一種方式是靜態內部類別的建立方式。這種平常就不多見了,它的資源利用率高。將靜態變數放在方法內,使靜態變數成為方法內的變數而不是類別中的變數。它可以讓單例物件呼叫自身的靜態方法和屬性。

原文網址:https://juejin.cn/post/6844903990585458702

作者:硬核心專案經理

推薦學習: 《PHP影片教學

以上是一起聊聊PHP中的單例模式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除