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


一、單件模式

英文叫做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;






 

 



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



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM