鍍金池/ 教程/ PHP/ 回顧博客應(yīng)用程序
了解 Router
回顧博客應(yīng)用程序
應(yīng)用 Form 和 Fieldset
編輯數(shù)據(jù)和刪除數(shù)據(jù)
介紹我們第一個(gè)“博客” Module
介紹 Zend\Db\Sql 和 Zend\Stdlib\Hydrator
介紹 Service 和 ServiceManager
為不同的數(shù)據(jù)庫(kù)后臺(tái)做準(zhǔn)備

回顧博客應(yīng)用程序

通過(guò)之前的七個(gè)章節(jié),我們已經(jīng)創(chuàng)建了一個(gè)以博客作為示例的完整功能的增刪改查應(yīng)用程序。在我們制作的過(guò)程中應(yīng)用了幾種不同的設(shè)計(jì)模式和最佳實(shí)踐?,F(xiàn)在是時(shí)候來(lái)重新數(shù)一數(shù)并且看看其中一些我們寫(xiě)過(guò)的代碼示例了。這將會(huì)以問(wèn)與答的形式呈現(xiàn)。

我們總是需要所有的層和接口嗎?

簡(jiǎn)單回答:不是。

詳細(xì)回答:接口的重要性會(huì)隨著你的應(yīng)用程序的增長(zhǎng)而增長(zhǎng)。如果你可以預(yù)見(jiàn)到你的應(yīng)用程序會(huì)被其他人使用并且是可擴(kuò)展的,那么你應(yīng)該好好地考慮一下編碼時(shí)總是使用接口。這是非常常見(jiàn)的最佳實(shí)踐,而且和 ZF2 沒(méi)有直接關(guān)系,更多的是遵守嚴(yán)格的面向?qū)ο蟪绦蚓幊桃?guī)范。

我們介紹過(guò)的多層架構(gòu)(控制器 -> 服務(wù) -> 映射器 -> 后端)的用處在于讓我們嚴(yán)格地分開(kāi)考慮所有的對(duì)象。有許多可用的資源可以為您詳細(xì)解釋每一個(gè)分層的巨大優(yōu)勢(shì),所以請(qǐng)自行參閱。

然而,對(duì)于十分簡(jiǎn)單的應(yīng)用程序,你很可能會(huì)將映射器層刪減掉。實(shí)際上所有映射器的代碼層通常都直接存放在服務(wù)內(nèi)部。而且這招對(duì)大多數(shù)應(yīng)用程序都好使,但一旦你開(kāi)始打算支持多個(gè)后端(例如開(kāi)源軟件);又或者,你想準(zhǔn)備好更換后端,那么你應(yīng)該總是考慮是否要包含這一層。

有這么多對(duì)象,難道就不會(huì)有很多代碼重疊嗎?

簡(jiǎn)單回答:沒(méi)錯(cuò)。

詳細(xì)回答:實(shí)際上沒(méi)必要詳細(xì)回答。大多數(shù)代碼重疊也都來(lái)自于映射器層、如果你自己看看那些類,你就會(huì)注意到實(shí)際上只有兩件事情是和某個(gè)具體對(duì)象捆綁的。第一件就是,數(shù)據(jù)庫(kù)表的名稱;第二件就是,事實(shí)上是對(duì)象原型會(huì)被傳值給映射器。

原型已經(jīng)從 __construct() 函數(shù)傳進(jìn)類里所以它已經(jīng)是可替換的。如果你想讓表名也是可替換的話,你只需要在構(gòu)造器中提供表名即可,然后你就有了一個(gè)全能的數(shù)據(jù)庫(kù)映射器實(shí)現(xiàn),基本上可以用來(lái)處理你的應(yīng)用程序里幾乎所有的對(duì)象。

你可以參照下例來(lái)編寫(xiě)一個(gè) factory 類:

 <?php

 class NewsMapperFactory implements FactoryInterface
 {
     public function createService(ServiceLocatorInterface $serviceLocator)
     {
         return new ZendDbSqlMapper(
             $serviceLocator->get('Zend\Db\Adapter\Adapter'), // 數(shù)據(jù)庫(kù)適配器
             'news',                                          // 表名
             new ClassMethods(false),                         // 對(duì)象充水器
             new News()                                       // 對(duì)象原型
         );
     }
 }

為什么有那么多控制器?

如果你回過(guò)頭去看幾年前的代碼示例,就會(huì)發(fā)現(xiàn)曾經(jīng)在每個(gè)控制器里面都有大量的代碼。這已經(jīng)被認(rèn)為是一個(gè)不良實(shí)踐,這類控制器被稱為“肥胖控制器”或者“臃腫控制器”。

我們之前創(chuàng)建的每個(gè)控制器之間的主要區(qū)別是不同的控制器有不同的依賴對(duì)象。舉例來(lái)說(shuō),WriteController 要求 PostFormPostService,然而 DeleteController 只要求 PostService。對(duì)于這個(gè)例子來(lái)說(shuō),將 deleteAction() 寫(xiě)入 WriteController 是不合理的,因?yàn)檫@會(huì)導(dǎo)致過(guò)程中創(chuàng)建一個(gè)不必要的 PostForm 實(shí)例。在大型程序中這種錯(cuò)誤會(huì)制造巨大的性能瓶頸,從而拖慢應(yīng)用程序。

再看看 DeleteControllerListController,你會(huì)注意到兩個(gè)控制器都有一樣的依賴對(duì)象。兩者都只要求 PostService,那么為何不將其合并成一個(gè)控制器呢?原因是語(yǔ)義,你會(huì)跑去 ListController 尋找 deleteAction() 嗎?我們大多數(shù)人不會(huì)干這事,所以我們?yōu)槠浣⒘艘粋€(gè)新類。

在應(yīng)用程序中的 InsertFormUpdateForm 互有區(qū)別,你也許會(huì)總是想將其分為兩個(gè)控制器而不是像我們的例子中將其做成一個(gè)聯(lián)合的 WriteController。這類事情基本上是因應(yīng)用程序的不同而不同,不過(guò)基本目標(biāo)一直都是:讓你的控制器總是纖細(xì)/輕量。

還有更多的問(wèn)題嗎? 請(qǐng)聯(lián)絡(luò)我們!

如果你覺(jué)得這個(gè) FAQ 還少了點(diǎn)什么,請(qǐng)將你的問(wèn)題私信給我們,然后我們會(huì)給你答案!