php設計模式總結-單件模式

wangtaotao發表於2014-03-11

一、單件模式

英文叫做sington。其他語言中有叫做單例模式,其實都是一樣的道理。保證只會出現單個例項,所以是單例。翻譯成單件,永遠只會產生一件,呵呵。

還有翻譯成單元素模式。其實關鍵是看這個英文比較好。英文是sington,統一是使用這個單詞。

單件模式的目的我理解如下:

避免重複建立(例項化)物件,已經有現成的例項就用現成的。

減少資源的浪費(因為建立多個例項,浪費記憶體,完全沒必要),單件模式保證了每時每刻引用的都是同一個例項。

為什麼同時建立多個例項會引起邏輯上的錯誤呢?

$obj1
$obj2
多個例項。可能會覆蓋掉裡面的靜態static變數嗎? 不是這樣子的。

其實是因為我目前還沒遇到更加嚴重的問題。目前是簡單的應用。

二、我覺得單件模式實踐的注意點在下面幾個方面

1、不要使用全域性變數來儲存例項值。因為全域性變數在任何地方都可以被訪問和修改,這就意味著可能會被其他程式碼給破壞掉值,這樣就達不到永遠是同一

個例項的效果。

2、使用static靜態變數。這樣只能函式內部訪問。解決了全域性變數被破壞的風險。

我覺得這是很多要做到例項唯一的一個關鍵部分。像框架中為保證所有對類例項的引用是唯一一個,都是將例項儲存在static變數中。這樣子下回撥用的

時候也是同一個例項。不會重複建立。

抓住了這個精髓,我覺得是可以變化的。並不一定要遵循設計模式書中的做法。因為目標是相同的。技巧可以不同。

3、一般將類的__construct()建構函式標識為private,這樣就是避免程式設計師直接例項化這個類。根據每種語言的特點,加上private關鍵詞,程式設計師new一

個物件,就會報錯。這種技巧是一種輔助手段。為保證只有一個類例項做輔助方案的。核心還是在於第二點的static關鍵字。

只要程式設計師約定好,這個輔助手段其實可以沒有仍然能夠做到單件。不是為設計模式而設計。瞭解實現目標才是關鍵的。

我在想,可以使用protected來替代嗎?

目標就是,要禁止使用new來例項化這個函式。當例項化一個類的時候,預設會去執行建構函式,而加上protected和private關鍵字的成員,
都同樣不能在類外部呼叫的。所以使用protected也是可行的。

但為什麼要使用private呢?還有個好處,可以避免被繼承的子類所重寫,覆蓋掉方法的內容。因為加上protected標識的成員是能夠被子類給重寫的。
既然對建構函式加上了private,那就意味著子類是不能繼承這個類的。瞭解這個特性設計的時候就要考慮,無子類繼承它的概念。

4、程式碼實踐

class test
{

static $_instance = false;

private function __construct()
{

      /*一般將建構函式加上private關鍵字,這樣子避免直接使用外部直接new來例項物件,當然內部使用new來建立是不會影響的*/
}

function get_instance()
{

          if(self::$_instance==false && !is_object(self::$_instance)){
       
         self::$_instance = new test();
      }
      return self::$_instance;

}

}

實際專案開發中,有個變體是,建立a、b、c的例項都需要通過一個公共的方法來呼叫,這樣子可以實現單件模式。

類似於thinkphp等框架中的。

像下面是phpcms中的

pc_base::load_app_calss(`test`);

load_app_class($class_name)
{

static $class_array = array();

if(isset($class_array[$class_name]) && is_object($class_array[$class_name])) )
return $class_array[$class_name];
}else{
//這裡可能還要有程式碼載入這個類檔案,根據實際而定。可以是去預設一個資料夾夾中載入。也可以認為呼叫這個方法的前提是類檔案要載入進來
$class_array[$class_name] = new $class_array[$class_name];
return $class_array[$class_name];

}

其實可以避免建立很多資料庫連結。寫到這裡,我想起了mysql對於同一組引數進行的mysql_connect()連線,是不會重新建立連線的。php手冊中對這個函

數的解釋如下:

如果用同樣的引數第二次呼叫 mysql_connect(),將不會建立新連線,而將返回已經開啟的連線標識。

 

其實呢,只是mysql_connect這個函式做了可複用了。不討論資料庫連線方面。例項化其他的類,也需要建立大量的例項。佔有資源。是指同一次執行的代

碼過程中才能起到節省資源的效果

 

比如a.php的程式碼過程如下:

$class = test::get_instance();//得到這個test這個類的例項

$class->get_name();

get_count_number();//假設這個函式裡面又需要用到那個類,則又需要進行例項化,如果統一呼叫get_instance()來獲取例項,則前面得到的例項是可以

複用的。

 

三、單態模式(monostate)

1、單件模式還有一種變體:就是類的單件模式,也就是monostate模式。MonoState的意思就是”單一的狀態”。也就是常說的單態。實現的目標為:所有實

例都是共享類中同一個值。

monostate的設計目標為:實現多個例項可以共享變數(類裡面的屬性),成為單態,儘管存在多個例項,但例項中的變數的最終只會有一個狀態(可以理解為

一個值),不會出現多個值(也就是每個例項裡面的變數都是不同的值)。

2、它與單件的區別為:

單件是將建構函式宣告為private,來保證只有一個例項。而單態則不需要。它關注的側重點是最終只有一個數值,而使用者例項化多少類,不是它所關心的


MonoState並不限制建立物件的個數,但是它的狀態卻只有一個狀態。

3、monostate模式實踐

實踐要點:把類裡面的變數(屬性)標識為static即可

<?php

class test
{

static $_state = array();

function set($key,$value)
{
  self::$_state[$key]= $value;
}

function get($key)
{

   return self::$_state[$key];

}

}

$obj = new test();

$obj->set(`name`,`wangtao`);

$obj->set(`sex`,`male`);

echo $obj->get(`name`);//得到結果是wangtao

//再次例項化一次,看訪問物件的成員,是否得到一樣的資料。
$obj2 = new test();

echo $obj2->get(`name`);//輸出wangtao

 

//再次新建立一個例項$obj2,訪問name這個變數,資料是共享的,所以輸出還是wangtao。當然使用set()把值改變了,其他例項也會訪問到改變後的值。

總結:實現monostate模式,具體實現有多種辦法,只要達到共享資料的目的就ok。比如使用$_GLOABS[]全域性變數,把資料儲存在全域性變數中,然後放到類

成員中也可以,《php設計模式》這本書就是使用這種形式實現。使用靜態變數(static關鍵字)也可以。上面使用的就是靜態變數的方式。我覺得使用

static方式更加直觀易懂

四、思考:sington與monostate能混合一起實現嗎?

既然sington模式可以避免建立多個例項。而monostate是關注多個例項之間共享資料。

那麼有沒有種辦法讓兩者混合呢。

也就是說:我構造一個類,既能夠達到單件的效果,也能實現monostate的效果。開玩笑玩玩,呵呵,加深深入理解。

我覺得,單件關注的是例項化一個類。monostate關注的狀態的一致性。其實兩者是不相容的。

如果實現了單件模式。那麼就不存在多個例項物件存在。既然都是呼叫同一個例項,這樣子裡面的成員變數肯定是共享的,因為使用的是同一個例項的成員

。為此我特意做試驗,如下:

class test
{

static $_state;//實現單態,就是將裡面變數定義為static即可,現在這個類實現了monostate模式
static $_instance = false;

private function __construct()
{

}

/*
實現單例模式
*/
function get_instance()
{

       if(self::$_instance==false && !is_object(self::$_instance)){
       
         self::$_instance = new test();
      }
      return self::$_instance;

}

}

$obj1 = test::get_instance();
$obj1->_state = 20;

$obj2 = test::get_instance();//因為這裡引用的還是同一個例項,所以下面輸出屬性的值,還是前面更改的20

echo $obj2->_state;

 

 

以上是給自己總結用。不正確之處歡迎指正。


相關文章