PHP設計模式之單例模式

PHP設計模式之單例模式

單例模式絕對是在經常使用以及面試常問設計模式中排名首位的。一方面它夠簡單,三言兩語就能說明白。另外一方面,它又夠複雜,它的實現不只僅只有一種形式,並且在Java等異步語言中還要考慮多線程加鎖的問題。因此在面試時,千萬不要覺得面試官出單例模式的問題就放鬆了,這個模式真的是可深可淺,也極其能體現一個開發者的水平。由於只要工做過一段時間,不可避免的就會接觸到這個模式。php

Gof類圖及解釋

GoF定義:保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。java

GoF類圖nginx

單例模式

代碼實現git

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()方法得到全局惟一的一個對象。github

$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系統服務器中的那惟一一份真實的花名冊數據庫

完整代碼:github.com/zhangyue050…設計模式

實例

既然上面說過數據庫操做類和網絡請求類都很喜歡用單例模式,那麼咱們就來實現一個Http請求類的單例模式的開發。記得在很早前作Android的時候,尚未如今這麼多的框架,Http請求都是本身封裝的,網上的教程中大部分也都是採起的單例模式。緩存

緩存類圖服務器

緩存模板方法模式版

完整源碼:github.com/zhangyue050…

<?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 staticinstance = new HttpService();。這樣能夠省略掉GetInstance()方法,可是這個靜態變量無論用不用都會直接實例化出來佔用內存。這種單例就叫作餓漢式單例模式。
  • 咱們的代碼和例子很明顯不是餓漢式的,這種形式叫作懶漢式。你要主動的來用GetInstance()獲取,我纔會建立對象。
  • 懶漢式在多線程的應用中,如java多線程或者PHP中使用swoole以後,會出現重複建立的問題,並且這屢次建立的都不是同一個對象了。這時通常會使用雙重檢測來來確保全局仍是隻有惟一的一個對象。具體代碼你們能夠去本身找一下。餓漢式不會有問題,餓漢式自己就已經給靜態屬性賦值了,不會再改變。具體能夠參考靜態類相關文章(公衆號內查詢《PHP中的static》或掘金juejin.im/post/5cb5b2…)。
  • 還有一種方式是靜態內部類的建立方式。這種日常就很少見了,它的資源利用率高。將靜態變量放在方法內,使靜態變量成爲方法內的變量而不是類中的變量。它可讓單例對象調用自身的靜態方法和屬性。

下期看點

是否是忽然發現單例真的沒有想象中的那麼簡單啊,還有這麼多我不知道的東西。一我的從知道本身知道到知道本身不知道就是上升了一個臺階,再下去就是不知道本身知道了,到了這個階段的碼農那可都是高手中的高手了。單例模式就是這樣一個經典經常使用的超級模式。爲何叫超級模式呢?由於它和工廠兩大模式真的能夠說是面試必備題,不學可不行哦!下一個登場的是狀態模式,從名字就能夠看出,和類的狀態有關,但具體是幹嗎的呢?咱們下回見。

相關文章
相關標籤/搜索