自制PHP框架之设计模式


Posted in PHP onMay 07, 2017

为什么要使用设计模式?

设计模式,我的理解是为了达到“可复用”这个目标,而设计的一套相互协作的类。

感兴趣的读者可以阅读《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.类的设计者并不是类的使用者

类的设计者和类的使用者不是同一个开发人员,那么记忆一个字符串或许比记忆一个类名要生动的多。我们都学过计算机网络原理,都知道记忆一个域名要比记忆一个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框架,应该要做好代码的复用。设计模式一直是很多争论的焦点,“究竟该不该使用设计模式?”,本文开始,我也努力回避“过于纠结这个问题”,我认为,设计模式有其存在的价值,至少在具体项目中,确实在很多版本迭代中节省了工作量,提高工作效率,但是如果在一个小项目中为了“秀一下我会设计模式”而使用设计模式,就不合理了。

PHP 相关文章推荐
PHP 类型转换函数intval
Jun 20 PHP
PHP基础教程(php入门基础教程)一些code代码
Jan 06 PHP
PHP中4个加速、缓存扩展的区别和选用建议
Mar 12 PHP
php根据年月获取季度的方法
Mar 31 PHP
thinkphp3.2中Lite文件替换框架入口文件或应用入口文件的方法
May 21 PHP
PHP实现事件机制实例分析
Jun 26 PHP
修复ShopNC使用QQ 互联时提示100010 错误
Nov 08 PHP
PHP面向对象程序设计实例分析
Jan 26 PHP
thinkphp,onethink和thinkox中验证码不显示的解决方法分析
Jun 06 PHP
PHP设计模式之工厂模式与单例模式
Sep 28 PHP
PHP精确计算功能示例
Nov 29 PHP
PHP扩展mcrypt实现的AES加密功能示例
Jan 29 PHP
自制PHP框架之模型与数据库
May 07 #PHP
自制PHP框架之路由与控制器
May 07 #PHP
PHP-CGI远程代码执行漏洞分析与防范
May 07 #PHP
PHP关键特性之命名空间实例详解
May 06 #PHP
PHP 中使用explode()函数切割字符串为数组的示例
May 06 #PHP
Thinkphp 空操作、空控制器、命名空间(详解)
May 05 #PHP
thinkPHP实现的联动菜单功能详解
May 05 #PHP
You might like
Protoss魔法科技
2020/03/14 星际争霸
PHP通用检测函数集合
2006/11/25 PHP
php使用百度翻译api示例分享
2014/01/31 PHP
php实现下载限制速度示例分享
2014/02/13 PHP
php查询ip所在地的方法
2014/12/05 PHP
PHP函数func_num_args用法实例分析
2015/12/07 PHP
php文档工具PHP Documentor安装与使用方法
2016/01/25 PHP
用showModalDialog弹出页面后,提交表单总是弹出一个新窗口
2009/07/18 Javascript
jquery animate 动画效果使用说明
2009/11/04 Javascript
jquery让返回的内容显示在特定div里(代码少而精悍)
2014/06/23 Javascript
sails框架的学习指南
2014/12/22 Javascript
JavaScript更改字符串的大小写
2015/05/07 Javascript
郁闷!ionic中获取ng-model绑定的值为undefined如何解决
2016/08/27 Javascript
node.js实现复制文本到剪切板的功能
2017/01/23 Javascript
JQuery查找子元素find()和遍历集合each的方法总结
2017/03/07 Javascript
微信小程序的分类页面制作
2017/06/27 Javascript
[03:02]生活中的Dendi之野外度假篇
2016/08/09 DOTA
[02:10]DOTA2 TI10勇士令状玩法及不朽Ⅰ展示:焕新世界,如你所期
2020/05/29 DOTA
Windows和Linux下使用Python访问SqlServer的方法介绍
2015/03/10 Python
Python编程中对super函数的正确理解和用法解析
2016/07/02 Python
Python利用ElementTree模块处理XML的方法详解
2017/08/31 Python
完美解决Pycharm无法导入包的问题 Unresolved reference
2018/05/18 Python
对pyqt5中QTabWidget的相关操作详解
2019/06/21 Python
python利用openpyxl拆分多个工作表的工作簿的方法
2019/09/27 Python
简单了解Python3 bytes和str类型的区别和联系
2019/12/19 Python
Python注释、分支结构、循环结构、伪“选择结构”用法实例分析
2020/01/09 Python
压铸汽车模型收藏家:Diecastmodelswholesale.com
2016/12/21 全球购物
绘画专业自荐信
2014/07/04 职场文书
2014年社区民政工作总结
2014/12/02 职场文书
党员个人党性分析材料
2014/12/18 职场文书
2014年加油站站长工作总结
2014/12/23 职场文书
工作简历自我评价
2015/03/11 职场文书
毕业实习单位意见
2015/06/04 职场文书
2016年第32个教师节致辞
2015/11/26 职场文书
《水浒传》读后感3篇(范文)
2019/09/19 职场文书
mysql幻读详解实例以及解决办法
2022/06/16 MySQL