全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

自制PHP框架之设计模式

为什么要使用设计模式?

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

感兴趣的读者可以阅读《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框架  # 设计模式和框架的区别  # easyswoole一键安装脚本及宝塔安装错误问题  # thinkphp框架类库扩展操作示例  # PHP框架实现WebSocket在线聊天通讯系统  # thinkPHP框架乐观锁和悲观锁实例分析  # 浅谈laravel框架与thinkPHP框架的区别  # PHP实现用户异地登录提醒功能的方法【基于thinkPHP框架】  # 自制PHP框架之模型与数据库  # 自制PHP框架之路由与控制器  # 详解PHP框架EasySwoole  # 子类  # 的是  # 要做  # 只有一个  # 用在  # 复用  # 再写  # 我也  # 迭代  # 就会  # 是在  # 都有  # 我会  # 太多  # 就不  # 这就是  # 两种  # 可以用  # 我对  # 会在 


相关文章: 如何用景安虚拟主机手机版绑定域名建站?  郑州企业网站制作公司,郑州招聘网站有哪些?  如何制作一个表白网站视频,关于勇敢表白的小标题?  如何通过西部建站助手安装IIS服务器?  小视频制作网站有哪些,有什么看国内小视频的网站,求推荐?  c++怎么用jemalloc c++替换默认内存分配器【性能】  h5在线制作网站电脑版下载,h5网页制作软件?  整人网站在线制作软件,整蛊网站退不出去必须要打我是白痴才能出去?  建站之星多图banner生成与模板自定义指南  威客平台建站流程解析:高效搭建教程与设计优化方案  建站主机空间推荐 高性价比配置与快速部署方案解析  专业制作网站的公司哪家好,建立一个公司网站的费用.有哪些部分,分别要多少钱?  如何快速生成ASP一键建站模板并优化安全性?  购物网站制作公司有哪些,哪个购物网站比较好?  浅谈Javascript中的Label语句  公司网站制作价格怎么算,公司办个官网需要多少钱?  c# 服务器GC和工作站GC的区别和设置  建站之星导航如何优化提升用户体验?  如何通过WDCP绑定主域名及创建子域名站点?  高防服务器如何保障网站安全无虞?  厦门模型网站设计制作公司,厦门航空飞机模型掉色怎么办?  建站之家VIP精选网站模板与SEO优化教程整合指南  高防网站服务器:DDoS防御与BGP线路的AI智能防护方案  佛山企业网站制作公司有哪些,沟通100网上服务官网?  C#如何使用XPathNavigator高效查询XML  javascript基本数据类型及类型检测常用方法小结  山东云建站价格为何差异显著?  音响网站制作视频教程,隆霸音响官方网站?  魔方云NAT建站如何实现端口转发?  宝塔建站教程:一键部署配置流程与SEO优化实战指南  制作旅游网站html,怎样注册旅游网站?  建站主机解析:虚拟主机配置与服务器选择指南  代购小票制作网站有哪些,购物小票的简要说明?  免费公司网站制作软件,如何申请免费主页空间做自己的网站?  外贸公司网站制作哪家好,maersk船公司官网?  免费网站制作appp,免费制作app哪个平台好?  外汇网站制作流程,如何在工商银行网站上做外汇买卖?  如何快速查询域名建站关键信息?  香港服务器建站指南:免备案优势与SEO优化技巧全解析  哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?  如何在宝塔面板创建新站点?  如何选择美橙互联多站合一建站方案?  制作假网页,招聘网的薪资待遇,会有靠谱的吗?一面试又各种折扣?  如何快速辨别茅台真假?关键步骤解析  高端智能建站公司优选:品牌定制与SEO优化一站式服务  深入理解Android中的xmlns:tools属性  如何快速搭建二级域名独立网站?  网站制作大概要多少钱一个,做一个平台网站大概多少钱?  网站制作网站,深圳做网站哪家比较好?  用v-html解决Vue.js渲染中html标签不被解析的问题 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。