為什么要使用設計模式?
設計模式,我的理解是為了達到“可復用”這個目標,而設計的一套相互協作的類。
感興趣的讀者可以閱讀《Design Patterns: Elements of Reusable Object-Oriented Software》,四位作者(Gang of Four)在書中列舉了業界聞名的23種設計模式。
這里先介紹我們框架要涉及的三種設計模式。
單例模式(singleton)
單例模式可以保證一個類只有一個對象實例, 常用在數據庫存取類,從而節省硬件資源的消耗。
這里,我們改寫上一章節的MySQL類
class MySQL extends DB{ private static $instance=null; public static function getInstance(){ if(self::$instance==null){ self::$instance=new MySQL(); } return self::$instance; } public function MySQL(){ /*Config*/ $this->IP='*'; $this->ServerID='*'; $this->ServerPassword='*'; $this->DataBaseName='*'; /*End of Config*/ $this->connection=mysqli_connect($this->IP,$this->ServerID,$this->ServerPassword,$this->DataBaseName); if(!$this->connection){ die('Could not connect'.$this->connection); } mysqli_query($this->connection,'set names utf8'); } public function Execute($sql){ return mysqli_query($this->connection,$sql); } public function Query($sql){ $result=mysqli_query($this->connection,$sql); $arr=array(); while($row=mysqli_fetch_array($result)){ $arr[]=$row; } return $arr; } public function Close(){ mysqli_close($this->connection); } }
這里要注意的是,如果實例化一個MySQL類,我們不再寫
$db=new MySQL();
而是這樣:
$db=MySQL::getInstance();
因為只有getInstance這個靜態函數,才能保證只調用一次MySQL類的構造函數。
單例模式是很常用的設計模式,這里不再贅述。
外觀模式(Facade)
因為命名空間的問題,外觀模式可以保證一個類的諸多方法看似是“一個類提供的”,這里我們先設計一個簡單的服務提供者類
class ServiceProvider{ public function Write($arg){ echo $arg; } }
這個類只有一個Write方法,就是把參數打印出來
然后定義一個Facade類
class Facade{ public static function getInstance($classname,$args){ return new $classname($args); } public static function getFacadeAccessor(){ // } public static function __callstatic($method,$args){ $instance=static::getInstance(static::getFacadeAccessor(),$args); return call_user_func_array(array($instance,$method),$args); } }
要理解這個類,我們只要關注最后一個函數,就是__callstatic魔術方法。這個方法就是Facade類型對象或者其子類在調用他自身沒有定義過的函數時,就會調用__callstatic方法,而這個方法最后調用了call_user_func_array函數,就是把任務交給提供這項服務的類去完成,同時完成參數的傳遞。
我們再寫一個Facade子類
class MyFacade extends Facade{ public static function getFacadeAccessor(){ return ServiceProvider::class; } }
這里注意,子類實現了父類沒有具體實現的getFacadeAccessor方法,這個方法就是要告訴父類的__callstatic方法:“我作為Facade,代表的是什么哪個類,任務就由他來實現吧”,從語法上看,只是返回了一個表示類名的字符串。所以父類起初并不知道它的子類都代表著什么“服務提供者類”,只有當子類的靜態函數被調用后,因為子類沒有該靜態函數,所以父類的__callstatic方法被啟動了。
抽象工廠(Factory)
我對抽象工廠有一個粗俗的理解:“對象與字符串的對應”,也就是用一個字符串就可以創造一個類的對象。這種做法主要用在兩種情況下是很方便的:
1.類名不穩定,會在項目中頻繁修改
類名修改,很多時候并不是設計者的“命名潔癖”或者“命名強迫癥”導致的修改,而是在項目的不斷迭代,發覺這個類設計的不合理。如果這個類用的不頻繁,那么改個類名只要手工做一些小的修改即可,但是如果這個類通篇存在于代碼之中(假如是數據庫類),那修改工作量就大了,當然,我們也可以對代碼文件使用“字符串替換”,但是假如一個PHP寫成的項目,PHP文件有幾十上百個,這也是不合理的事。
2.類的設計者并不是類的使用者