在這次鐵人賽的 Day 4Day 5 筆者分別介紹了單例模式以及註冊者模式。把 Day 4 的「單例特性」範例程式碼改寫成如下:

trait Singleton
{
   /**
    * Constructor
    */
    private function __construct()
    {

    }

    /**
     * 取得實例
     *
     * @return self
     */
    public static function getInstance()
    {
        $name = strtolower(get_called_class());
        $name = ltrim(substr($name, strrpos($name, '\\')), '\\');

        if (!Registry::has($name)) {
            $instance = new self();
            Registry::set($instance, $name);
        }

        return Registry::get($name);
    }
}
  1. 因為使用註冊表來存放單例,instance 靜態屬性不再需要,所以移除了。
  2. getInstance 方法改寫成使用註冊表來返回單例。

說明

$name = strtolower(get_called_class());

取得類別名稱 (含命名空間的名稱)。

get_called_class() 是用來取得「延遲靜態綁定」 (late static binding) 的類別名稱,專門用在靜態方法裡取得正確的類別名稱,如果是放在非靜態的方法中,作用和 get_class() 無異。

$name = ltrim(substr($name, strrpos($name, '\\')), '\\');

去除命名空間的名稱,取得類別本身的名稱。
例:APP\CORE\MAN 過濾後取得字串 man

if (!Registry::has($name)) {
    $instance = new self();
    Registry::set($instance, $name);
}

註冊表如果不存在 Man,則實例化 Man 之後存入註冊表。

return Registry::get($name);

從註冊表取出 Man 的實例。

如果是用這樣子的方式,則取用 Man 這個類別的單例則變成既可以由本身的 getInstance 靜態方法取得,也可以從註冊表取得。

$a = Man::getInstance();
$b = Registry::get('man');

$a$b 為相同的實例。

結論

這個範例是藉由註冊表模式本身具存放物件的唯一實例的這種性質,搭配單例模式,剛好可以混合使用。不過你會發現,getInstance 內容變複雜了。程式碼沒變精簡反而變更多,筆者舉這個範例的理由很簡單:

「設計模式常常是混合使用,是活的而不是死的。是彈性的,而非死守的信條。」

下一篇文章,筆者要為大家介紹的是最常見的「工廠模式」(factory pattern)。

最後修改日期: 2022-02-01

作者

留言

撰寫回覆或留言

發佈留言必須填寫的電子郵件地址不會公開。