自制PHP框架之設(shè)計(jì)模式
為什么要使用設(shè)計(jì)模式?
設(shè)計(jì)模式,我的理解是為了達(dá)到“可復(fù)用”這個(gè)目標(biāo),而設(shè)計(jì)的一套相互協(xié)作的類。
感興趣的讀者可以閱讀《Design Patterns: Elements of Reusable Object-Oriented Software》,四位作者(Gang of Four)在書(shū)中列舉了業(yè)界聞名的23種設(shè)計(jì)模式。
這里先介紹我們框架要涉及的三種設(shè)計(jì)模式。
單例模式(singleton)
單例模式可以保證一個(gè)類只有一個(gè)對(duì)象實(shí)例, 常用在數(shù)據(jù)庫(kù)存取類,從而節(jié)省硬件資源的消耗。
這里,我們改寫上一章節(jié)的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); } }
這里要注意的是,如果實(shí)例化一個(gè)MySQL類,我們不再寫
$db=new MySQL();
而是這樣:
$db=MySQL::getInstance();
因?yàn)橹挥術(shù)etInstance這個(gè)靜態(tài)函數(shù),才能保證只調(diào)用一次MySQL類的構(gòu)造函數(shù)。
單例模式是很常用的設(shè)計(jì)模式,這里不再贅述。
外觀模式(Facade)
因?yàn)槊臻g的問(wèn)題,外觀模式可以保證一個(gè)類的諸多方法看似是“一個(gè)類提供的”,這里我們先設(shè)計(jì)一個(gè)簡(jiǎn)單的服務(wù)提供者類
class ServiceProvider{ public function Write($arg){ echo $arg; } }
這個(gè)類只有一個(gè)Write方法,就是把參數(shù)打印出來(lái)
然后定義一個(gè)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); } }
要理解這個(gè)類,我們只要關(guān)注最后一個(gè)函數(shù),就是__callstatic魔術(shù)方法。這個(gè)方法就是Facade類型對(duì)象或者其子類在調(diào)用他自身沒(méi)有定義過(guò)的函數(shù)時(shí),就會(huì)調(diào)用__callstatic方法,而這個(gè)方法最后調(diào)用了call_user_func_array函數(shù),就是把任務(wù)交給提供這項(xiàng)服務(wù)的類去完成,同時(shí)完成參數(shù)的傳遞。
我們?cè)賹懸粋€(gè)Facade子類
class MyFacade extends Facade{ public static function getFacadeAccessor(){ return ServiceProvider::class; } }
這里注意,子類實(shí)現(xiàn)了父類沒(méi)有具體實(shí)現(xiàn)的getFacadeAccessor方法,這個(gè)方法就是要告訴父類的__callstatic方法:“我作為Facade,代表的是什么哪個(gè)類,任務(wù)就由他來(lái)實(shí)現(xiàn)吧”,從語(yǔ)法上看,只是返回了一個(gè)表示類名的字符串。所以父類起初并不知道它的子類都代表著什么“服務(wù)提供者類”,只有當(dāng)子類的靜態(tài)函數(shù)被調(diào)用后,因?yàn)樽宇悰](méi)有該靜態(tài)函數(shù),所以父類的__callstatic方法被啟動(dòng)了。
抽象工廠(Factory)
我對(duì)抽象工廠有一個(gè)粗俗的理解:“對(duì)象與字符串的對(duì)應(yīng)”,也就是用一個(gè)字符串就可以創(chuàng)造一個(gè)類的對(duì)象。這種做法主要用在兩種情況下是很方便的:
1.類名不穩(wěn)定,會(huì)在項(xiàng)目中頻繁修改
類名修改,很多時(shí)候并不是設(shè)計(jì)者的“命名潔癖”或者“命名強(qiáng)迫癥”導(dǎo)致的修改,而是在項(xiàng)目的不斷迭代,發(fā)覺(jué)這個(gè)類設(shè)計(jì)的不合理。如果這個(gè)類用的不頻繁,那么改個(gè)類名只要手工做一些小的修改即可,但是如果這個(gè)類通篇存在于代碼之中(假如是數(shù)據(jù)庫(kù)類),那修改工作量就大了,當(dāng)然,我們也可以對(duì)代碼文件使用“字符串替換”,但是假如一個(gè)PHP寫成的項(xiàng)目,PHP文件有幾十上百個(gè),這也是不合理的事。
2.類的設(shè)計(jì)者并不是類的使用者
類的設(shè)計(jì)者和類的使用者不是同一個(gè)開(kāi)發(fā)人員,那么記憶一個(gè)字符串或許比記憶一個(gè)類名要生動(dòng)的多。我們都學(xué)過(guò)計(jì)算機(jī)網(wǎng)絡(luò)原理,都知道記憶一個(gè)域名要比記憶一個(gè)IP地址要生動(dòng)的多,這就是DNS解決的問(wèn)題。
因?yàn)槌橄蠊S很多教材都有涉及,不再贅述,本文將介紹一下目前非常流行的服務(wù)容器。
我們希望整個(gè)工程項(xiàng)目中,DB類,Session類,F(xiàn)ileSystem類“拿來(lái)即用”,不用每次繁瑣的初始化,比如寫$db=new DB(arg1,arg2);這類語(yǔ)句,也希望DB等類型的對(duì)象像一個(gè)“全局”變量一般,在整個(gè)程序運(yùn)行期間,隨時(shí)可以調(diào)用。
服務(wù)容器可以讓調(diào)用DB等類型的程序員不用知道這個(gè)類太多的細(xì)節(jié),甚至可以用一個(gè)字符串的別名來(lái)創(chuàng)建這樣一個(gè)對(duì)象。
我們定義一個(gè)服務(wù)容器類
class Container{ public $bindings; public function bind($abstract,$concrete){ $this->bindings[$abstract]=$concrete; } public function make($abstract,$parameters=[]){ return call_user_func_array($this->bindings[$abstract],$parameters); } }
可以把服務(wù)容器簡(jiǎn)單的看成一個(gè)全局變量,bind方法就是用關(guān)聯(lián)數(shù)組把字符串和構(gòu)造函數(shù)做綁定。
至此,有了服務(wù)容器,我們的Model類就要做修改了
class Model implements IModel{ public static $table; public static $container; public static $db; public function __construct(){ self::$container=new Container(); self::$container->bind('db',function(){ return MySQL::getInstance(); }); self::$db=self::$container->make('db',[]); } public static function get($id){ return self::where('id',$id); } public static function where($condition,$value){ $sql=sprintf("select * from %s where %s='%s'",self::$table,$condition,$value); return self::$db->Query($sql); } public static function all(){ $sql=sprintf("select * from %s",self::$table); return self::$db->Query($sql); } }
觀察上面代碼,我們同時(shí)用了單例模式和服務(wù)容器。
總結(jié):如果要做一個(gè)PHP框架,應(yīng)該要做好代碼的復(fù)用。設(shè)計(jì)模式一直是很多爭(zhēng)論的焦點(diǎn),“究竟該不該使用設(shè)計(jì)模式?”,本文開(kāi)始,我也努力回避“過(guò)于糾結(jié)這個(gè)問(wèn)題”,我認(rèn)為,設(shè)計(jì)模式有其存在的價(jià)值,至少在具體項(xiàng)目中,確實(shí)在很多版本迭代中節(jié)省了工作量,提高工作效率,但是如果在一個(gè)小項(xiàng)目中為了“秀一下我會(huì)設(shè)計(jì)模式”而使用設(shè)計(jì)模式,就不合理了。
相關(guān)文章
PHP中date()日期函數(shù)有關(guān)參數(shù)整理
PHP中date()日期函數(shù)有關(guān)參數(shù)整理,需要的朋友可以參考下。2011-07-07php實(shí)現(xiàn)的簡(jiǎn)單美國(guó)商品稅計(jì)算函數(shù)
這篇文章主要介紹了php實(shí)現(xiàn)的簡(jiǎn)單美國(guó)商品稅計(jì)算函數(shù),涉及php數(shù)學(xué)計(jì)算的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07PHP callback函數(shù)使用方法和注意事項(xiàng)
這篇文章主要介紹了PHP callback函數(shù)使用方法和注意事項(xiàng),本文講解了callback函數(shù)的一些使用技巧和避免事項(xiàng),并給出了一個(gè)使用實(shí)例,需要的朋友可以參考下2015-01-01PHP數(shù)據(jù)庫(kù)操作之基于Mysqli的數(shù)據(jù)庫(kù)操作類庫(kù)
Mysqli 是什么,我這里也不進(jìn)行描述了。因?yàn)榫W(wǎng)上關(guān)于 Mysqli 的教程數(shù)不勝數(shù),我這里為大家介紹一款基于 Mysqli 的操作數(shù)據(jù)庫(kù)類(M.class.php)2014-04-04PHP使用redis實(shí)現(xiàn)統(tǒng)計(jì)緩存mysql壓力的方法
這篇文章主要介紹了PHP使用redis實(shí)現(xiàn)統(tǒng)計(jì)緩存mysql壓力的方法,涉及PHP操作MySQL數(shù)據(jù)庫(kù)及使用Redis統(tǒng)計(jì)的相關(guān)技巧,需要的朋友可以參考下2015-11-1110個(gè)簡(jiǎn)化PHP開(kāi)發(fā)的工具
這篇文章主要介紹了10個(gè)簡(jiǎn)化PHP開(kāi)發(fā)的工具,都是今年比較熱門和使用的,需要的朋友可以參考下2014-12-12PHP的中使用非緩沖模式查詢數(shù)據(jù)庫(kù)的方法
緩沖查詢和非緩沖查詢(Buffered and Unbuffered queries)。PHP的查詢?nèi)笔∧J绞蔷彌_模式。也就是說(shuō),查詢數(shù)據(jù)結(jié)果會(huì)一次全部提取到內(nèi)存里供PHP程序處理,需要的朋友可以參考下2017-02-02基于PHP RSA密文過(guò)長(zhǎng)加密解密 越過(guò)1024的解決方法
下面小編就為大家分享一篇基于PHP RSA密文過(guò)長(zhǎng)加密解密 越過(guò)1024的解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-03-03