自制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框架,应该要做好代码的复用。设计模式一直是很多争论的焦点,“究竟该不该使用设计模式?”,本文开始,我也努力回避“过于纠结这个问题”,我认为,设计模式有其存在的价值,至少在具体项目中,确实在很多版本迭代中节省了工作量,提高工作效率,但是如果在一个小项目中为了“秀一下我会设计模式”而使用设计模式,就不合理了。

(0)

相关推荐

  • php设计模式小结

    1.单例模式 所谓单例模式,也就是在任何时候,应用程序中只会有这个类的一个实例存在.常见的,我们用到单例模式只让一个对象去访问数据库,从而防止打开多个数据库连接.要实现一个单例类应包括以下几点: 和普通类不同,单例类不能被直接实例化,只能是由自身实例化.因此,要获得这样的限制效果,构造函数必须标记为private. 要让单例类不被直接实例化而能起到作用,就必须为其提供这样的一个实例.因此,就必须要让单例类拥有一个能保存类的实例的私有静态成员变量和对应的一个能访问到实例的公共静态方法. 在PHP中

  • PHP设计模式之观察者模式(Observer)详细介绍和代码实例

    [意图] 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新[GOF95] 又称为发布-订阅(Publish-Subscribe)模式.模型-视图(Model-View)模式.源-监听(Source-Listener)模式.或从属者(Dependents)模式 [观察者模式结构图] [观察者模式中主要角色] 1.抽象主题(Subject)角色:主题角色将所有对观察者对象的引用保存在一个集合中,每个主题可以有任意多个观察者. 抽象主题提供了增加和

  • php设计模式 Template (模板模式)

    继承关系由于自身的缺陷,被专家们扣上了"罪恶"的帽子."使用委派关系代替继承关系","尽量使用接口实现而不是抽象类继承"等等专家警告,让我们这些菜鸟对继承"另眼相看".其实,继承还是有很多自身的优点所在.只是被大家滥用的似乎缺点更加明显了.合理的利用继承关系,还是能对你的系统设计起到很好的作用的.而模板方法模式就是其中的一个使用范例. GOF给模板方法(Template Method)模式定义一个操作中的算法的骨架,而将一些步

  • PHP最常用的2种设计模式工厂模式和单例模式介绍

    1.工厂模式 主要作用是降低耦合度. 复制代码 代码如下: abstract class Operation{ abstract public function getValue($num1,$num2); public function getAttr(){ return 1; } } class Add extends Operation{ public function getValue($num1, $num2){ return $num1+$num2; } } class Sub ex

  • php设计模式 DAO(数据访问对象模式)

    复制代码 代码如下: <?php /** * 数据访问对象(Data Access Object) 示例 * * @create_date: 2010-01-04 */ class BaseDAO { var $_db = null; var $_table = null; function BaseDAO($config) { $this->_db = new MysqlDB(); // 这里的不能进行操作 } /** * 获取处理 * * @param array $filter // 过

  • php设计模式 Proxy (代理模式)

    代理,指的就是一个角色代表另一个角色采取行动,就象生活中,一个红酒厂商,是不会直接把红酒零售客户的,都是通过代理来完成他的销售业务.而客户,也不用为了喝红酒而到处找工厂,他只要找到厂商在当地的代理就行了,具体红酒工厂在那里,客户不用关心,代理会帮他处理. 代理模式,就是给某一对象提供代理对象,并由代理对象控制具体对象的引用. 代理模式涉及的角色: 抽象主题角色,声明了代理主题和真实主题的公共接口,使任何需要真实主题的地方都能用代理主题代替. 代理主题角色,含有真实主题的引用,从而可以在任何时候操

  • php设计模式 Singleton(单例模式)

    复制代码 代码如下: <?php /** * 单例模式 * * 保证一个类仅有一个实例,并提供一个访问它的全局访问点 * */ class Singleton { static private $_instance = null; private function __construct() { } static public function getInstance() { if(is_null(self::$_instance)) { self::$_instance = new Singl

  • php单态设计模式(单例模式)实例

    单态设计模式也叫单例模式: 1.单态设计模式含义: 单态模式的主要作用是保证在面向对象编程设计中,一个类只能有一个实例对象存在.作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统全局地提供这个实例.它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用. 2.单台模式的三个关键点: ① 需要一个保存类的唯一实例的静态成员变量: ②构造函数和克隆函数必须声明为私有的,防止外部程序new类从而失去单例模式的意义: ③必须提供一个访问这个实例的公共的静态方法(通常为

  • php设计模式之简单工厂模式详解

    本文以实例形式较为详细的介绍了PHP设计模式的简单工厂模式,对于进行PHP程序设计来说有很好的借鉴作用.具体如下: 一.概念 简单工厂模式 [静态工厂方法模式](Static Factory Method) 是类的创建模式 工厂模式的几种形态: 1.简单工厂模式(Simple Factory)又叫做 静态工厂方法模式(Static Factory Method) 2.工厂方法模式(Factory Method)又叫做 多态性工厂模式(Polymorphic Factory) 3.抽象工厂模式(A

  • 自制PHP框架之设计模式

    为什么要使用设计模式? 设计模式,我的理解是为了达到"可复用"这个目标,而设计的一套相互协作的类. 感兴趣的读者可以阅读<Design Patterns: Elements of Reusable Object-Oriented Software>,四位作者(Gang of Four)在书中列举了业界闻名的23种设计模式. 这里先介绍我们框架要涉及的三种设计模式. 单例模式(singleton) 单例模式可以保证一个类只有一个对象实例, 常用在数据库存取类,从而节省硬件资源

  • 自制PHP框架之模型与数据库

    什么是模型? 我们的WEB系统一定会和各种数据打交道,实际开发过程中,往往一个类对应了关系数据库的一张或多张数据表,这里就会出现两个问题. 1.类和数据表,一方修改会导致另一方的修改,只要数据表结构不定下来,业务逻辑的开发几乎没法开工 2.获取数据时会牵涉很多SQL语句的拼接,如果数据结构变动,这些SQL需要改写 假如要开发一个博客系统,我们先设计两个Model和两张数据表 第一张数据表,表名是post,存储了博客文章,数据如下: 第二章数据表,表名是comment,存储了博客文章的评论,数据如

  • 自制PHP框架之路由与控制器

    我们为什么要使用路由?原因1:一个更漂亮的URI 1.URI的改进 刚刚开始学PHP时,我们一定写过blog.php?id=1之类的URI,使用GET方式获取参数.这样的URI有两个缺点,一是容易被SQL注射攻击,二是维护性可读性差,大家可以比较下面两种URI哪一种更具备可读性. www.mysite.com/blog.php?id=1 上面URI是我们初学PHP最常用的. www.mysite.com/blog/1 这种URI是目前最流行的URI,举个例子,比如很多读书类,电影类网站,都使用了

  • 浅析php单例模式

    本系列文章来总结一下设计模式在PHP中的应用,这是第一篇创建型模式之单例模式. 一.设计模式简介 首先我们来认识一下什么是设计模式: 设计模式是一套被反复使用.容易被他人理解的.可靠的代码设计经验的总结. 设计模式不是Java的专利,我们用面向对象的方法在PHP里也能很好的使用23种设计模式. 那么我们常说的架构.框架和设计模式有什么关系呢? 架构是一套体系结构,是项目的整体解决方案:框架是可供复用的半成品软件,是具体程序代码.架构一般会涉及到采用什么样的框架来加速和优化某部分问题的解决,而好的

  • 详解探索 vuex 2.0 以及使用 vuejs 2.0 + vuex 2.0 构建记事本应用

    前言 首先说明这并不是一个教程贴,而记事本应用是网上早有的案例,对于学习 vuex 非常有帮助.我的目的是探索 vuex 2.0 ,然后使用 vuejs 2.0 + vuex 2.0 重写这个应用,其中最大的问题是使用 vue-cli 构建应用时遇到的问题.通过这些问题深入探索 vue 以及 vuex . 我对于框架的学习一直断断续续,最先接触的是 react,所以有一些先入为主的观念,喜欢 react 更多一点,尤其在应用的构建层面来说.之所以断断续续,是因为自己 JS 基础较弱,刚开始学习的

  • 图解Spring框架的设计理念与设计模式

    本文主要剖析Spring框架的作者设计Spring框架的骨骼架构的设计理念,有那几个核心组件?为什么需要这些组件?它们又是如何结合在一起构成Spring的骨骼架构?Spring的AOP特性又是如何利用这些基础的骨骼架构来工作的?Spring中又使用了那些设计模式来完成它的这种设计的?它的这种设计理念对我们以后的软件设计有何启示?本文将详细解答这些问题. Spring的骨骼架构 Spring总共有十几个组件,但是真正核心的组件只有几个,下面是Spring框架的总体架构图: 图1.Spring框架的

  • 设计模式在Spring框架中的应用汇总

    这篇文章主要介绍了设计模式在Spring框架中的应用汇总,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 在开始正文之前,请你先思考几个问题: 你项目中有使用哪些 GOF 设计模式 说一说 GOF 23 种设计模式的设计理念 说说 Spring 框架中如何实现设计模式 假设我是面试官问起了你这些面试题,你该如何回答呢,请先思考一分钟. 好的,我们开始进入正题.设计模式实践里面提供了许多经久不衰的解决方案和最佳方案.这里,GOF 设计模式主要分为三

  • python web自制框架之接受url传递过来的参数实例

    我们知道,在django里有个request,可以接收表单等前端传过来的数据,现在我们也做一个类似的功能. 首先我们定义一个类class,然后初始化数据与定义保存参数的方法,如下: class Request(object): def __init__(self): self.method = 'GET' self.path = '' self.query = {} self.body = '' def form(self): body = urllib.parse.unquote(self.b

  • 在Java Spring框架中使用的设计模式有哪些

    目录 1 简单工厂模式 2 工厂方法模式 3 单例模式 4 代理模式 好处 静态代理 动态代理 1 简单工厂模式 当A对象需要调用B对象的方法时,我们需要在A中new一个B的实例,我们把这种方式叫作硬编码耦合,缺点是一旦需求发生变化,比如需要使用C类来代替B时,就要改写A类的方法. 假如应用中有上千个类以硬编码的方式耦合了B,改就很头疼. 于是有了简单工厂模式,又叫静态工厂方法,就是由一个工厂类根据传入参数,动态决定应该创建哪个产品类. Spring中的BeanFactory就是简单工厂模式的体

  • 设计模式中的迭代器模式在Cocoa Touch框架中的使用

    基本理解 迭代器模式(Iterrator):提供一个方法顺序访问一个聚合对象中的各个元素,而又不暴露该元素的内部表示. 当你访问一个聚合对象,而且不管这些对象是什么都需要遍历的时候,你就应该考虑用迭代器模式. 你需要对聚集有多种方式遍历时,可以考虑用迭代器模式. 迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据. 迭代器定义了一个用于访问集合元素并记录当前元素的接口. 不同的迭代器可以执行不同的迭代策略.

随机推荐