PHP框架的三层架构设计真的合适么?-创新互联
对于基本的Web开发,我们已经习惯了MVC架构。模型层(M)提供持久化数据对象与数据访问,控制层(C)完成业务逻辑处理,视图层(V)提供模板表现。其中控制层与模型层和视图层交互形成整个系统。
创新互联公司成立与2013年,我们提供高端网站建设、小程序开发、电商视觉设计、手机APP定制开发及网络营销搜索优化服务,在传统互联网与移动互联网发展的背景下,我们坚守着用标准的设计方案与技术开发实力作基础,以企业及品牌的互联网商业目标为核心,为客户打造具商业价值与用户体验的互联网+产品。这种分层方式在逻辑上实现了解耦与分离,很多语言如Java和Python的框架都有各自的实现方式,如Struts采用Bean+JSP+Hibernate的方式实现。Django采用中间件的方式实现。无论是JAVA框架的实现方式还是Python框架的实现方式,都比较好的解决了MVC之间的交互问题,使得每个层次成为单独的构件,每一层的构件可复用在其它地方。如Java Bean除了可供JSP使用,还可包装成Soap服务。这样既保证了层之间的逻辑分离,也保证了层之间的物理分离。如Java Bean可以单独被部署到分布式应用服务器上。EJB就是一个很好的例子。而对于目前广泛应用的一些PHP框架,在实现上感觉缺少对物理独立性的考虑。
以Yii框架为例子来说,Yii框架的组织结构大致如下所示:
Site
|____resources
|____protected
|____config
|____controllers
|____models
|____views
|____components
|____framework
|____index.php
resources代表站点的一些资源性资料,如图片、样式表等。freamework为框架核心档。index.php为入口文件,所有访问路径均以index.php后跟参数为标准。而protected内就是业务逻辑需要的MVC三层及系统需要的一些配置信息了。protected内的componets下是系统的组件类,如果有与系统业务流程无关,但是在业务逻辑中需要的函数库,可以将它们作为组件放到这个目录下,如果配置文件内注册了该组件,在系统运行时会产生它们的实例以供调用。执行的流程大致如下图所示:
根据上图可以看出,Action需要同时与Model层与View层进行交互,在Action内,不仅有业务逻辑,还需调用Model层的方法和View层的方法。这样,业务逻辑与Model层、View层就存在了紧耦合的关系。这种层次结构应用在基本的网站上还是可以满足要求,但是不具备扩展性和可修改性。如果现在想把业务逻辑包装称REST风格或SOAP服务,那么只能重新写一次包含业务逻辑的Action,因为现在的Action里已经包含了页面的输出方法,而不是单纯的数据结果。对于需要使用同一动态数据展示不同表现形式的需求,也只能通过判断(修改)的方式而不能通过增加类或简单Action(扩展)的方式实现。这违反了面向对象的开放封闭原则。由于系统需求往往是频繁的变更的,如果我们常常的修改现有的代码,不仅会造成现有系统代码结构的混乱,而且极易造成隐藏的,难以发现的错误。所以,只有通过扩展来实现需求的变更,对系统来说才是最安全的。我们的工作才会更加的轻松和高效。
既然Yii提供的源码不能完美的解决扩展性和可修改性的问题,那么我们何不对它进行一次小小的改造,使它能胜任更加高的要求呢。俗话说的好,在软件的世界里,加一层能解决所有的问题。所以,我们也决定使用“加一层”的办法,以提高我们系统的可扩展性与可修改性。由于Yii是通过URI路由来查找Action并进行相应操作,最后也是通过Action方法产生响应结果的。所以,为了不破坏Yii的框架结构,我们只能在Action的下方增加一层,作为专门的业务逻辑层。为了偷懒,我们借鉴Java的叫法,称它为bean层。此时,Action就最为一个专门的包装层,包装bean的逻辑,然后与View层交互产生页面,或直接输出数据。这样如果我想把业务流程包装成SOAP服务,只需增加一个Action即可,无需重写业务逻辑。如果有多种视图显示要求,也无须重写业务逻辑,只需扩展Action即可。下面用代码来说明“这一层”是如何加上去的。
首先我们在protected目录下新建一个beans目录,然后在components内新建LoadBean.php文件,用于实例化bean类及获取bean对象。beans下的文件以XXXBean.php命名,如SiteBean.php。在beans下新建一个configure.php文件。此文件是bean加载的配置文件,以实现IOC之用。新结构如下所示:
Site
|____resources
|____protected
|____config
|____controllers
|____models
|____views
|____beans
|____configure.php
|____SiteBean.php
|____components
|____LoadBean.php
|____framework
|____index.php
beans下是专门负责业务逻辑的地方,Action内只需调用beans下的业务逻辑并提供输出即可,无需再写业务逻辑代码。为了达到以上要求,我们需要对Yii的代码做少许修改。首先找到框架核心内的CWebApplication.php文件,它的位置是framework/web/CWebApplication.php。 在该文件内添加一个getBeanPath与一个reloadBeans方法。代码如下:
public function getBeanPath(){ return $this->_controllerPath=$this->getBasePath().DIRECTORY_SEPARATOR.'beans'; } public function reloadBeans(){ $beanPath = $this->getBeanPath(); if(is_dir($beanPath)){ $current_dir =opendir($beanPath); while(($file = readdir($current_dir))!==false){ if($file=='.' OR $file=='..') continue; require($beanPath.DIRECTORY_SEPARATOR.$file); } } }然后找到该文件内的runController方法,在runController方法的开头处调用reloadBeans方法:
public function runController($route) { $this->reloadBeans(); if(($ca=$this->createController($route))!==null) { list($controller,$actionID)=$ca; $oldController=$this->_controller; $this->_controller=$controller; $controller->init(); $controller->run($actionID); $this->_controller=$oldController; } else throw new CHttpException(404,Yii::t('yii','Unable to resolve the request "{route}".', array('{route}'=>$route===''?$this->defaultController:$route))); }第三步,打开刚才新建的LoadBean.php文件,添加以下代码:
class LoadBean { private $objs; public function init(){ $beanconfig = Yii::app()->basePath.'\beans\configure.php'; $beans = require $beanconfig; foreach($beans as $bean){ if (!$this->objs[$bean] instanceof $bean.'Bean'){ $class = $bean.'Bean'; if(class_exists($class)){ $this->objs[$bean] = new $class($bean); } } } } public function obj($name){ try{ if (array_key_exists($name,$this->objs)){ return $this->objs[$name]; }else{ throw new Exception('bean name error'); } }catch(Exception $e){ echo $e->getMessage(); } } }第四步,找到protected/config/main.php文件,添加以下代码:
'beans'=>array( 'class'=>'LoadBean', ),让系统在初始化时加载第三步添加的LoadBean类。
第五步,找到protected/beans/SiteBean.php,添加以下代码:
class SiteBean extends Controller{ public function abc(){ return123; } }此abc方法即是我们的业务逻辑代码。
第六步,找到protected/beans/configure.php,添加以下代码:
return array( 'Site', );此'Site'即为SiteBean类的'Site'名。
第七步,实现Action方法,找到protected/controllers/SiteController.php文件(如没有,可直接创建),代码如下:
class SiteController extends Controller{ public function actionIndex(){ print_r(Yii::app()->beans->obj('Site')->abc()); } }Action类不直接与Bean类交互,而是通过组件类做代理进行通讯,使Action与Bean也实现了分离。
此时,利用浏览器访问http://yourdomain/index.php?r=Site/index,即可显示123。在此,我们实现了业务逻辑与Action的分离,增加了系统的扩展性与可修改性,bean实现了物理部署独立。形成了我们的四层架构。
另外有需要云服务器可以了解下创新互联cdcxhl.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。
当前文章:PHP框架的三层架构设计真的合适么?-创新互联
分享URL:http://scjbc.cn/article/dgpheg.html