在這次鐵人賽的 Day 4 及 Day 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);
}
}
- 因為使用註冊表來存放單例,
instance
靜態屬性不再需要,所以移除了。 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)。
留言