我們一直強(qiáng)調(diào)推薦讓后臺(tái)接口開發(fā)更簡(jiǎn)單。
所以,我們提供了PhalApi開發(fā)框架和WIKI文檔。然后,這僅僅是個(gè)開始。
因?yàn)椋嬲l(fā)揮作用,實(shí)現(xiàn)價(jià)值的還是來(lái)自項(xiàng)目實(shí)際開發(fā)中的源代碼。
但縱使我們提供了好的框架作為基礎(chǔ),但不得不承認(rèn)的一個(gè)事實(shí)是:架構(gòu)模型和項(xiàng)目代碼隨著各自的演進(jìn)逐漸產(chǎn)生分歧。而這種分歧,如果沒有注意、管理和約束,則會(huì)產(chǎn)生越來(lái)越混亂的代碼。
在說(shuō)明如何編寫簡(jiǎn)潔的項(xiàng)目代碼之前,讓我們先了解一下PhalApi的架構(gòu)思想以及主要的設(shè)計(jì)意圖。
開發(fā)-配置-使用 模式即:開發(fā)實(shí)現(xiàn)-配置注冊(cè)-客戶使用 模式。
現(xiàn)分說(shuō)如下。
開發(fā)實(shí)現(xiàn)的主要內(nèi)容是組件、公共服務(wù)或者基礎(chǔ)設(shè)施的功能實(shí)現(xiàn),此部分主要針對(duì)高級(jí)開發(fā)工程師,或者有經(jīng)驗(yàn)的PHP同學(xué)。
例如對(duì)項(xiàng)目的接口簽名的驗(yàn)證攔截、一個(gè)完成了對(duì)七牛云存儲(chǔ)接口調(diào)用的擴(kuò)展、又或者是項(xiàng)目?jī)?nèi)部加密的方案等,這些以包或者接口提供,為外部使用提供了配置說(shuō)明、使用示例和文檔說(shuō)明。更為重要的是,應(yīng)該提供了配套的單元測(cè)試,并有著很高的代碼覆蓋率。
此類實(shí)現(xiàn)應(yīng)該是穩(wěn)定的,即沒有明顯或者隱藏的BUG。即使有,原作者也可以快速進(jìn)行定位和解決,以及后期的擴(kuò)展和升級(jí)。
一旦上面的接口被實(shí)現(xiàn)后,不同的項(xiàng)目都可以輕松引入和使用。這塊通常由項(xiàng)目的負(fù)責(zé)人,或者主程來(lái)操作,因?yàn)樵趯?duì)項(xiàng)目進(jìn)行構(gòu)建部署、組件和環(huán)境裝配時(shí),需要考慮到哪些組件需要被用到,以何種方式進(jìn)行初始化和裝載。
但使用的方式,應(yīng)該是簡(jiǎn)明的。如簡(jiǎn)明的安裝,簡(jiǎn)明的配置。所以,這里自然而言,就涉及到了 依賴注入 ,DI。
通過(guò)DI,項(xiàng)目的負(fù)責(zé)人,可以輕松地將已通過(guò)嚴(yán)格測(cè)試的組件/服務(wù)注冊(cè)進(jìn)來(lái)。完成此步驟后,一切都整裝待發(fā),剩下的就是使用的問(wèn)題了。
項(xiàng)目會(huì)不斷有新的需求出來(lái),而團(tuán)隊(duì)也會(huì)因此同步增加吸納新開發(fā)同學(xué)進(jìn)來(lái)負(fù)責(zé)新模塊新功能的開發(fā)。而新的同學(xué),往往會(huì)是一些開發(fā)新手,他們需要使用已有的功能,快速實(shí)現(xiàn)一些具體的業(yè)務(wù)邏輯、規(guī)則和功能。
但如果他們還需要實(shí)現(xiàn)一些基礎(chǔ)重要的功能,又要考慮如何與現(xiàn)在項(xiàng)目整合,會(huì)分散他們的關(guān)注點(diǎn)。而且,即使放手給他們?nèi)プ?,他們也?huì)常常因?yàn)榭紤]不周或者編程風(fēng)格各異而產(chǎn)出一些與項(xiàng)目期望不符的代碼。
若換一種工作的方式,即如果新手使用已有的組件進(jìn)行一些特定領(lǐng)域業(yè)務(wù)的開發(fā),會(huì)是怎樣?
我想,會(huì)有很大的改觀。
比如,我們對(duì)新來(lái)的同學(xué)說(shuō),你使用DI()->logger就可以寫一條日志了,如:
DI()->logger->debug('app enter');
新手可能很喜歡追問(wèn)一些問(wèn)題,他可能會(huì)問(wèn)及到,那怎么將一些參數(shù)(當(dāng)時(shí)日志的上下文)也進(jìn)行紀(jì)錄呢?你可以很驕傲地說(shuō):也是可以的,你只需要這樣寫就可以了:
DI()->logger->debug('app enter', array('device' => 'iOS', 'version' => '1.1.0'));
開發(fā)-配置-使用 模式 也符合了創(chuàng)建和使用分離的思想。
不同的項(xiàng)目,不同的應(yīng)用,需要的初始化服務(wù)不一樣;不同的規(guī)模,對(duì)不同的技術(shù)解決方案也不一樣;不同的環(huán)境,配置也不一樣。
但即使是這樣,新手還是可以一如既往地使用之前注冊(cè)的服務(wù)(也就是不需要修改任何調(diào)用代碼)。也就是上層的調(diào)整或者環(huán)境變更這些,對(duì)新手的使用都是透明的。為了更好地理解這些概念,這里補(bǔ)充一些案例場(chǎng)景。
假設(shè)我們有個(gè)項(xiàng)目A,分別部署到內(nèi)網(wǎng)測(cè)試環(huán)境和外網(wǎng)生產(chǎn)環(huán)境,顯然內(nèi)外網(wǎng)環(huán)境的配置是不一樣的。我們希望在內(nèi)網(wǎng)環(huán)境為日志開啟debug模式以方便開發(fā)人員進(jìn)行調(diào)試,在外網(wǎng)則希望將其關(guān)閉以減少系統(tǒng)的性能開銷。在一開始使用文件作為日志存儲(chǔ)方案時(shí),對(duì)應(yīng)的內(nèi)網(wǎng)環(huán)境初始化代碼如下:
//日志紀(jì)錄
DI()->logger = new PhalApi_Logger_File(API_ROOT . '/Runtime',
PhalApi_Logger::LOG_LEVEL_DEBUG | PhalApi_Logger::LOG_LEVEL_INFO | PhalApi_Logger::LOG_LEVEL_ERROR);
在外網(wǎng),我們只需要去掉PhalApi_Logger::LOG_LEVEL_DEBUG即可:
//日志紀(jì)錄
DI()->logger = new PhalApi_Logger_File(API_ROOT . '/Runtime',
PhalApi_Logger::LOG_LEVEL_INFO | PhalApi_Logger::LOG_LEVEL_ERROR);
隨著項(xiàng)目的不斷發(fā)展,我們有了一批又一批的新用戶。產(chǎn)品經(jīng)理為此很開心,也請(qǐng)我們開發(fā)吃了好幾頓大餐。但謹(jǐn)慎的我們發(fā)現(xiàn)了現(xiàn)在文件日志的一些限制。如即時(shí)文件讀寫帶來(lái)了I/O瓶頸,而且不能將分布式的日志文件自動(dòng)收集起來(lái)。所以,我們決定對(duì)logger進(jìn)行更深層次的探索。。。
至于最后是使用了Hive還是Hadoop,還是MC異步后臺(tái)隊(duì)列的方式實(shí)現(xiàn),我們這里不具體指定。假設(shè)新的logger研發(fā)成功后,我們便可以輕松對(duì)原有的文件日志組件進(jìn)行升級(jí),實(shí)現(xiàn)完美切換:
//升級(jí)后的日志紀(jì)錄
DI()->logger = new My_Logger(PhalApi_Logger::LOG_LEVEL_INFO | PhalApi_Logger::LOG_LEVEL_ERROR);
這不僅是幾行代碼上的區(qū)別,而是針對(duì)不同問(wèn)題不同技術(shù)解決方案的抉擇。這也是有經(jīng)驗(yàn)的開發(fā)和新手之間的區(qū)別,因?yàn)槟氵x擇的技術(shù)解決方案要和面臨的風(fēng)險(xiǎn)相匹配。例如用牛刀來(lái)殺雞,就是一個(gè)不匹配的做法,就如同使用高級(jí)的Hive來(lái)實(shí)現(xiàn)單一小項(xiàng)目的日志存儲(chǔ)一樣。
這是令人值得興奮的。在很多舊的項(xiàng)目里面,當(dāng)遇到瓶頸時(shí),會(huì)請(qǐng)一些外部的專家來(lái)指導(dǎo)或優(yōu)化。但即使擁有著各種“法寶”以及知道何時(shí)該使用哪種方案的專家,對(duì)于這種殘留的代碼也會(huì)步履維艱基于束手無(wú)策。因?yàn)?,各種初始化和調(diào)用的代碼,分遍在項(xiàng)目的“全國(guó)各地,四面八方”。即使你優(yōu)化了,你會(huì)發(fā)現(xiàn)還要手動(dòng)一個(gè)個(gè)地進(jìn)行切換升級(jí)。更重要的是,很多時(shí)候不是你想優(yōu)化就能優(yōu)化的。
我曾經(jīng)遇到過(guò)這樣一個(gè)舊系統(tǒng)。它是在UcHome基礎(chǔ)上做二次開發(fā),但對(duì)于它的數(shù)據(jù)庫(kù)使用,開發(fā)人員沒有過(guò)多地優(yōu)化,如:沒有使用緩存,沒有進(jìn)行批量合并查詢優(yōu)化,重復(fù)查詢相同的數(shù)據(jù),沒有建立索引等等,等等。這樣的后果就是,請(qǐng)求一次接口,會(huì)觸發(fā)150條到500條SQL語(yǔ)句不等。我后來(lái),在底層添加了在線查看調(diào)試SQL語(yǔ)句的功能,嘗試進(jìn)行了一些合并查詢,但當(dāng)我想為數(shù)據(jù)庫(kù)的表添加索引時(shí),發(fā)現(xiàn)它用的卻是虛擬表 -- 視圖!
如果說(shuō)DI服務(wù)是微觀上對(duì)“開發(fā)-配置-使用”模式的使用,那么PhalApi的擴(kuò)展類庫(kù)則是宏觀上的應(yīng)用。
擴(kuò)展類庫(kù)也是由第三方(可能是PhalApi開發(fā)團(tuán)隊(duì)、他人或者你)開發(fā)實(shí)現(xiàn)的,然后再通過(guò)簡(jiǎn)單配置(或者免配置),就可以使用擴(kuò)展類庫(kù)的功能了。如郵件發(fā)送、phprpc協(xié)議。
之所以提供擴(kuò)展類庫(kù)的形式,是因?yàn)镈I服務(wù)更適合于單個(gè)類以及幾個(gè)操作接口,而擴(kuò)展類庫(kù)則提供更豐富的功能操作和一系列的接口。
這樣以后,項(xiàng)目就可以簡(jiǎn)單快速共享各種擴(kuò)展類庫(kù)了。難道這不是一件令人興奮的事情嗎?
因?yàn)椤肮?!我又找到了一個(gè)可以直接用的代碼類庫(kù)”,而不是“唉,又要寫一堆代碼,還要測(cè)試、聯(lián)調(diào)。。?!?。
程序、系統(tǒng)和框架,其作用太多數(shù)都體現(xiàn)在動(dòng)態(tài)的功能上,而不是靜態(tài)有限的功能。而動(dòng)態(tài)的功能則很大程序上依賴于各種配置,如Tomcat下各層級(jí)xml配置。有些框架對(duì)配置這塊提供了豐富的支持,但為此的代碼是,配置難以掌控。
就拿Yii框架為例(Yii確實(shí)是一個(gè)很了不起的框架,這里只是以事論事),當(dāng)你需要在視圖渲染一個(gè)數(shù)據(jù)表格時(shí),你可以使用 CGridView ,并類似這樣配置:
$columns = array(
array('name' => 'mId', 'header' => '序號(hào)'),
array('name'=>'id', 'header'=>'事件ID'),
array('name'=>'title', 'header'=>'標(biāo)題'),
array('name'=>'content', 'header'=>'內(nèi)容', 'type' => 'html'),
);
$this->widget('bootstrap.widgets.TbGridView', array(
'type'=>'striped bordered condensed',
'dataProvider'=>$dataProvider,
'columns'=> $columns,
));
更為復(fù)雜的情況可以是:
$columns = array(
// ...
array('class' => 'CDataColumn', 'header' => '內(nèi)容', 'type' => 'html', 'name' => 'content', 'htmlOptions' => array('width' => '200px')),
array(
'class'=>'CButtonColumn',
'template'=>'{showEvent}<br/><br/>{deleteEvent}',
'header'=>'操作',
'buttons'=>array
(
'showEvent' => array(
'label' => '查看',
'url' => '"?r=DailyOperations/eventManagerShow&user_iduser_id=' . $userId . '&eventId=". $data["id"];',
'options' => array('target' => '_blank'),
),
'deleteEvent' => array(
'label'=>'刪除',
'url'=>'"javascript:void(0)"',
'imageUrl'=>'/images/delete_24.png',
'deleteConfirmation'=>"js:'Record with ID '+$(this).parent().parent().children(':first-child').text()+' will be deleted! Continue?'",
'click'=>'js:function(){if (confirm("此操作將刪除:ID = " + $(this).parent().parent().children(\':first-child\').text() + " \n是否確定?")) {deleteEvent($(
this).parent().parent().children(\':first-child\').text());};}',
),
),
),
);
// ...
然后,對(duì)于我這么笨的人來(lái)說(shuō),不管是簡(jiǎn)單的配置,還是復(fù)雜的配置,每次當(dāng)我需要使用時(shí),我都非常害怕且需要從以下三方便獲取幫助:
因?yàn)?,每次我都記不住這些配置,但我又不得不承認(rèn)它的效果很好。然后我覺得其缺點(diǎn)至少有兩點(diǎn):
這是我對(duì)Yii框架配置的體會(huì)。
最初,感受到配置式的開發(fā),是在大學(xué)的時(shí)候做一個(gè)OutLook的插件。這個(gè)插件需要同步本地和遠(yuǎn)程服務(wù)器的聯(lián)系人,其中當(dāng)有沖突時(shí),就有這么幾種策略:沖突時(shí)以本地為準(zhǔn)、沖突時(shí)以遠(yuǎn)程為準(zhǔn)、沖突時(shí)提醒我、忽略沖突。
這是當(dāng)時(shí)寫的博客,感興趣可以看看 配置編程: 讓項(xiàng)目開發(fā)從多樣到統(tǒng)一
這有點(diǎn)像我們常用的SVN的處理方式。然而當(dāng)我在嘗試開發(fā)實(shí)現(xiàn)時(shí),我發(fā)現(xiàn)過(guò)程很復(fù)雜,但處理又是如此相似。這里的區(qū)別很微妙,特別這些策略又是由外部用戶指定時(shí)。最后,我驚訝地發(fā)現(xiàn),如果我使用配置來(lái)做的話,會(huì)非常簡(jiǎn)單且明了!
但那時(shí),只是初體會(huì)。
現(xiàn)在,經(jīng)過(guò)了幾年的開發(fā),我才慢慢發(fā)現(xiàn),可以把這種開發(fā)模式總結(jié)為:開發(fā)-配置-使用模式。
不知是否有其他模式和此新發(fā)現(xiàn)的模式類似?
框架外延是指跨項(xiàng)目,與業(yè)務(wù)無(wú)關(guān)的抽象特性與代碼實(shí)現(xiàn)。
這些從簡(jiǎn)單到復(fù)雜,有:通用功能,定制,擴(kuò)展類庫(kù)和產(chǎn)品簇框架。
有些功能是常用的,也是通用的。一般以函數(shù)或者工具類的形式提供,如一個(gè)生成隨機(jī)數(shù)的方法。這些通用的功能,開發(fā)人員都可以在項(xiàng)目間流通使用。
當(dāng)發(fā)現(xiàn)PhalApi現(xiàn)在的框架不支持某些接口下具體方案的實(shí)現(xiàn)時(shí),或者支持但不滿足當(dāng)前項(xiàng)目的開發(fā)需要時(shí),可以進(jìn)行一些定制化。
之前有一個(gè)項(xiàng)目的開發(fā)同學(xué),給我提供了一個(gè)很好的建議。他說(shuō)他的項(xiàng)目為了能夠獲得更高的安全性,對(duì)客戶端傳遞的參數(shù)進(jìn)行了整包加密,然后在服務(wù)商進(jìn)行整包解密再通過(guò)getRules()獲取。所以對(duì)應(yīng)的定制大概實(shí)現(xiàn)可以是這樣的:
<?php
class My_Request extends PhalApi_Request {
protected function genData($data){
$needData = array();
if (!isset($data) || !is_array($data)) {
$data = $_POST;
}
$needData['service'] = isset($data['service']) ? $data['service'] : '';
//整包加密后的數(shù)據(jù)包
$params = isset($data['params']) ? $data['params'] : '';
//TODO: 對(duì)稱解密 ...
$needData = array_merge($params, $needData);
return $needData;
}
}
有時(shí)候,我們?cè)陧?xiàng)目中需要使用到一組功能操作,這時(shí)可以結(jié)合外觀模式,將這些需要用到的接口以包的形式都封裝到一個(gè)擴(kuò)展里面。因?yàn)閿U(kuò)展這個(gè)概念是容易理解的,也是開發(fā)人員所喜愛的。
在PhalApi的Issues里面,有位同學(xué)就提到了對(duì)新浪或者百度云空間的支持,就是對(duì)應(yīng)的一個(gè)場(chǎng)景。
現(xiàn)在PhalApi提供了部分的擴(kuò)展類庫(kù),但由于個(gè)人時(shí)間有限,而且也不可能知悉全部項(xiàng)目需要用到的全部擴(kuò)展。所以當(dāng)發(fā)現(xiàn)需要的擴(kuò)展類庫(kù)還沒有提供時(shí),簡(jiǎn)單的方法:自動(dòng)寫一個(gè)。
這也是對(duì)框架進(jìn)行外延的一種途徑。伴隨著這種途徑經(jīng)歷的次數(shù)越多,你會(huì)突然有一天發(fā)現(xiàn)你所搭建的這一切結(jié)合起來(lái)后,已經(jīng)可以很好地應(yīng)對(duì)了公司內(nèi)的目前全部項(xiàng)目的開發(fā)。
這個(gè)時(shí)候,就來(lái)了產(chǎn)品簇框架這一階段。
PhalApi只是提供了一個(gè)基礎(chǔ)的框架,一個(gè)項(xiàng)目實(shí)際開發(fā)的基礎(chǔ)框架,更是一個(gè)產(chǎn)品簇框架的鋪墊。如果說(shuō)我們關(guān)注項(xiàng)目的快速交付,不如說(shuō)我們更關(guān)注 如何提供一個(gè)底層框架以支持不同項(xiàng)目的產(chǎn)品簇框架開發(fā),從而最終支持項(xiàng)目的快速交付。
也只有這樣,你辛苦付出的代碼,你才會(huì)更加珍惜和不斷為之冥想、維護(hù)和改進(jìn)。
也只有這樣,你公司里面的其他項(xiàng)目才會(huì)更愿意和信賴使用,因?yàn)榭蚣苁悄阒苯犹峁┑摹?br />也只有這樣,代碼才得以永恒,因?yàn)檫@種思想在你、我、各個(gè)項(xiàng)目團(tuán)隊(duì)間不斷傳遞,共存在每個(gè)開發(fā)人員的腦里和心中,而不是為某個(gè)自私的人把持著。
框架外延 是針對(duì)特定領(lǐng)域項(xiàng)目以外的問(wèn)題。然而,在使用PhalApi進(jìn)行項(xiàng)目開發(fā)時(shí),我們?nèi)绻魂P(guān)注這些是無(wú)用的,我們還要關(guān)注項(xiàng)目的實(shí)際開發(fā),如何編寫代碼,即:項(xiàng)目?jī)?nèi)涵。
項(xiàng)目?jī)?nèi)涵是指完成特定領(lǐng)域功能所需要的前置條件、基礎(chǔ)設(shè)施和工作流程。
這里面著重講解復(fù)雜的領(lǐng)域和更廣義的數(shù)據(jù)源這兩方面。不是說(shuō)其他的方面不重要,而是這兩方既重要但又常常為人們所誤解誤用。
在一個(gè)項(xiàng)目架構(gòu)里面,有三個(gè)主要模型:設(shè)計(jì)模型、領(lǐng)域模型和代碼模型。設(shè)計(jì)模型在選擇PhalApi時(shí)已大體確定,領(lǐng)域模式則需要項(xiàng)目干系人員消化、理解并表達(dá)出來(lái)。對(duì)于開發(fā)人員,代碼模型則是他們表達(dá)的媒介。
這一層,主要關(guān)注的是領(lǐng)域業(yè)務(wù)規(guī)則的處理。所以,我們拋開外界客戶端接口調(diào)用的簽名驗(yàn)證、參數(shù)獲取、安全性等問(wèn)題,也不考慮數(shù)據(jù)從何而來(lái)、存放于何處,而是著重關(guān)注對(duì)領(lǐng)域業(yè)務(wù)數(shù)據(jù)的處理上。
根據(jù)這么年來(lái)的工作、項(xiàng)目開發(fā)和學(xué)習(xí),這里有一些建議。
領(lǐng)域之所以復(fù)雜,在于規(guī)則眾多。如果不能很好地把控這些規(guī)則,當(dāng)規(guī)則發(fā)生變化時(shí),就會(huì)出現(xiàn)很大的問(wèn)題。在開發(fā)過(guò)程中,要注意對(duì)規(guī)則進(jìn)行提煉并且放置在一個(gè)指定的位置。如對(duì)游戲玩家的經(jīng)驗(yàn)計(jì)算等級(jí)時(shí),這樣一個(gè)規(guī)則就要統(tǒng)一好。不要到處都有類型相同的計(jì)算接口。
領(lǐng)域的邏輯是對(duì)現(xiàn)實(shí)業(yè)務(wù)場(chǎng)景的再解釋。現(xiàn)實(shí)的因素充滿變數(shù)并且由人為指定,所以不能簡(jiǎn)單的在計(jì)算機(jī)中“推導(dǎo)”出領(lǐng)域邏輯。在開發(fā)過(guò)程中,要特別對(duì)這些領(lǐng)域邏輯準(zhǔn)確并很好的解釋,以便后面接手的同學(xué)可以更容易理解和明白這些流程、限制和規(guī)則。
其中一個(gè)有力的指導(dǎo)就是釋意接口。對(duì)接口簽名甚至是對(duì)變量命名的仔細(xì)推敲都是很有益處的,因?yàn)槊帜苷?,不至于混淆或者含糊不清?/p>
領(lǐng)域?qū)雨P(guān)注的是流程、規(guī)則,所以當(dāng)你進(jìn)行用戶個(gè)性化分流和排序時(shí),不應(yīng)該把底層網(wǎng)絡(luò)接口請(qǐng)求的細(xì)節(jié)也放到這里流程里面。把底層技術(shù)實(shí)現(xiàn)的細(xì)節(jié)和業(yè)務(wù)規(guī)則的處理分開是很有好處的,這樣便于更清晰領(lǐng)域邏輯的表達(dá),也助于單元測(cè)試時(shí)的測(cè)試樁模擬。
領(lǐng)域?qū)庸倘恢匾绻麤]有數(shù)據(jù)源層,領(lǐng)域?qū)泳褪且粋€(gè)空中樓閣。
但不應(yīng)把數(shù)據(jù)源就理所當(dāng)然地對(duì)等成數(shù)據(jù)庫(kù)。因?yàn)檫@種觀念很常見但也很狹隘。首先,很多項(xiàng)目在對(duì)數(shù)據(jù)存儲(chǔ)時(shí),不一定會(huì)落地存儲(chǔ),即使落地也不一定使用數(shù)據(jù)庫(kù)。我曾經(jīng)在一家游戲公司任職時(shí),就看到他們使用了文件來(lái)存放。相信,你也看到過(guò)。其次,在現(xiàn)在多客戶端多系統(tǒng)的交互背景下,很多系統(tǒng)都需要進(jìn)行數(shù)據(jù)共享和通信,為了提高服務(wù)器的性能也會(huì)使用到緩存。這些場(chǎng)景下,會(huì)導(dǎo)致數(shù)據(jù)是通過(guò)接口來(lái)獲取,或者來(lái)源于緩存??梢钥闯?,如果把數(shù)據(jù)源就看作是MySql,是非常局限的。
我們?cè)赑halApi中繼續(xù)使用了Model層,因?yàn)槭躆VC模式的影響,大家都對(duì)Model層非常熟悉。但我們卻為它賦予了新的詮釋和活力。
Model導(dǎo)獲取的數(shù)據(jù),可以是來(lái)自數(shù)據(jù)庫(kù)的讀取,也可以是通過(guò)開放平臺(tái)接口獲取的數(shù)據(jù),也可以是不落地直接存放于緩存的數(shù)據(jù)。
框架外延和項(xiàng)目?jī)?nèi)涵,分別是對(duì) 開發(fā)-配置-使用 模式 前半部分和后半部分的詮釋。
剩下的問(wèn)題就在于,如何評(píng)判我們編寫的代碼是好的,是美的,并且能夠按期望地工作?
如果只是主觀地判斷,顯然是不可靠的也不是完全可信的。例如“我覺得我這樣沒問(wèn)題??!”,即使代碼能夠正常運(yùn)行,也不并代表在其他臨界或者極端的情況也能如期運(yùn)行,也不代表這些代碼就具備了一些好的品質(zhì),更不代表這些代碼遵循了我們的架構(gòu)明顯的編程風(fēng)格:編寫人容易理解的代碼。
既然主觀不可靠,就應(yīng)該轉(zhuǎn)到客觀上的標(biāo)準(zhǔn)。
靜態(tài)的代碼分析,是很有用的,但對(duì)于最終需要?jiǎng)討B(tài)執(zhí)行的代碼,還是需要單元測(cè)試才能更好的探知代碼內(nèi)部每個(gè)角落的狀況。
這里,我們強(qiáng)烈推薦測(cè)試先行,也就是測(cè)試驅(qū)動(dòng)開發(fā)??赡苣阋褜?duì)此耳熟能詳,也可能你略知一二。
但請(qǐng)相信,測(cè)試先行是值得的。關(guān)于真實(shí)的測(cè)試,即編寫PHPUnit單元測(cè)試,關(guān)于PHPUnit的使用,請(qǐng)查看: PHPUnit Manual – 第 1 章 自動(dòng)化測(cè)試 。
PhalApi是開放式的框架,這里不僅僅體現(xiàn)在源代碼開放,產(chǎn)品開放,還表現(xiàn)在思想開放。
這樣說(shuō),可能有些抽象。若落實(shí)到代碼層次,你會(huì)發(fā)現(xiàn)PhalApi提供了接口上的約束,并為每塊接口提供了很好的擴(kuò)展機(jī)制。也就是說(shuō),一旦你發(fā)現(xiàn)已提供的功能不滿足你項(xiàng)目的需要,你可以輕松定制、擴(kuò)展和升級(jí)。
這就是我們的設(shè)計(jì)意圖。
對(duì)于新手,你可以快速使用這個(gè)框架;對(duì)于老手,你則可以定制它。
開發(fā)-配置-使用 模式 并不是為了傳遞 “君君臣臣、父父子子”這樣的封建等級(jí)觀念。而是為了資源更好的調(diào)配,最終更好地完成項(xiàng)目的開發(fā)。
架構(gòu)系統(tǒng)有分層的思想,這里也一樣。通過(guò)高級(jí)開發(fā)、開發(fā)工程師和新手各盡其才、各施其職,能夠更好的提高各自的關(guān)注點(diǎn),并發(fā)揮他們應(yīng)有的能力和價(jià)值。
比如對(duì)于高級(jí)開發(fā),我們希望他們能夠抽離業(yè)務(wù),并且產(chǎn)出一些公司內(nèi)可以使用的通用組件、核心技術(shù)甚至是產(chǎn)品簇開發(fā)框架。而對(duì)于他們來(lái)說(shuō),也許這也正是他們所喜愛的,因?yàn)樗麄冊(cè)诠リP(guān)一些技術(shù)難點(diǎn),又或者在解決一些他們覺得有挑戰(zhàn)的工作。
對(duì)于項(xiàng)目負(fù)責(zé)人,也就是開發(fā)工程師,他們更關(guān)注的是整個(gè)項(xiàng)目的運(yùn)行和所能提供的功能。而這一些功能需要各業(yè)務(wù)規(guī)則在代碼上的體現(xiàn),最終又會(huì)直接或間接落到某些基礎(chǔ)設(shè)施的支持上,一如數(shù)據(jù)庫(kù)的查詢操作。顯然,他們不希望每次都為這樣通用的技術(shù)支持重復(fù)開發(fā)。如果有已經(jīng)能夠直接拿來(lái)使用的代碼,那該多好。網(wǎng)上雖然資源眾多,但符合公司項(xiàng)目使用的,少之甚少,又或許會(huì)有這樣那樣的限制。如果有公司內(nèi)部可重用的組件庫(kù),這種情況會(huì)大為改觀。
對(duì)于新手,我們要求明顯會(huì)低很多。簡(jiǎn)單來(lái)說(shuō),他們會(huì)使用就可以了。當(dāng)然,我們也推薦新手在熟練使用的情況下,再深化到底層,慢慢過(guò)渡到大工。
盡管 我們提供了這樣架構(gòu)明顯的編程風(fēng)格,但仍然需要你以及你的團(tuán)隊(duì)來(lái)遵循。
正如前面所說(shuō)的,項(xiàng)目的每一行代碼和每一個(gè)命名都來(lái)自你的思考和雙手的輸入。也正如此,你的付出讓你更深刻體會(huì)到編程的樂趣,特別是項(xiàng)目發(fā)揮和實(shí)現(xiàn)了有價(jià)值的業(yè)務(wù)功能時(shí)。這時(shí),你會(huì)發(fā)現(xiàn)維護(hù)別人的代碼不再是一件痛苦的事件,與團(tuán)隊(duì)的合作也變得更加融洽,因?yàn)楹湍阋黄饖^斗努力的是一支精英團(tuán)隊(duì)。
請(qǐng)記住,這只是一個(gè)開始,一個(gè)起點(diǎn)。
更多建議: