對(duì)于基本的Web開發(fā),我們已經(jīng)習(xí)慣了MVC架構(gòu)。模型層(M)提供持久化數(shù)據(jù)對(duì)象與數(shù)據(jù)訪問,控制層(C)完成業(yè)務(wù)邏輯處理,視圖層(V)提供模板表現(xiàn)。其中控制層與模型層和視圖層交互形成整個(gè)系統(tǒng)。
創(chuàng)新互聯(lián)公司成立與2013年,我們提供高端網(wǎng)站建設(shè)、小程序開發(fā)、電商視覺設(shè)計(jì)、手機(jī)APP定制開發(fā)及網(wǎng)絡(luò)營銷搜索優(yōu)化服務(wù),在傳統(tǒng)互聯(lián)網(wǎng)與移動(dòng)互聯(lián)網(wǎng)發(fā)展的背景下,我們堅(jiān)守著用標(biāo)準(zhǔn)的設(shè)計(jì)方案與技術(shù)開發(fā)實(shí)力作基礎(chǔ),以企業(yè)及品牌的互聯(lián)網(wǎng)商業(yè)目標(biāo)為核心,為客戶打造具商業(yè)價(jià)值與用戶體驗(yàn)的互聯(lián)網(wǎng)+產(chǎn)品。這種分層方式在邏輯上實(shí)現(xiàn)了解耦與分離,很多語言如Java和Python的框架都有各自的實(shí)現(xiàn)方式,如Struts采用Bean+JSP+Hibernate的方式實(shí)現(xiàn)。Django采用中間件的方式實(shí)現(xiàn)。無論是JAVA框架的實(shí)現(xiàn)方式還是Python框架的實(shí)現(xiàn)方式,都比較好的解決了MVC之間的交互問題,使得每個(gè)層次成為單獨(dú)的構(gòu)件,每一層的構(gòu)件可復(fù)用在其它地方。如Java Bean除了可供JSP使用,還可包裝成Soap服務(wù)。這樣既保證了層之間的邏輯分離,也保證了層之間的物理分離。如Java Bean可以單獨(dú)被部署到分布式應(yīng)用服務(wù)器上。EJB就是一個(gè)很好的例子。而對(duì)于目前廣泛應(yīng)用的一些PHP框架,在實(shí)現(xiàn)上感覺缺少對(duì)物理獨(dú)立性的考慮。
以Yii框架為例子來說,Yii框架的組織結(jié)構(gòu)大致如下所示:
Site
|____resources
|____protected
|____config
|____controllers
|____models
|____views
|____components
|____framework
|____index.php
resources代表站點(diǎn)的一些資源性資料,如圖片、樣式表等。freamework為框架核心檔。index.php為入口文件,所有訪問路徑均以index.php后跟參數(shù)為標(biāo)準(zhǔn)。而protected內(nèi)就是業(yè)務(wù)邏輯需要的MVC三層及系統(tǒng)需要的一些配置信息了。protected內(nèi)的componets下是系統(tǒng)的組件類,如果有與系統(tǒng)業(yè)務(wù)流程無關(guān),但是在業(yè)務(wù)邏輯中需要的函數(shù)庫,可以將它們作為組件放到這個(gè)目錄下,如果配置文件內(nèi)注冊(cè)了該組件,在系統(tǒng)運(yùn)行時(shí)會(huì)產(chǎn)生它們的實(shí)例以供調(diào)用。執(zhí)行的流程大致如下圖所示:
根據(jù)上圖可以看出,Action需要同時(shí)與Model層與View層進(jìn)行交互,在Action內(nèi),不僅有業(yè)務(wù)邏輯,還需調(diào)用Model層的方法和View層的方法。這樣,業(yè)務(wù)邏輯與Model層、View層就存在了緊耦合的關(guān)系。這種層次結(jié)構(gòu)應(yīng)用在基本的網(wǎng)站上還是可以滿足要求,但是不具備擴(kuò)展性和可修改性。如果現(xiàn)在想把業(yè)務(wù)邏輯包裝稱REST風(fēng)格或SOAP服務(wù),那么只能重新寫一次包含業(yè)務(wù)邏輯的Action,因?yàn)楝F(xiàn)在的Action里已經(jīng)包含了頁面的輸出方法,而不是單純的數(shù)據(jù)結(jié)果。對(duì)于需要使用同一動(dòng)態(tài)數(shù)據(jù)展示不同表現(xiàn)形式的需求,也只能通過判斷(修改)的方式而不能通過增加類或簡(jiǎn)單Action(擴(kuò)展)的方式實(shí)現(xiàn)。這違反了面向?qū)ο蟮拈_放封閉原則。由于系統(tǒng)需求往往是頻繁的變更的,如果我們常常的修改現(xiàn)有的代碼,不僅會(huì)造成現(xiàn)有系統(tǒng)代碼結(jié)構(gòu)的混亂,而且極易造成隱藏的,難以發(fā)現(xiàn)的錯(cuò)誤。所以,只有通過擴(kuò)展來實(shí)現(xiàn)需求的變更,對(duì)系統(tǒng)來說才是最安全的。我們的工作才會(huì)更加的輕松和高效。
既然Yii提供的源碼不能完美的解決擴(kuò)展性和可修改性的問題,那么我們何不對(duì)它進(jìn)行一次小小的改造,使它能勝任更加高的要求呢。俗話說的好,在軟件的世界里,加一層能解決所有的問題。所以,我們也決定使用“加一層”的辦法,以提高我們系統(tǒng)的可擴(kuò)展性與可修改性。由于Yii是通過URI路由來查找Action并進(jìn)行相應(yīng)操作,最后也是通過Action方法產(chǎn)生響應(yīng)結(jié)果的。所以,為了不破壞Yii的框架結(jié)構(gòu),我們只能在Action的下方增加一層,作為專門的業(yè)務(wù)邏輯層。為了偷懶,我們借鑒Java的叫法,稱它為bean層。此時(shí),Action就最為一個(gè)專門的包裝層,包裝bean的邏輯,然后與View層交互產(chǎn)生頁面,或直接輸出數(shù)據(jù)。這樣如果我想把業(yè)務(wù)流程包裝成SOAP服務(wù),只需增加一個(gè)Action即可,無需重寫業(yè)務(wù)邏輯。如果有多種視圖顯示要求,也無須重寫業(yè)務(wù)邏輯,只需擴(kuò)展Action即可。下面用代碼來說明“這一層”是如何加上去的。
首先我們?cè)趐rotected目錄下新建一個(gè)beans目錄,然后在components內(nèi)新建LoadBean.php文件,用于實(shí)例化bean類及獲取bean對(duì)象。beans下的文件以XXXBean.php命名,如SiteBean.php。在beans下新建一個(gè)configure.php文件。此文件是bean加載的配置文件,以實(shí)現(xiàn)IOC之用。新結(jié)構(gòu)如下所示:
Site
|____resources
|____protected
|____config
|____controllers
|____models
|____views
|____beans
|____configure.php
|____SiteBean.php
|____components
|____LoadBean.php
|____framework
|____index.php
beans下是專門負(fù)責(zé)業(yè)務(wù)邏輯的地方,Action內(nèi)只需調(diào)用beans下的業(yè)務(wù)邏輯并提供輸出即可,無需再寫業(yè)務(wù)邏輯代碼。為了達(dá)到以上要求,我們需要對(duì)Yii的代碼做少許修改。首先找到框架核心內(nèi)的CWebApplication.php文件,它的位置是framework/web/CWebApplication.php。 在該文件內(nèi)添加一個(gè)getBeanPath與一個(gè)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); } } }然后找到該文件內(nèi)的runController方法,在runController方法的開頭處調(diào)用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', ),讓系統(tǒng)在初始化時(shí)加載第三步添加的LoadBean類。
第五步,找到protected/beans/SiteBean.php,添加以下代碼:
class SiteBean extends Controller{ public function abc(){ return123; } }此abc方法即是我們的業(yè)務(wù)邏輯代碼。
第六步,找到protected/beans/configure.php,添加以下代碼:
return array( 'Site', );此'Site'即為SiteBean類的'Site'名。
第七步,實(shí)現(xiàn)Action方法,找到protected/controllers/SiteController.php文件(如沒有,可直接創(chuàng)建),代碼如下:
class SiteController extends Controller{ public function actionIndex(){ print_r(Yii::app()->beans->obj('Site')->abc()); } }Action類不直接與Bean類交互,而是通過組件類做代理進(jìn)行通訊,使Action與Bean也實(shí)現(xiàn)了分離。
此時(shí),利用瀏覽器訪問http://yourdomain/index.php?r=Site/index,即可顯示123。在此,我們實(shí)現(xiàn)了業(yè)務(wù)邏輯與Action的分離,增加了系統(tǒng)的擴(kuò)展性與可修改性,bean實(shí)現(xiàn)了物理部署獨(dú)立。形成了我們的四層架構(gòu)。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。