设计php框架_自制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框架之设计模式相关推荐
- python 分布式计算框架_漫谈分布式计算框架
如果问 mapreduce 和 spark 什么关系,或者说有什么共同属性,你可能会回答他们都是大数据处理引擎.如果问 spark 与 tensorflow 呢,就可能有点迷糊,这俩关注的领域不太一样 ...
- 基于python的个人博客系统的设计开题报告_基于JavaSSM框架的个人博客系统设计与实现开题报告...
1.本课题研究的意义: 研究目的: 从本质上讲,博客是一种表达个人思想.网络连接.内容,按时间排列顺序,并且不断更新的出版方式,是网络时代的个人文摘,它代表着新的生活.工作方式,更代表着新的学习方式. ...
- java审批流程框架_基于SSM框架下的JAVA企业流程审批系统
每天记录学习,每天会有好心情.*^_^* 今天将为大家分析一个企业流程审批系统(现代企业对资金流的控制十分严格,但是绝大部分企业的费用审批还停滞在手动填单.逐级递交.逐级审批的现状,既没有效率也不利于 ...
- grpc框架_分布式RPC框架dubbo、motan、rpcx、gRPC、thrift简介与性能比较
Dubbo Dubbo 是阿里巴巴公司开源的一个Java高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成.曾有一段时间放弃维护,不过当前 ...
- guice 框架_玩! 框架+ Google Guice
guice 框架 在我目前正在工作的项目中,我们开始使用Google Guice. 对于那些不知道的人, Google Guice是一个依赖项注入框架. 依赖项注入的基本思想是提供一个其依赖的类,而不 ...
- quartz框架_定时任务调度框架Quartz
最近需要写一个每天定点自动执行的定时任务,对于以前自己写小项目,可能会选择java自带的Timer类,但是对于公司中的项目,Timer类实现定时任务只能有一个后台线程执行任务,并且只能让程序按照某个频 ...
- wpf ui框架_.NET跨平台框架Avalonia UI
转自:TwilightLemoncnblogs.com/TwilightLemon/p/13474796.html 前言 Avalonia 是一个跨平台的.NET UI框架,支持 Windows.Li ...
- 开源框架_跨平台开源框架对比介绍
知识库 夕阳下你的背影 轻轻嗅探你诱人的气息 幻想远方的你就在身边 这的空气也已不再污浊 若隐若现 知识库主要记录生活,工作不断归纳总结的经验,能时刻指导我不断清空自我,处于空杯状态去面对未来和过去 ...
- java 路由框架_使用Spring框架和AOP实现动态路由
本文的大体思路是展示了一次业务交易如何动态地为子系统处理过程触发业务事件.本文所示的例子使用Spring框架和Spring AOP有效地解耦业务服务和子系统处理功能.现在让我们仔细看看业务需求. 业务 ...
最新文章
- 集成Lua到你的Android游戏 - 转
- 全卷积神经网络( FCN ):语义分割深度模型先驱
- 建模心法(1)——百战不殆是可能的吗?
- map的内存释放问题
- Linux下LCD自动关闭解决方法,一般就自动十分钟黑屏的原因
- 深度学习论文翻译--Deep Residual Learning for Image Recognition
- QT乱码总结0.Qt乱码产生因素
- Ubuntu14.04-LAMP环境搭建
- 电脑软件:推荐八款提高工作效率的软件,值得收藏!
- 注释 向 Java 代码中添加元数据
- Java是如何实现跨平台运行的
- options请求_HTTP 协议 POST 请求,为什么会有一个 OPTION?
- git jenkins 子目录_在Jenkins中,如何将项目签出到特定目录(使用GIT)
- P2P共享经济都可接受 O2O×××有何不可?
- html中怎样设置放大功能,CSS如何设置图片放大效果?
- matlab信号如何加白噪声,matlab给信号加白噪声
- 网络配置问题Bringing up interface eth0: Device eth0 does not seem to be present, delaying initialization.
- 0基础学HTML,从快捷方式(IntelliJ IDEA)入手,一步步就能看懂了!
- python里的百分号_python中的百分号
- 一行代码深度定制你的专属二维码:(amzqr、MyQR制作动态二维码)
热门文章
- JavaFX技巧2:使用Canvas API进行清晰绘图
- Devoxx 2012:Java 8 Lambda和并行性,第1部分
- 通过beforeClass和afterClass设置增强Spring Test Framework
- Java 7功能概述
- Sublime Text for Mac 如何格式化代码
- Linux 中 ctime,mtime,atime 的区别
- Java包命名规则_包命名规范
- c 调用c语言dll数组,C#调用C类型dll入参为struct的问题详解
- linux升级tls,为CentOS升级OpenSSL 让Nginx支持TLS 1.2
- linux终端cd未找到命令,为什么`which`命令不能用于`cd`?我也找不到`cd`的可执行文件!...