往期回顾:「七天自制PHP框架」第二天:模型与数据库,点击此处

原文地址:http://www.cnblogs.com/sweng/p/6624845.html,欢迎关注:编程老头

为什么要使用设计模式?

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

感兴趣的读者可以阅读《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实现的设计模式的更多相关文章

  1. 「七天自制PHP框架」第四天:模型关联

    往期回顾:「七天自制PHP框架」第三天:PHP实现的设计模式,点击此处 原文地址:http://www.cnblogs.com/sweng/p/6624845.html,欢迎关注:编程老头 前阵子在网 ...

  2. 「七天自制PHP框架」第二天:模型与数据库

    往期回顾:「七天自制PHP框架」第一天:路由与控制器,点击此处 什么是模型? 我们的WEB系统一定会和各种数据打交道,实际开发过程中,往往一个类对应了关系数据库的一张或多张数据表,这里就会出现两个问题 ...

  3. 「七天自制PHP框架」应用:JSON生成器

    刚刚开始学做一个WebAPP,数据查询的一般套路是通过一张PHP页面读取数据库,获得列表后“嵌写”在PHP页面中,虽然写法上丑陋至极,但也有“快糙猛”出效果的成就感,如图. 后来想想,不对啊,难道以后 ...

  4. 「七天自制PHP框架」第一天:路由与控制器

    我们为什么要使用路由? 原因1:一个更漂亮的URI 1.URI的改进 刚刚开始学PHP时,我们一定写过blog.php?id=1之类的URI,使用GET方式获取参数.这样的URI有两个缺点,一是容易被 ...

  5. 「七天自制PHP框架」应用:Model外键链接

    这里以行政区数据为例: 一级行政区数据范例: 二级行政区范例: 三级行政区范例: 在Model层建立三个Model class ProvinceModel extends Model{ public ...

  6. 「7天自制PHP框架」第一天:路由与控制器

    我们为什么要使用路由? 原因1:一个更漂亮的URI 1.URI的改进 刚刚开始学PHP时,我们一定写过blog.php?id=1之类的URI,使用GET方式获取参数.这样的URI有两个缺点,一是容易被 ...

  7. Web自动化必会知识:「Web基础、元素定位、元素操作、Selenium运行原理、项目实战+框架」

    1.web 基础-html.dom 对象.js 基本语法 Dom 对象里面涉及元素定位以及对元素的修改.因为对元素操作当中涉及的一些 js 操作,js 基本语法要会用.得要掌握前端的基本用法.为什么要 ...

  8. 「拥抱开源, 又见 .NET」系列第三次线下活动简报

    「拥抱开源, 又见 .NET」 随着 .NET Core的发布和开源,.NET又重新回到人们的视野. 自2016年 .NET Core 1.0 发布以来,其强大的生命力让越来越多技术爱好者对她的未来满 ...

  9. 「About Blockchain(一)」达沃斯年会上的区块链

    「About Blockchain(一)」 --达沃斯年会上的区块链 写在前面:1月23日到26日,在瑞士达沃斯召开了第48届世界经济论坛.这个新闻本没有引起我格外的关注,直到前两天张老师分享给我一篇 ...

随机推荐

  1. NodeJs中process.cwd()与__dirname的区别

    process.cwd() 是当前执行node命令时候的文件夹地址 ——工作目录,保证了文件在不同的目录下执行时,路径始终不变__dirname 是被执行的js 文件的地址 ——文件所在目录 Node ...

  2. 【学习笔记】TCP通信的细节及TCP连接对HTTP事务处理性能影响

    从三次握手的细节说起 刚开始尝试使用java等后端语言写IO流,或用套接字(socket)实现简单C/S通信的同学们,常常会接触到的一个概念:就是所谓的"三次握手",socket作 ...

  3. iOS截取http/https流量

    0x01.Why? 做移动测试的同学经常会在app和server中间架设一个代理(例如charles或者fiddler等),由经代理,app和server之间的交互及交互内容变得可视化,使得我们不再摸 ...

  4. Eclipse中将含有图片资源的项目打包成jar文件

    前言: 最近学了GUI编程和UDP协议,心血来潮想做一个局域网内的聊天软件,前期都还算顺利,直到后来将整个项目打包成jar文件时遇到了困难.如图: 自己设置的图标不见了,但是也没有默认的图标,说明图片 ...

  5. Linux 上做免密码登陆

    基于密钥验证1.生成一对密钥ssh-keygen -t {rsa|dsa} -f /path/to/keyfile -N 'password'2.将公钥传输至服务器某用户的家目录的.ssh/autho ...

  6. 实现一个自己的promise

    这是小弟的一篇开篇小作,如有不当之处,请各位道友批评指正.本文将探讨Promise的实现. 一.ES6中的Promise 1.简介 据说js很早就实现了Promise,我是不知道的,我第一次接触Pro ...

  7. Java基础学习(三)—面向对象(上)

    一.理解面向对象       面向对象是一种思想,是基于面向过程而言的,就是说面向对象是将功能等通过对象来实现,将功能封装进对象之中,让对象去实现具体的细节:这种思想是将数据作为第一位,而方法或者说是 ...

  8. 500. Keyboard Row

    Given a List of words, return the words that can be typed using letters of alphabet on only one row' ...

  9. 栈实现getMin

    题目 实现一个特殊的栈,在实现栈的基本功能的基础上,在实现返回栈中最小元素的操作. 要求 pop.push.getMin操作的时间复杂度都是O(1). 设计的栈类型可以使用现成的栈结构. 解答 在设计 ...

  10. CSS开发框架技术OOCSS编写和管理CSS的方法

    目前最流行的CSS开发框架技术当属OOCSS,尽管还有其他类似技术(如BEM).这些方法试图对CSS采用面向对象的编程原则.样式语言与面向对象的设计原则在概念之间存在一定的问题.欠缺经验的人员可能不会 ...