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下是系统的组件类,如果有与系统业务流程无关,但是在业务逻辑中需要的函数库,可以将它们作为组件放到这个目录下,如果配置文件内注册了该组件,在系统运行时会产生它们的实例以供调用。执行的流程大致如下图所示:

PHP框架的三层架构设计真的合适么?

  根据上图可以看出,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框架的三层架构设计真的合适么?-创新互联
转载源于:http://ybzwz.com/article/dgpheg.html