「七天自制PHP框架」第三天:PHP實現的設計模式

往期回顧:「七天自制PHP框架」次日:模型與數據庫,點擊此處php

原文地址:http://www.cnblogs.com/sweng/p/6624845.html,歡迎關注:編程老頭html

爲何要使用設計模式?

設計模式,個人理解是爲了達到「可複用」這個目標,而設計的一套相互協做的類。mysql

感興趣的讀者能夠閱讀《Design Patterns: Elements of Reusable Object-Oriented Software》,四位做者(Gang of Four)在書中列舉了業界聞名的23種設計模式。程序員

這裏先介紹咱們框架要涉及的三種設計模式。sql

單例模式(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.類的設計者並非類的使用者

類的設計者和類的使用者不是同一個開發人員,那麼記憶一個字符串或許比記憶一個類名要生動的多。咱們都學過計算機網絡原理,都知道記憶一個域名要比記憶一個IP地址要生動的多,這就是DNS解決的問題。

由於抽象工廠不少教材都有涉及,再也不贅述,本文將介紹一下目前很是流行的服務容器。

咱們但願整個工程項目中,DB類,Session類,FileSystem類「拿來即用」,不用每次繁瑣的初始化,好比寫$db=new DB(arg1,arg2);這類語句,也但願DB等類型的對象像一個「全局」變量通常,在整個程序運行期間,隨時能夠調用。

服務容器可讓調用DB等類型的程序員不用知道這個類太多的細節,甚至能夠用一個字符串的別名來建立這樣一個對象。

咱們定義一個服務容器類

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);
	}
}

能夠把服務容器簡單的當作一個全局變量,bind方法就是用關聯數組把字符串和構造函數作綁定。

至此,有了服務容器,咱們的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);
	}
}

觀察上面代碼,咱們同時用了單例模式和服務容器。

總結:若是要作一個PHP框架,應該要作好代碼的複用。設計模式一直是不少爭論的焦點,「究竟該不應使用設計模式?」,本文開始,我也努力迴避「過於糾結這個問題」,我認爲,設計模式有其存在的價值,至少在具體項目中,確實在不少版本迭代中節省了工做量,提升工做效率,可是若是在一個小項目中爲了「秀一下我會設計模式」而使用設計模式,就不合理了。

相關文章
相關標籤/搜索