PHP设计模式
说明: 本文档用于学习PHP设计模式时的记录和分享,欢迎各界人士转载和收藏。为了尊重著作人的辛苦和汗水,请在转载的时候注明出处,谢谢!
1. MVC模式
由于WEB应用越来越多样化,使用技术的分支也就越来越多。通常一个web应用需要:服务端的PHP语言,Mysql数据库,页面的HTML+CSS,和前端的Javascript。对于技术团队而言,这些技术通常需要设置:PHP工程师,前端工程师,和数据库工程师。这样每个岗位就可以将技术剥离开来。每个开发人员就可以只专注于自己的那一部分。
于是,MVC模式就应运而生的。最为代表的是smarty,后来PHP的框架陆续的都是基于MVC模式设计的。
1.1 MVC的结构和作用
- M(model):数据模型层,顾名思义是操作数据库层面的类和方法的集合。
- V(View):视图层,用于存放web前端的HTML和JS的业务逻辑
- C(Controller):控制器层,路由指向的业务逻辑层。
在我们使用的框架中,无论是laravel,还是ThinkPHP,还是Kohana,在我们的代码区域文件夹内都会内置:controller,model,view三个为文件夹,通过框架内置的router和autoload的功能完成三个部分的整合。
虽然,每个PHP Developer 都一直在用这个设计模式,还是有不少人并没有真正理解这个模式的真正内涵。例如:我们在写model的时候,很多人为了把model的功能写的强大,而不断的丰富某个model方法,最终导致,在控制器层面只是做了个参数获取,和model的调用。而实际上,我们的model,要写的越简单越好,如果你的model写完,DBA可以很轻松的阅读你的代码,那么你就成功了。
说道这里,有些人会疑惑,那么数据验证应该放在哪呢?在kohanna框架中我们会看到跟controller,model文件夹同级还有一个文件夹叫:helper。这这个文件夹内可以存放各种我们开发中无法区分是业务逻辑还是数据模型操作的代码。
###1.2 HMVC的概念
前面我们介绍MVC,其实是为了抛砖引玉的。我们的重点在这。H是什么?我也不知道,我不喜欢去记一些,看似很牛逼的名词。有的地方也称之为DMVC。
但是我们需要知道的是:在MVC前加了一个H,肯定是在MVC的基础上做了一个升级。到底做了什么升级呢?我们来看看 magento系统的目录结构。ps:magento是目前国外最流行的商城系统,没有之一。
-app
-code
-local
-product
-controllers
-model
-helper
-block
-etc
-sql
-catalog
-controllers
-model
-helper
-block
-etc
-sql
-core
-product
-controllers
-model
-helper
-block
-etc
-sql
-community
-flashsales
-controllers
-model
-helper
-block
-etc
-sql
大家可以看一看,这套目录结构是不是有很多相似的地方?对的,app/code目录其实就相当于laravel的app目录,这个目录是我们要编写的代码位置。我们可以看到这里有三个子文件夹,core,community,local。一套成熟的系统必定会有很多内置的模块和功能,例如:user,catalog,product,payment 等等。在magento中这些内置的模块和功能都写在core文件夹下。在core下是模块文件夹,模块文件夹下是基于mvc的方式,分布在controllers 和model 中。视图以同样的方式独立出去的,在这里我们不做赘述。helper就是存储一些定义不明确的类和方法,block是模块化集成快,在magento中很少有功能是独立使用的,都是在block中组装完成后,直接将block拼进视图中。sql是模块的升级脚本和安装脚本。好了,在上面的目录树中,我们可以看到core下有一个product模块,在local下有catalog和product两个模块。如果local下没有product模块,系统将调用core下的模块,如果local下复写了core下的product模块,那么core下的product模块将不再进行调用。这点就比较类似,类的继承。
这种设计模式的好处就是:在软件开发中,我们软件的核心的程序高内聚,并且独立,这样有助于我们系统的升级和管理。用户在二次开发的时候,不会对系统核心文件修改造成代码错乱。
1.3 工厂模式
刚才我们说道,软件开发要讲究:高内聚。那么现在我们来说一说另一个准则:松耦合。
高内聚,顾名思义将模块内部的元素高内聚在一起,刚好完成一件事。松耦合:是指将各个模块之间的联系降低,独立性强。那么工厂模式就是松耦合的最佳表现
我们来试验一下: 编写程序实现数据的加减乘除。
通常情况下,我们会写一个类来实现:代码如下:
class Calculate{
/**
* 计算结果
*
* @param int|float $num1
* @param int|float $num2
* @param string $operator
* @return int|float
*/
public function cal($num1,$num2,$operator){
try {
$result=0;
switch ($operator){
case '+':
$result= $num1+$num2;
break;
case '-':
$result= $num1-$num2;
break;
case '*':
$result= $num1*$num2;
break;
case '/':
if ($num2==0) {
throw new Exception("除数不能为0");
}
$result= $num1/$num2;
break;
}
return $result;
}catch (Exception $e){
echo "您输入有误:".$e->getMessage();
}
}
}
$test=new Calculate();
echo $test->cal(2,3,'+');//打印:5
echo $test->cal(5,0,'/');//打印:您输入有误:除数不能为0
?>
我们编写了一个caculate类,为这个类写一个caculate计算方法, 然后分别执行了加法和除法运算。程序运行正常。
现在我们再看一个例子:
简单工厂模式的初步实现
/**
* 操作类
* 因为包含有抽象方法,所以类必须声明为抽象类
*/
abstract class Operation{
//抽象方法不能包含函数体
abstract public function getValue($num1,$num2);//强烈要求子类必须实现该功能函数
}
/**
* 加法类
*/
class OperationAdd extends Operation {
public function getValue($num1,$num2){
return $num1+$num2;
}
}
/**
* 减法类
*/
class OperationSub extends Operation {
public function getValue($num1,$num2){
return $num1-$num2;
}
}
/**
* 乘法类
*/
class OperationMul extends Operation {
public function getValue($num1,$num2){
return $num1*$num2;
}
}
/**
* 除法类
*/
class OperationDiv extends Operation {
public function getValue($num1,$num2){
try {
if ($num2==0){
throw new Exception("除数不能为0");
}else {
return $num1/$num2;
}
}catch (Exception $e){
echo "错误信息:".$e->getMessage();
}
}
}
?>
Operation 是一个abstract抽象类,所有继承该类的子类都必须实现getValue()方法的功能。
后面是加减乘除四个类,分别继承Operation类。
<?php
/**
* 工程类,主要用来创建对象
* 功能:根据输入的运算符号,工厂就能实例化出合适的对象
*
*/
class Factory{
public static function createObj($operate){
switch ($operate){
case '+':
return new OperationAdd();
break;
case '-':
return new OperationSub();
break;
case '*':
return new OperationSub();
break;
case '/':
return new OperationDiv();
break;
}
}
}
$test = Factory::createObj('/');
$result = $test->getValue(23,0);
echo $result;
?>
这个类,是用于根据$operate 来相应实例化我们所需的加减乘除类,然后实现运算。
写到这里,我们很明显的可以看出,这两种方法都可以完成加减乘除的操作。区别是,第一种方法在一个类里面就完成了。 第二种方法是将每个操作独立成类,通过factory类来实例化我们所需要的类。这就是工厂模式。
有的人,可能会觉得,既然我们一个类就能完成,何必要用这种方式,这么复杂。说的没错,如果我们在开发中真的只需要写加减乘除这么简单的操作的时候,我们当然没必要为难自己。但是现在互联网软件越来越庞大,功能越来越多。我们的操作绝对不仅仅是加减乘除这儿简单,可能涉及到大数据的操作,复杂的业务逻辑等。刚才我们说过开发软件需要,高内聚,松耦合。在合适的情况下,我们把每一个独立的功能模块独立出去,通过工厂模式来组装我们需要的功能,这样既有利于每个独立模块的维护,同样更有利于增加模块而不需要冒着比较大的风险去修改代码。
所以我们如果要给上面代码增加个求余的功能,那么只需要增加一个OperationRem类,在factory里面增加一个case即可:
/**
* 求余类(remainder)
*
*/
class OperationRem extends Operation {
public function getValue($num1,$num2){
return $num1%$num2;
}
}
class Factory{
public static function createObj($operate){
switch ($operate){
case '+':
return new OperationAdd();
break;
case '-':
return new OperationSub();
break;
case '*':
return new OperationSub();
break;
case '/':
return new OperationDiv();
break;
case '%':
return new OperationRem();
break;
}
}
}
我们来调用一下:
$test = Factory::createObj('%');
echo $test->getValue(23,3);
结果输出 2
我们来总结一下, 工厂模式的编写规则:
简单工厂模式:
- 抽象基类:类中定义抽象一些方法,用以在子类中实现
- 模块子类:继承基类,实现基类中的抽象方法
- 工厂类:用以实例化对象
评论区