您好,登錄后才能下訂單哦!
對于基本的Web開發,我們已經習慣了MVC架構。模型層(M)提供持久化數據對象與數據訪問,控制層(C)完成業務邏輯處理,視圖層(V)提供模板表現。其中控制層與模型層和視圖層交互形成整個系統。
這種分層方式在邏輯上實現了解耦與分離,很多語言如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實現了物理部署獨立。形成了我們的四層架構。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。