明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

用封裝類(lèi)來(lái)合理的設(shè)計(jì)PHP項(xiàng)目--談PHP項(xiàng)目中類(lèi)的封裝

[摘要]編碼對(duì)于合格的PHP程序員來(lái)說(shuō)并不是什么難事(也許只是花費(fèi)時(shí)間長(zhǎng)短的問(wèn)題),因此系統(tǒng)分析和設(shè)計(jì)這一階段就顯得尤為重要。不過(guò)本文并不打算討論和需求分析、獲取商業(yè)邏輯相關(guān)的話(huà)題,而是針對(duì)系統(tǒng)設(shè)計(jì)方面進(jìn)行探討。面臨難題編碼對(duì)于合格的PHP程序員來(lái)說(shuō)并不是什么難事(也許只是花費(fèi)時(shí)間長(zhǎng)短的問(wèn)題),因此系統(tǒng)分...
編碼對(duì)于合格的PHP程序員來(lái)說(shuō)并不是什么難事(也許只是花費(fèi)時(shí)間長(zhǎng)短的問(wèn)題),因此系統(tǒng)分析和設(shè)計(jì)這一階段就顯得尤為重要。不過(guò)本文并不打算討論和需求分析、獲取商業(yè)邏輯相關(guān)的話(huà)題,而是針對(duì)系統(tǒng)設(shè)計(jì)方面進(jìn)行探討。
面臨難題
編碼對(duì)于合格的PHP程序員來(lái)說(shuō)并不是什么難事(也許只是花費(fèi)時(shí)間長(zhǎng)短的問(wèn)題),因此系統(tǒng)分析和設(shè)計(jì)這一階段就顯得尤為重要。對(duì)于一個(gè)擔(dān)任PHP項(xiàng)目的系統(tǒng)分析員來(lái)說(shuō),面臨著兩個(gè)難題:

PHP語(yǔ)言本身的限制。
這一點(diǎn)在復(fù)雜系統(tǒng)的面向?qū)ο笤O(shè)計(jì)中尤其顯著。PHP的面向?qū)ο筇匦栽诂F(xiàn)有版本中雖然得到了改善,但是還不甚健全,根本不足以擔(dān)任面向?qū)ο笤O(shè)計(jì)的實(shí)現(xiàn)語(yǔ)言;即使眼光長(zhǎng)遠(yuǎn)一些,在即將釋出的以Zend Engine 2.0支持的全新PHP中,面向?qū)ο筇匦砸膊粫?huì)像現(xiàn)在流行的Java或者C++那樣(關(guān)于這方面的內(nèi)容可以參見(jiàn)我在developerWorks中國(guó)網(wǎng)站發(fā)表的另一篇文章)。但是如果采用完全面向過(guò)程(準(zhǔn)確說(shuō)是面向Web頁(yè)面)的方式,可以想見(jiàn)整個(gè)系統(tǒng)的設(shè)計(jì)會(huì)非常復(fù)雜,而由此帶來(lái)的編碼復(fù)雜和維護(hù)困難更加難以應(yīng)付。
現(xiàn)有資料的嚴(yán)重缺乏。
這是眾所周知的現(xiàn)象即針對(duì)Web項(xiàng)目的系統(tǒng)設(shè)計(jì)資料不足;而在這些有限資料中,關(guān)于PHP的設(shè)計(jì)資料又非常匱乏。如果本公司或本人也沒(méi)有相關(guān)的技術(shù)積累,系統(tǒng)分析員只能在黑暗中摸索方法(更壞的兩種情況,一是照搬其他項(xiàng)目比如Java或者C++的設(shè)計(jì),二是認(rèn)為項(xiàng)目簡(jiǎn)單而不負(fù)責(zé)任的草草了事)。



認(rèn)識(shí)面對(duì)的系統(tǒng)
既然如此,采用何種方法妥善處理PHP系統(tǒng)的分析和設(shè)計(jì)?最初的構(gòu)想應(yīng)該需要分清項(xiàng)目承擔(dān)任務(wù)的類(lèi)型:

涉及大量客戶(hù)本身或者客戶(hù)所在行業(yè)的商業(yè)邏輯的項(xiàng)目,包括辦公系統(tǒng)、訂單系統(tǒng)以及其他商業(yè)系統(tǒng)。
簡(jiǎn)單網(wǎng)站項(xiàng)目,包括一些需要承擔(dān)高訪問(wèn)量或要求快速響應(yīng)的項(xiàng)目比如品牌網(wǎng)站或者活動(dòng)網(wǎng)站以及其他一些網(wǎng)站。
綜合性網(wǎng)站項(xiàng)目。通常包含多個(gè)相對(duì)獨(dú)立的子系統(tǒng)比如新聞子系統(tǒng)、論壇子系統(tǒng)、產(chǎn)品陳列子系統(tǒng)等等。



PHP的設(shè)計(jì)初衷在于解決后兩種項(xiàng)目的迫切需求,語(yǔ)言本身對(duì)于這些項(xiàng)目進(jìn)行了良好的改造。而眾多的PHP開(kāi)發(fā)者對(duì)這些項(xiàng)目也具有或多或少的經(jīng)驗(yàn),相關(guān)書(shū)籍中的范例也大都圍繞于此。相對(duì)說(shuō)來(lái)第一種系統(tǒng)所有的資料不多,各種出版物對(duì)其內(nèi)容也很少提及。因此在本文中將題所述對(duì)第一種類(lèi)型的項(xiàng)目進(jìn)行詳細(xì)講述(有關(guān)MVC模式和類(lèi)封裝),同時(shí)附帶提及第二種項(xiàng)目(有關(guān)黑客代碼)以及第三種項(xiàng)目的設(shè)計(jì)方法。當(dāng)然,并不是被歸類(lèi)的這些項(xiàng)目就只能采用本文描述的方式,系統(tǒng)分析員需要權(quán)衡各方面因素加以選擇。

方案一:涉及大量商業(yè)邏輯項(xiàng)目
如何分離用戶(hù)界面和后臺(tái)操作?如何避免將商業(yè)邏輯混淆于一般的流程控制中?作為一個(gè)嚴(yán)謹(jǐn)?shù)纳逃庙?xiàng)目,就需要考慮很多類(lèi)似的問(wèn)題。對(duì)于由PHP擔(dān)當(dāng)?shù)倪@類(lèi)項(xiàng)目,貫徹Model-View-Controller(MVC)模式的設(shè)計(jì)是一個(gè)非常好的方法。

理論描述
在這里我不想多加解釋MVC模式本身--簡(jiǎn)單的從字面上以及應(yīng)用上說(shuō),通過(guò)將系統(tǒng)的設(shè)計(jì)分為Model模型/邏輯、View視圖/界面、Controller控制/流程三個(gè)邏輯部分達(dá)到良好的項(xiàng)目效果,以此便利各部分開(kāi)發(fā)者的工作并降低日后的維護(hù)成本。(如果您熟悉JSP開(kāi)發(fā)的Model 2模式,可以發(fā)現(xiàn)它也是MVC模式的很好體現(xiàn)。)就現(xiàn)實(shí)的項(xiàng)目開(kāi)發(fā)而言,現(xiàn)存的很大問(wèn)題包括網(wǎng)頁(yè)設(shè)計(jì)人員和程序開(kāi)發(fā)人員的工作交錯(cuò)和沖突以及商業(yè)邏輯嵌入頁(yè)面造成不可重用也很難維護(hù)等等。引入MVC模式一方面可以為系統(tǒng)的總體設(shè)計(jì)指出明確的方向,對(duì)于開(kāi)發(fā)團(tuán)隊(duì)的分工也是良好的指導(dǎo)。

既然依照MVC模式要求對(duì)系統(tǒng)的總體結(jié)構(gòu)在邏輯上分成三部分,那么團(tuán)隊(duì)的開(kāi)發(fā)者中也存在著針對(duì)各個(gè)部分的開(kāi)發(fā)者。

開(kāi)發(fā)者角色 相關(guān)系統(tǒng)邏輯 職責(zé)
網(wǎng)頁(yè)設(shè)計(jì)人員 View視圖/界面 設(shè)計(jì)所有用戶(hù)界面的網(wǎng)頁(yè)模板。
控制流程開(kāi)發(fā)人員 Controller控制/流程 編寫(xiě)系統(tǒng)流程中的所有PHP頁(yè)面。
商業(yè)邏輯開(kāi)發(fā)人員 Model模型/邏輯 開(kāi)發(fā)系統(tǒng)設(shè)計(jì)中規(guī)定的各個(gè)類(lèi)(其中的方法)。



由以上的表格可以看出,傳統(tǒng)的網(wǎng)頁(yè)設(shè)計(jì)和程序開(kāi)發(fā)的人員分工被打破而取而代之的是根據(jù)系統(tǒng)邏輯劃定的職責(zé)。對(duì)于網(wǎng)頁(yè)設(shè)計(jì)人員,職責(zé)并沒(méi)有改變,準(zhǔn)確說(shuō)由于這樣的劃分避免了以往與程序設(shè)計(jì)人員的糾紛,他們完成的只是網(wǎng)頁(yè)模板,因此只需關(guān)注于純粹的網(wǎng)頁(yè)代碼(主要是HTML,也許會(huì)有其他客戶(hù)端的代碼比如WML之類(lèi))而根本不需要被服務(wù)器端的<? … ?>干擾。程序設(shè)計(jì)人員則被分為兩部分:其中比較容易把握的是商業(yè)邏輯開(kāi)發(fā)人員,他們的任務(wù)是根據(jù)系統(tǒng)分析員給定的模塊(準(zhǔn)確說(shuō)是類(lèi)方法)完成之,在他們手中的PHP更像一般的程序設(shè)計(jì)語(yǔ)言(比如Java)而與Web沒(méi)有什么關(guān)系;一時(shí)較難接受的是控制流程開(kāi)發(fā)人員,他們的任務(wù)是在實(shí)現(xiàn)系統(tǒng)設(shè)計(jì)時(shí)制定的系統(tǒng)流程的同時(shí),根據(jù)客戶(hù)端的輸入調(diào)用商業(yè)邏輯(相應(yīng)的類(lèi)方法)以及輸出更新的界面(對(duì)設(shè)計(jì)網(wǎng)頁(yè)模板進(jìn)行處理),在他們手中PHP可以充分發(fā)揮Web編程語(yǔ)言的優(yōu)勢(shì)。

代碼組織相關(guān)的話(huà)題
這樣的觀念有些抽象,沒(méi)有實(shí)例的演示很難接受。在舉例之前先介紹一下我對(duì)這類(lèi)工程的推薦代碼結(jié)構(gòu):

一級(jí)目錄 二級(jí)目錄 三級(jí)目錄 備注
/project_name 項(xiàng)目源代碼根目錄
/Templates 網(wǎng)頁(yè)模板目錄(View)
/admin 管理控制臺(tái)目錄/admin下的網(wǎng)頁(yè)模板
/Include 商業(yè)邏輯目錄(Model)
/Temp 臨時(shí)代碼目錄(可選),可供開(kāi)發(fā)者進(jìn)行一些試驗(yàn)代碼的測(cè)試
/images 圖片目錄,網(wǎng)頁(yè)模板采用
/css 樣式單目錄,網(wǎng)頁(yè)模板采用
/scripts 客戶(hù)端代碼目錄,網(wǎng)頁(yè)模板采用
/admin 管理控制臺(tái)目錄(可選),包含所有后臺(tái)管理的功能代碼
/other_dir 對(duì)應(yīng)與源代碼根目錄下的/other_dir,包含管理該類(lèi)的功能代碼
/other_dir 其他與相應(yīng)功能相關(guān)的目錄,比如與用戶(hù)相關(guān)的/member目錄或者與從產(chǎn)品相關(guān)的/product目錄等等
/config.inc.php 全局配置變量,定義系統(tǒng)中的全局變量
/security.inc.php 安全策略控制(可選)
/error.php 錯(cuò)誤控制返回頁(yè)面(可選),也可以采用靜態(tài)頁(yè)面如/error.html或者其他頁(yè)面名稱(chēng)



看完之后您是不是被喚起了一點(diǎn)使用Java進(jìn)行Web開(kāi)發(fā)的記憶?比如WEB-INF目錄下的classes目錄和lib目錄以及web.xml都是開(kāi)發(fā)中的規(guī)則--雖然支持PHP的Web服務(wù)器不可能像Java應(yīng)用服務(wù)器那樣自動(dòng)加載這些目錄下的文件,但是規(guī)定一個(gè)合適的代碼組織模式還是非常有利于開(kāi)發(fā)的便利和后續(xù)的維護(hù)的。(我開(kāi)始考慮PHP項(xiàng)目的代碼結(jié)構(gòu)就是由Tomcat的開(kāi)發(fā)手冊(cè)中獲得了啟發(fā)。)

一個(gè)用戶(hù)登錄的例子
根據(jù)以上的代碼目錄,前文所說(shuō)的MVC模式的實(shí)現(xiàn)可以得到更簡(jiǎn)單的解釋。以最常見(jiàn)的用戶(hù)登錄功能為例,設(shè)想/project_name目錄下有一個(gè)/member目錄包含有關(guān)于用戶(hù)的一切功能,其中包含了login.php頁(yè)面接受用戶(hù)登錄使用的用戶(hù)名稱(chēng)和密碼,index.php頁(yè)面是登錄完成之后的用戶(hù)主頁(yè),而/project_name目錄下的error.php是登錄失敗后的錯(cuò)誤顯示頁(yè)面。

用戶(hù)通過(guò)系統(tǒng)的其他部分請(qǐng)求進(jìn)入用戶(hù)主頁(yè)即/member/index.php頁(yè)面,此時(shí)該頁(yè)面判斷用戶(hù)情況:已登錄用戶(hù)則直接顯示本頁(yè)內(nèi)容(可以采用檢查session等方法);未登錄用戶(hù)則需要登錄(重定向到/member/login.php);出現(xiàn)了未知錯(cuò)誤(重定向到/error.php)。同時(shí)采取相應(yīng)的反應(yīng)。
假如用戶(hù)被引導(dǎo)至登錄頁(yè)面即/member/login.php頁(yè)面,該頁(yè)面接受用戶(hù)的登錄信息(用戶(hù)名稱(chēng)和密碼),并判斷是否正確登錄:正確登錄則再次重定向到用戶(hù)主頁(yè)/member/index.php;登錄錯(cuò)誤則重定向到/error.php。
假如用戶(hù)被引導(dǎo)至錯(cuò)誤顯示頁(yè)面/error.php頁(yè)面(無(wú)論是從以上哪個(gè)頁(yè)面前來(lái)),都會(huì)顯示錯(cuò)誤信息。



流程圖示如下:





根據(jù)以上的文字描述和圖示,再結(jié)合MVC模式的實(shí)現(xiàn),可以非常輕松的寫(xiě)出這幾個(gè)頁(yè)面的框架代碼:

先看簡(jiǎn)單的頁(yè)面/error.php:

然后是/member/login.php:

最后是/member/index.php:




(注意:以上代碼只是片斷,而且沒(méi)有考慮項(xiàng)目全局,只起演示作用)

關(guān)于Controller
首先可以明確的是,以上的三個(gè)頁(yè)面代碼就是前文所說(shuō)的Controller控制/流程代碼。很明顯,他們的不包含特定的操作,也沒(méi)有一行網(wǎng)頁(yè)代碼,有的只是與前面流程圖一致的流程控制代碼(放眼望去,這些頁(yè)面的共同特點(diǎn)是充滿(mǎn)了引用網(wǎng)頁(yè)模板并輸出、取得對(duì)象并執(zhí)行其某個(gè)方法或者重定向)。

再選擇其中的一個(gè)頁(yè)面/member/login.php詳細(xì)的解釋。整個(gè)頁(yè)面通過(guò)判斷是否提交表單分為兩個(gè)部分:顯示登錄表單供用戶(hù)填寫(xiě)和處理登錄信息。作為前者直接引用一個(gè)處于網(wǎng)頁(yè)模板目錄/Templates下對(duì)應(yīng)該頁(yè)面的member_login.dwt并在解析后輸出;作為后者先取得一個(gè)Member對(duì)象(該對(duì)象出于商業(yè)邏輯目錄/Include下的Member.inc.php中),然后獲得登錄判斷的結(jié)果后進(jìn)行重定向。在這個(gè)控制頁(yè)面的代碼中,member_login.dwt作為View視圖/界面出現(xiàn),類(lèi)Member作為Model模型/邏輯出現(xiàn),而頁(yè)面代碼本身就Controller控制/流程。下面就是加入標(biāo)示的/member/login.php框架代碼:





(關(guān)于模板類(lèi)以及在MVC模式中的應(yīng)用,可以參考本站另一篇文章《在PHP中選擇合適的模板》)

關(guān)于Model
既然談到了Model,下面就是另一個(gè)重要的話(huà)題:類(lèi)封裝在PHP項(xiàng)目中的應(yīng)用。

請(qǐng)注意用詞"類(lèi)封裝"--這和"面向?qū)ο?quot;或者其他什么"采用對(duì)象設(shè)計(jì)"的方法有著本質(zhì)的不同。"類(lèi)封裝"只是講述了將商業(yè)邏輯采用類(lèi)方法的方式封裝成各個(gè)不同的類(lèi),因而這里的"類(lèi)"并不是因此采用了面向?qū)ο笤O(shè)計(jì)出現(xiàn)的"類(lèi)"--準(zhǔn)確的說(shuō),這里的"類(lèi)"其實(shí)是對(duì)一系列相關(guān)功能模塊進(jìn)行合并的結(jié)果。

為什么不直接采用面向?qū)ο蟮姆绞蕉遣捎眠@種看起來(lái)不倫不類(lèi)的辦法去設(shè)計(jì)系統(tǒng)呢?PHP不是具有面向?qū)ο筇匦詥幔坎诲e(cuò),PHP具有這樣的特性,但是非常不完全(可以參考本站另一篇文章《從Zend Engine 2.0的設(shè)計(jì)藍(lán)圖(草稿)看PHP的將來(lái)》)。舉例來(lái)說(shuō),PHP是沒(méi)有接口這一概念和實(shí)現(xiàn)方法的,同時(shí)也就沒(méi)有什么多重繼承、方法重載之類(lèi)的典型面向?qū)ο筇卣鳌H绻且捎妹嫦驅(qū)ο蟮脑O(shè)計(jì)方法,也許在概要設(shè)計(jì)階段可以非常輕松,但是詳細(xì)設(shè)計(jì)階段就會(huì)比較苦悶,而如果還有幸堅(jiān)持到編碼階段簡(jiǎn)直就是苦不堪言了。另一方面,如果不在系統(tǒng)中引入類(lèi)的概念,而是采用函數(shù)來(lái)實(shí)現(xiàn)模塊功能,那么可以想象在一個(gè)采用這樣"純粹"的中大型系統(tǒng)中會(huì)有多少的函數(shù),由此帶來(lái)的麻煩非常明顯。

還是回到PHP語(yǔ)言本身。雖然PHP提供不了什么實(shí)際的面向?qū)ο笾С郑沁是提供了對(duì)類(lèi)以及其中的屬性和方法的定義。那么自然而然可以想到的是采用類(lèi)的方法封裝相關(guān)函數(shù)模塊,既可以借鑒一些對(duì)象設(shè)計(jì)的優(yōu)點(diǎn),又可以避免完全采用函數(shù)模塊的一些缺點(diǎn)。

(一些采用函數(shù)模塊的系統(tǒng)會(huì)采用這樣一種方式:將相關(guān)的函數(shù)編寫(xiě)在相同的文件中,這樣在引用時(shí)可以引入單獨(dú)的文件。比如Member.func.php這個(gè)文件中包含了所有與用戶(hù)相關(guān)的操作,在處理用戶(hù)登錄時(shí)可以先require這個(gè)文件,然后調(diào)用諸如member_login()這樣的函數(shù)。但是這樣的方式僅僅解決了系統(tǒng)中眾多函數(shù)的代碼組織問(wèn)題,沒(méi)有解決名字沖突的問(wèn)題。下面的舉例中就會(huì)看到。)

比如上文的用戶(hù)登錄實(shí)例中,如果采用函數(shù)模塊的方法,代碼也許是這樣:





而采用類(lèi)封裝的方法,可能就是這樣:





也許您會(huì)覺(jué)得代碼并沒(méi)有什么區(qū)別(甚至看起來(lái)采用函數(shù)模塊的代碼由于不需要取得新的對(duì)象而顯得更簡(jiǎn)潔一些),而真正的不同是發(fā)生在include的文件里面。采用函數(shù)模塊的方法將相關(guān)的函數(shù)集合在一個(gè)文件中加以組織(有些系統(tǒng)還不能做到這一點(diǎn),那么就會(huì)造成異;靵y的局面),而采用類(lèi)封裝的方法在每一個(gè)文件中聲明一個(gè)和文件名相同的類(lèi)(比如在Member.inc.php聲明一個(gè)Member的類(lèi),這一點(diǎn)和Java的規(guī)定相似);而在使用時(shí),都需要先進(jìn)行include(如果采用函數(shù)模塊又沒(méi)有進(jìn)行很好的組織,也許有些人就會(huì)很"簡(jiǎn)便"的將所有函數(shù)include進(jìn)每一個(gè)頁(yè)面--PHP可不是Java那樣編譯執(zhí)行,光是解析這些函數(shù)就會(huì)花費(fèi)一段時(shí)間),但是關(guān)鍵就在于采用類(lèi)封裝的方法可以清楚的指明調(diào)用的位置--某個(gè)類(lèi)(Member)的某個(gè)方法(login):從避免名字沖突的角度來(lái)說(shuō)這一點(diǎn)是非常成功的;而對(duì)于代碼檢查和維護(hù)而言,方便程度更是不言而喻。設(shè)想一個(gè)頁(yè)面需要完成若干功能,因而需要include數(shù)個(gè)文件:采用函數(shù)模塊的方法不能夠輕易的從函數(shù)調(diào)用中找到函數(shù)本身所在的文件(如果函數(shù)名稱(chēng)或者include文件名稱(chēng)沒(méi)有什么統(tǒng)一規(guī)則,那么這個(gè)工作就非常艱巨了),而采用類(lèi)封裝的辦法可以根據(jù)類(lèi)名稱(chēng)和類(lèi)文件名稱(chēng)準(zhǔn)確定位類(lèi)方法代碼的位置。(也許您會(huì)認(rèn)為這樣一個(gè)小小的好處不足掛齒,但是經(jīng)歷一個(gè)維護(hù)工程之后也許就不會(huì)再有什么異議。)

以上是采用類(lèi)封裝方法的原因,決定采用這種方法設(shè)計(jì)系統(tǒng)只是第一步;完成整個(gè)系統(tǒng)的設(shè)計(jì)還有很多可以借鑒的經(jīng)驗(yàn)。

部分設(shè)計(jì)可以借鑒面向?qū)ο蟮乃悸贰km然PHP中沒(méi)有接口和抽象類(lèi)的定義,繼承機(jī)制也非常不完全,但至少具備了基本的類(lèi)定義和簡(jiǎn)單的繼承關(guān)系。類(lèi)似"公司-雇員"、"賣(mài)家-商品-買(mǎi)家"這類(lèi)顯而易見(jiàn)的關(guān)系可以很容易在系統(tǒng)中通過(guò)類(lèi)和類(lèi)關(guān)系定義。既然PHP可以做到這一點(diǎn),就按照實(shí)際的邏輯關(guān)系去定義即可。
經(jīng)常會(huì)在系統(tǒng)中出現(xiàn)的另一個(gè)情況是關(guān)于個(gè)體和列表的關(guān)系--這樣說(shuō)也許難以理解,想象一個(gè)BBS系統(tǒng)中的帖子列表和每個(gè)帖子之間,就是這樣的關(guān)系。根據(jù)設(shè)計(jì)經(jīng)驗(yàn),這樣的關(guān)系大量存在于PHP或者其他Web系統(tǒng)中。對(duì)于這類(lèi)關(guān)系,我個(gè)人建議可以采用以下Item和Item_List的類(lèi)封裝方式:
Item類(lèi)定義:Item.inc.php的代碼
Item_List類(lèi)定義:Item_List.inc.php的代碼
由于PHP對(duì)于類(lèi)的成員變量和方法并沒(méi)有語(yǔ)法上的訪問(wèn)限制(均為公開(kāi)),因此會(huì)帶來(lái)對(duì)象使用方面的某些混亂;诖,建議在開(kāi)發(fā)團(tuán)隊(duì)的代碼規(guī)范中加以規(guī)定,從代碼應(yīng)用的級(jí)別上控制這一情況:
首先,可以通過(guò)對(duì)成員變量和方法的注釋來(lái)說(shuō)明其屬性,由此使用該對(duì)象的其他開(kāi)發(fā)人員可以了解自己的使用方法是否觸犯了規(guī)定的訪問(wèn)限制。(如果采用phpdoc等自動(dòng)文檔生成的工具,開(kāi)發(fā)人員甚至可以在不翻閱類(lèi)源碼的情況下通過(guò)瀏覽類(lèi)文檔正確使用它。)
其次,對(duì)于成員變量訪問(wèn)限制的考慮,可以將一些主要的、經(jīng)常需要被訪問(wèn)或更改的變量(在注釋中)聲明為公開(kāi)。這樣的作法可以省卻大量get()和set()方法的代碼--雖然在其他的面向?qū)ο笳Z(yǔ)言中這一點(diǎn)被認(rèn)為非常丑陋,但是記住PHP不是Java,只要這樣的用法合理,就應(yīng)該大膽使用。
從上面的示例代碼中您也許已經(jīng)注意到了注釋的比重--雖然大家都了解注釋的重要性,但是仍然有必要提出。這個(gè)示例中采用了Javadoc的樣式,利用現(xiàn)有工具也可以很容易的直接生成文檔(當(dāng)然您和您的開(kāi)發(fā)團(tuán)隊(duì)也可以定義自己的合適注釋樣式和文檔生成工具)。對(duì)于系統(tǒng)分析員來(lái)說(shuō),您在設(shè)計(jì)階段完成之后交付給您的開(kāi)發(fā)伙伴的代碼部分很可能就是這些注釋占絕大部分的框架代碼;你們之間交流的工具除了那些沒(méi)完沒(méi)了的圖表之外就是這些程序員最熟悉的代碼和注釋了。



在PHP系統(tǒng)中進(jìn)行類(lèi)的設(shè)計(jì)雖然不像構(gòu)建面向?qū)ο笙到y(tǒng)那樣需要各種合理的模式介入(也沒(méi)有這樣的"本錢(qián)"為之),但還是需要一番思量的。邏輯上的合理性和操作上的可行性都是檢驗(yàn)的標(biāo)準(zhǔn)。

(說(shuō)到類(lèi)設(shè)計(jì),又想到了適合PHP開(kāi)發(fā)的IDE問(wèn)題。據(jù)我所知比較專(zhuān)業(yè)一些有Zend出品的Zend IDE;另外還有作為JBuilder的Open Tools出現(xiàn)的借助JBuilder的PHP開(kāi)發(fā)工具;不過(guò)最常用的還是PHPEd或者UltraEdit之類(lèi)的編輯器。如果現(xiàn)有的編輯器可以非常聰明的支持PHP的類(lèi)設(shè)計(jì)和代碼實(shí)現(xiàn)就非常理想了。)

關(guān)于View
最后說(shuō)到的是View方面,雖然這部分內(nèi)容與網(wǎng)頁(yè)設(shè)計(jì)人員聯(lián)系比較緊密,不過(guò)PHP項(xiàng)目(以及其他Web項(xiàng)目)的系統(tǒng)分析員也必須關(guān)注這一話(huà)題?梢钥闯鯩VC模式的應(yīng)用使得網(wǎng)頁(yè)開(kāi)發(fā)人員和程序設(shè)計(jì)人員的各自工作成果不會(huì)像以前那樣互相影響,自然可以提高各自的工作效率(相互關(guān)系也許會(huì)比以前更加融洽一些)。但是對(duì)于系統(tǒng)分析員來(lái)說(shuō),將用戶(hù)界面分離為各個(gè)獨(dú)立的網(wǎng)頁(yè)模板需要進(jìn)行許多分析工作。

首先是確定整個(gè)系統(tǒng)的流程,這一點(diǎn)在系統(tǒng)設(shè)計(jì)的初期就應(yīng)該做到。而對(duì)于View視圖/界面和Controller控制/流程來(lái)說(shuō),所有需要的頁(yè)面都是圍繞此流程產(chǎn)生。不過(guò)通常此時(shí)能夠在流程圖上看到的也許只是相關(guān)的參數(shù)在各個(gè)頁(yè)面之間傳遞,卻不能了解各個(gè)頁(yè)面展示的內(nèi)容--這就是下一步分析用戶(hù)界面需要進(jìn)行的工作。

在分析用戶(hù)界面的工作中,第一步可以確定各個(gè)頁(yè)面核心、對(duì)于完成流程必不可少的用戶(hù)界面元素(表單和表單域、鏈接等);第二步是確定頁(yè)面中需要出現(xiàn)的導(dǎo)航內(nèi)容;最后還需要依據(jù)流程復(fù)核。還是以上文的用戶(hù)登錄為例。對(duì)于/member/login.php這個(gè)關(guān)鍵的頁(yè)面,第一步可以確定的是在用戶(hù)提交之前應(yīng)該顯示一個(gè)表單,表單包含兩個(gè)文本框供用戶(hù)輸入用戶(hù)名稱(chēng)和密碼;而提交之后根據(jù)流程在本頁(yè)面中不需要有用戶(hù)界面,取而代之的是利用Controller控制/流程這一邏輯層進(jìn)行重定向。而第二步需要制定該頁(yè)面中(準(zhǔn)確說(shuō)是在顯示登錄表單時(shí))需要提供的導(dǎo)航鏈接,在這里可以加上到系統(tǒng)的主頁(yè)或者其他非注冊(cè)用戶(hù)頁(yè)的起點(diǎn)的鏈接(方便用戶(hù)臨時(shí)決定取消登錄)以及一個(gè)注銷(xiāo)現(xiàn)有用戶(hù)的鏈接(針對(duì)已登錄用戶(hù))。之后進(jìn)行復(fù)核,此時(shí)也許會(huì)發(fā)現(xiàn)這一設(shè)計(jì)似乎沒(méi)有考慮到在登錄前更好的區(qū)別是管理員登錄還是普通用戶(hù)登錄,那么就可以在表單中增加一個(gè)隱藏域表示選擇登錄的用戶(hù)是準(zhǔn)備以管理員還是普通用戶(hù)的身份進(jìn)行登錄。

確定完用戶(hù)界面的元素,并不意味著可以將這些分析結(jié)果交付網(wǎng)頁(yè)設(shè)計(jì)人員進(jìn)行制作了;還有最關(guān)鍵的一步?jīng)]有實(shí)施--為分析完成的各個(gè)頁(yè)面制定模板所需的變量名稱(chēng)。對(duì)于以上的用戶(hù)登錄實(shí)例,如果系統(tǒng)有識(shí)別曾經(jīng)登錄用戶(hù)的功能(依據(jù)之前訪問(wèn)時(shí)在客戶(hù)端設(shè)置的相關(guān)cookie值)并且把這個(gè)用戶(hù)名稱(chēng)顯示在登錄表單的用戶(hù)名稱(chēng)一欄,此時(shí)就需要在member_login.dwt設(shè)計(jì)中說(shuō)明該表單域?qū)⒈毁x值為一個(gè)模板變量(比如{USERNAME})。這一步驟完成之后就可以交付網(wǎng)頁(yè)設(shè)計(jì)人員進(jìn)行制作了。

需要指出的是,在編碼階段很可能局部的一些系統(tǒng)設(shè)計(jì)需要進(jìn)行修改,這其中也許就包括對(duì)網(wǎng)頁(yè)模板的修改,需要仔細(xì)處理。

對(duì)于代碼組織的補(bǔ)充說(shuō)明
還有幾個(gè)文件和目錄沒(méi)有在上文提及:

config.inc.php -- 如果您熟悉phpMyAdmin或者其他phpWizard.net釋出的項(xiàng)目,就應(yīng)該非常清楚這個(gè)文件的作用:定義本項(xiàng)目范圍內(nèi)的全局變量(在每個(gè)頁(yè)面中被include)。我個(gè)人認(rèn)為這是一個(gè)非常良好的設(shè)計(jì),因此也提倡在項(xiàng)目中應(yīng)用。另外,為了保證與項(xiàng)目中其他的變量沖突,建議在該文件中定義一個(gè)多重?cái)?shù)組,而各種全局變量都以該數(shù)組的某一個(gè)值出現(xiàn)。這樣方便團(tuán)隊(duì)中的其他開(kāi)發(fā)者只需要避免一個(gè)變量名的使用,而不是避免所有config.inc.php中出現(xiàn)的變量名。使用這個(gè)文件的另一個(gè)好處是由于將關(guān)鍵的變量(比如與服務(wù)器環(huán)境相關(guān)的變量)集中定義,可以方便的安裝和移植整個(gè)項(xiàng)目。
security.inc.php -- 顧名思義這個(gè)文件控制并實(shí)施整個(gè)系統(tǒng)的安全策略。關(guān)于安全問(wèn)題,可以想到的是兩種控制方案:在每個(gè)控制流程頁(yè)面頂端針對(duì)本頁(yè)面加以控制以及采用一個(gè)控制文件整個(gè)控制并被加入每個(gè)控制流程頁(yè)面。我個(gè)人提倡采用后一種方式,原因也很簡(jiǎn)單:定義簡(jiǎn)單而且維護(hù)方便。雖然相比每個(gè)頁(yè)面單獨(dú)定義,也許會(huì)損失一點(diǎn)點(diǎn)效率(一些不需要安全控制的頁(yè)面也需要include該文件并進(jìn)行判別),但是獲得的是對(duì)系統(tǒng)安全的整體控制以及代碼維護(hù)的便利(損失一個(gè)if…else…的判別換取這樣的結(jié)果還是很值得的)。
/Temp -- 很明顯存在于這個(gè)目錄下的都是一個(gè)臨時(shí)文件,并且這個(gè)目錄其實(shí)并不會(huì)出現(xiàn)在項(xiàng)目正式發(fā)行的版本中。如果開(kāi)發(fā)時(shí)對(duì)一些函數(shù)的使用不甚明了或者試驗(yàn)一段沒(méi)有相關(guān)經(jīng)驗(yàn)的代碼,都可以在此目錄下建立文件;因?yàn)樵撃夸浘臀挥陧?xiàng)目代碼之中,可以非常便利的取得項(xiàng)目運(yùn)行的上下文環(huán)境,大大降低了試驗(yàn)代碼的成本。
/admin -- 通常對(duì)系統(tǒng)的后臺(tái)管理內(nèi)容應(yīng)該放置在一個(gè)獨(dú)立的目錄中,我個(gè)人比較喜歡admin這個(gè)簡(jiǎn)寫(xiě)詞(當(dāng)然也有一些情況系統(tǒng)分析員認(rèn)為不應(yīng)該設(shè)置一個(gè)容易猜測(cè)的管理目錄名稱(chēng)以增加一重對(duì)系統(tǒng)安全的保護(hù))。
/css和/scripts -- 都是與網(wǎng)頁(yè)設(shè)計(jì)也就是View視圖/界面有關(guān)的文件存放處,分別是樣式單和客戶(hù)端腳本。這樣做的好處在任何一本講述網(wǎng)站規(guī)劃的書(shū)籍中都會(huì)有所提及。



方案二:簡(jiǎn)單網(wǎng)站項(xiàng)目
系統(tǒng)性能是這類(lèi)項(xiàng)目追求的首要目標(biāo),而與此同時(shí)系統(tǒng)的維護(hù)和擴(kuò)展幾乎可以不用多加考慮。(也許這句話(huà)聽(tīng)起來(lái)有些絕對(duì),但是根據(jù)客戶(hù)的需求和項(xiàng)目的性質(zhì)判斷,盡最大可能以最短時(shí)間滿(mǎn)足客戶(hù)的需求并使得系統(tǒng)高效運(yùn)轉(zhuǎn)就是項(xiàng)目成功的最好檢驗(yàn)標(biāo)準(zhǔn)。)因此,也許這類(lèi)項(xiàng)目就是PHP黑客的天堂(曾經(jīng)我也是一個(gè)過(guò)分追求PHP使用效率的人)。由于這類(lèi)項(xiàng)目的特殊性,這里討論的范圍不僅僅局限與系統(tǒng)設(shè)計(jì)而是從組建項(xiàng)目小組開(kāi)始直到交付項(xiàng)目的過(guò)程。

首先需要關(guān)注的是參與項(xiàng)目的人選(雖然也許這是項(xiàng)目經(jīng)理的職責(zé),但是最熟悉PHP項(xiàng)目特點(diǎn)的系統(tǒng)分析員應(yīng)該參與)。在PHP開(kāi)發(fā)人員方面,至少應(yīng)該選擇對(duì)PHP各種函數(shù)較為熟悉的開(kāi)發(fā)者(這類(lèi)項(xiàng)目不適合作為現(xiàn)實(shí)項(xiàng)目以培訓(xùn)參與的開(kāi)發(fā)新人),如果公司中還有能夠在源碼級(jí)別理解PHP的人員就更加理想(不過(guò)通常對(duì)于一般的PHP開(kāi)發(fā)公司是不可能的)。而在網(wǎng)頁(yè)設(shè)計(jì)人員方面,最好可以選擇一些略通客戶(hù)端(比如JavaScript)以及服務(wù)器端(最好是PHP)腳本的人員;因?yàn)檫@類(lèi)項(xiàng)目的一大特點(diǎn)即是單個(gè)網(wǎng)頁(yè)代碼量較大且?jiàn)A雜網(wǎng)頁(yè)代碼(通常是HTML)、客戶(hù)端腳本(比如JavaScript)和服務(wù)器端腳本(比如PHP),加入了解各種腳本語(yǔ)言的網(wǎng)頁(yè)設(shè)計(jì)人員的目的不是為了增加團(tuán)隊(duì)的PHP開(kāi)發(fā)力量,而是避免在修改網(wǎng)頁(yè)時(shí)影響程序設(shè)計(jì)人員的工作。

其次就是面向過(guò)程,準(zhǔn)確說(shuō)是面向頁(yè)面的系統(tǒng)設(shè)計(jì)。相對(duì)第一類(lèi)項(xiàng)目,客戶(hù)的需求在該類(lèi)項(xiàng)目中表現(xiàn)得非常清晰,而且一般長(zhǎng)期進(jìn)行Web開(kāi)發(fā)的公司對(duì)于這類(lèi)網(wǎng)站項(xiàng)目也應(yīng)該有一定的設(shè)計(jì)經(jīng)驗(yàn)積累。設(shè)計(jì)中需要圍繞整個(gè)系統(tǒng)的流程,包括每個(gè)頁(yè)面的輸入?yún)?shù)和輸出內(nèi)容(包括網(wǎng)頁(yè)中出現(xiàn)的除導(dǎo)航鏈接之外的功能性鏈接),以求完全滿(mǎn)足客戶(hù)的需求;另一關(guān)鍵在于確定系統(tǒng)安全策略,在這類(lèi)項(xiàng)目中主要是用戶(hù)等級(jí)的確定和頁(yè)面的訪問(wèn)權(quán)限,并給出實(shí)現(xiàn)的方式。不過(guò)還需要指出的是,這類(lèi)項(xiàng)目中由編碼階段返回設(shè)計(jì)階段的情況并不少見(jiàn),對(duì)于局部設(shè)計(jì)(比如頁(yè)面?zhèn)魅雲(yún)?shù)或者輸出鏈接)的更改應(yīng)該加以及時(shí)控制。

最后是針對(duì)代碼和數(shù)據(jù)庫(kù)的優(yōu)化。在這類(lèi)項(xiàng)目中需要適當(dāng)鼓勵(lì)開(kāi)發(fā)人員的黑客態(tài)度。推薦的辦法是系統(tǒng)分析員給出每個(gè)頁(yè)面的偽代碼(框架代碼),而局部的實(shí)現(xiàn)則由各個(gè)程序開(kāi)發(fā)人員和網(wǎng)頁(yè)設(shè)計(jì)人員進(jìn)行。

對(duì)于PHP代碼方面,通?梢詮娜缦聨追矫婵紤]:

算法的選擇和功能實(shí)現(xiàn)的方式:模塊級(jí)別的優(yōu)化,可以由幾名開(kāi)發(fā)人員共同討論解決;
函數(shù)的使用:代碼級(jí)別的優(yōu)化,需要開(kāi)發(fā)人員對(duì)各類(lèi)函數(shù)有清楚的認(rèn)識(shí),至少養(yǎng)成多多參考函數(shù)手冊(cè)的習(xí)慣;
數(shù)據(jù)庫(kù)的查詢(xún)和更改即SQL語(yǔ)句的使用:如果公司中有相關(guān)數(shù)據(jù)庫(kù)系統(tǒng)的管理人員,可以就一些優(yōu)化問(wèn)題征詢(xún)他們的建議;
其他應(yīng)該避免的問(wèn)題:比如拷貝代碼、等不良代碼情況。



而根據(jù)我的經(jīng)驗(yàn),通常會(huì)在這類(lèi)項(xiàng)目中撰寫(xiě)的黑客代碼如下:

循環(huán)語(yǔ)句的使用特別是在查找時(shí)的應(yīng)用:此時(shí)注意while和for的區(qū)別(想必大家在大學(xué)課堂中都做過(guò)這類(lèi)的程序),這也是良好的編程習(xí)慣;
SQL語(yǔ)句的優(yōu)化:首先是盡量避免多余的數(shù)據(jù)庫(kù)交互,這是提高效率非常重要的一點(diǎn);其次是不要害怕長(zhǎng)達(dá)幾行的語(yǔ)句而寧愿使用所謂簡(jiǎn)單的語(yǔ)句;再次是認(rèn)真考慮查詢(xún)語(yǔ)句返回的字段,減少不必要的數(shù)據(jù)。
表單提交值的獲取,比如復(fù)選框和文本域。精巧的表單域名稱(chēng)設(shè)計(jì)可以減少一定的代碼量,而處理提交值時(shí)也需要注意處理的方式。



黑客代碼在這類(lèi)項(xiàng)目中值得鼓勵(lì),不過(guò)最好在每段代碼旁附上盡可能詳細(xì)的注釋。

由于該類(lèi)項(xiàng)目的特殊性,完成項(xiàng)目的關(guān)鍵不僅僅在于系統(tǒng)設(shè)計(jì)階段,因此給出項(xiàng)目開(kāi)始、系統(tǒng)設(shè)計(jì)、編碼以及測(cè)試、交付這一過(guò)程的簡(jiǎn)單描述:

挑選合適人員組成項(xiàng)目小組,可以考慮銷(xiāo)售人員和客戶(hù)代表的加入。
系統(tǒng)分析員可以簡(jiǎn)單的從客戶(hù)的需求以及以往項(xiàng)目經(jīng)驗(yàn)的結(jié)合中總結(jié)出系統(tǒng)所需的每個(gè)網(wǎng)頁(yè)并對(duì)其功能作出描述,同時(shí)確定初步的安全策略。這一步驟中可以加入銷(xiāo)售人員和客戶(hù)代表的加入。(此時(shí)網(wǎng)頁(yè)設(shè)計(jì)人員正在準(zhǔn)備提供給客戶(hù)的一系列網(wǎng)站形象頁(yè)面。)
詳細(xì)設(shè)計(jì)中需要為每個(gè)頁(yè)面確定位置和名稱(chēng),更加關(guān)鍵的是確定輸入?yún)?shù)和輸出內(nèi)容以及不同級(jí)別用戶(hù)對(duì)于網(wǎng)頁(yè)的確切訪問(wèn)權(quán)限。同時(shí)進(jìn)行數(shù)據(jù)庫(kù)設(shè)計(jì)。該階段完成后至少應(yīng)該提供系統(tǒng)的流程圖(包括訪問(wèn)權(quán)限標(biāo)識(shí))以及數(shù)據(jù)庫(kù)設(shè)計(jì)資料。
網(wǎng)頁(yè)設(shè)計(jì)人員和程序開(kāi)發(fā)人員拿到相關(guān)資料各自進(jìn)行工作。對(duì)于前者,根據(jù)客戶(hù)認(rèn)可的一套形象設(shè)計(jì)每個(gè)頁(yè)面;對(duì)于后者,開(kāi)始進(jìn)行"興奮的"(因?yàn)榇藭r(shí)要求的是高效簡(jiǎn)介的代碼--黑客代碼)編碼工作。此階段工作中遇到的困難均需要反饋到系統(tǒng)分析員處,可能返回以上的第3步甚至第2步進(jìn)行設(shè)計(jì)修改。
程序編寫(xiě)和網(wǎng)頁(yè)設(shè)計(jì)結(jié)束后需要有一段整合的時(shí)間,也是程序開(kāi)發(fā)人員對(duì)代碼進(jìn)行自我測(cè)試的階段。同時(shí)在這一階段可以進(jìn)行的是代碼(包括網(wǎng)頁(yè)代碼和程序代碼)和數(shù)據(jù)庫(kù)的優(yōu)化工作。此階段結(jié)束后應(yīng)該可以提供一個(gè)完整的系統(tǒng)。
真正的測(cè)試階段通常都比較倉(cāng)促,這方面的技術(shù)和經(jīng)驗(yàn)公司也應(yīng)該有一定積累(如果有條件希望采用一些軟件工具進(jìn)行穩(wěn)定性和抗壓能力的測(cè)試)。最后是提供一個(gè)可Web訪問(wèn)的地址供客戶(hù)測(cè)試。此階段完成后可以提供正式交付客戶(hù)的系統(tǒng)。



方案三:綜合性網(wǎng)站項(xiàng)目
已經(jīng)有一些大型網(wǎng)站使用PHP作為主要的開(kāi)發(fā)語(yǔ)言。對(duì)于這類(lèi)項(xiàng)目,單純從PHP技術(shù)方面值得提出的話(huà)題不多,簡(jiǎn)而言之還是根據(jù)網(wǎng)站各部分的實(shí)際應(yīng)用情況(訪問(wèn)強(qiáng)度、操作行為等)選擇以上提出的兩種項(xiàng)目設(shè)計(jì)方法或者綜合使用。除此之外,根據(jù)我個(gè)人的經(jīng)驗(yàn),項(xiàng)目團(tuán)隊(duì)的組織和協(xié)調(diào)工作以及項(xiàng)目各期完成后的維護(hù)工作等等是較之單純的技術(shù)更加關(guān)鍵的因素。

對(duì)于這類(lèi)項(xiàng)目,可以提出的建議是,適當(dāng)采納一些開(kāi)源軟件對(duì)于快速、優(yōu)質(zhì)的完成項(xiàng)目很有好處。項(xiàng)目的某些部分可以直接引入開(kāi)源軟件項(xiàng)目的設(shè)計(jì)甚至是代碼,不過(guò)前提是系統(tǒng)設(shè)計(jì)人員對(duì)這些引入的項(xiàng)目需要非常了解,同時(shí)需要做好這些孤立的開(kāi)源項(xiàng)目和整個(gè)項(xiàng)目之間的接合(比如安全策略的考慮和全局變量的引用等)。

舉例來(lái)說(shuō),根據(jù)客戶(hù)要求某個(gè)綜合網(wǎng)站需要以下的功能:

復(fù)雜的新聞發(fā)布;
需要不多管理功能的在線論壇;
簡(jiǎn)單的產(chǎn)品陳列;
需要用戶(hù)管理。



(很明顯這是一個(gè)企業(yè)網(wǎng)站的雛形。)

其中的1、2項(xiàng)很明顯可以借用一些成熟的開(kāi)源軟件項(xiàng)目,而3項(xiàng)由于客戶(hù)需求簡(jiǎn)單自主開(kāi)發(fā)比較符合成本。由此看來(lái)4項(xiàng)則是整個(gè)系統(tǒng)中最重要的部分--需要做好與1、2項(xiàng)使用的開(kāi)源軟件項(xiàng)目的用戶(hù)管理集成工作(3項(xiàng)由于自主開(kāi)發(fā)的原因集成工作非常簡(jiǎn)單)。(某些技術(shù)積累較好的公司甚至對(duì)于以上提及的集成部分都有簡(jiǎn)單的解決方案,那么這樣一個(gè)網(wǎng)站項(xiàng)目的完成所需成本非常微小。)

幾個(gè)特殊的功能點(diǎn)
另外還有一些通常項(xiàng)目中都會(huì)出現(xiàn)但是必須妥善處理的功能點(diǎn):

1. 數(shù)據(jù)列表分頁(yè)。
關(guān)于這個(gè)功能,互聯(lián)網(wǎng)上的中文和英文資料都有許多。具體的技術(shù)和實(shí)施細(xì)節(jié)不需要多說(shuō),這里只需要指出的是:

A. 建議封裝成某一個(gè)工具類(lèi)的方法或者其他可復(fù)用的形式--這樣的好處不言自明,任何人都不希望系統(tǒng)中只要存在數(shù)據(jù)列表分頁(yè)的時(shí)候都會(huì)出現(xiàn)一堆幾乎相同的代碼。

B. 如果針對(duì)一些效率要求較高的項(xiàng)目(例如上文提到的"簡(jiǎn)單網(wǎng)站項(xiàng)目"類(lèi)型),應(yīng)該直接使用PHP自帶的針對(duì)特定數(shù)據(jù)庫(kù)系統(tǒng)的操作函數(shù)以及與該數(shù)據(jù)庫(kù)系統(tǒng)相關(guān)的結(jié)果集截取技術(shù)(SQL語(yǔ)句),比如MySQL中的'LIMIT start, offset'之類(lèi);其他一些需要系統(tǒng)設(shè)計(jì)工整合理的項(xiàng)目(例如上文提到的"設(shè)計(jì)大量商業(yè)邏輯項(xiàng)目"),如果采用了通用的數(shù)據(jù)庫(kù)接口,出于兼容多種數(shù)據(jù)庫(kù)系統(tǒng)的考慮,可以采用此接口完成結(jié)果集的篩選,以損失的效率換取系統(tǒng)更好的可維護(hù)性和可擴(kuò)展性。也就是說(shuō),對(duì)于采用特定數(shù)據(jù)庫(kù)操作函數(shù)還是第三方通用數(shù)據(jù)庫(kù)接口來(lái)實(shí)現(xiàn)數(shù)據(jù)列表分頁(yè),需要考慮系統(tǒng)的性能和擴(kuò)展兩方面因素。

2. 錯(cuò)誤控制。
這一點(diǎn)在上文之中也有提及。除了建立屬于工具類(lèi)的錯(cuò)誤類(lèi)之外,最好可以建立專(zhuān)門(mén)的錯(cuò)誤顯示頁(yè)面。該頁(yè)面既可以是靜態(tài)的HTML頁(yè)面(表達(dá)一些對(duì)用戶(hù)的歉意和出錯(cuò)之后的處理指導(dǎo))或者動(dòng)態(tài)的PHP頁(yè)面(可以包含具體的出錯(cuò)原因和地點(diǎn)以及其他更詳細(xì)的信息,前提是在系統(tǒng)安全策略允許提供這些信息)。而錯(cuò)誤類(lèi)的任務(wù)就是接受正常的程序中拋出的錯(cuò)誤,進(jìn)行必要處理之后將信息一起重定向在錯(cuò)誤顯示頁(yè)面上。



同時(shí),建立出錯(cuò)頁(yè)面對(duì)于開(kāi)發(fā)階段也有一定好處,可以彌補(bǔ)現(xiàn)有PHP缺少類(lèi)似try{…} catch{…} 塊的違例控制的缺點(diǎn),將調(diào)試中的錯(cuò)誤或者輸出通過(guò)錯(cuò)誤類(lèi)拋出并顯示出來(lái)。

3.上載與下載。
對(duì)于PHP來(lái)說(shuō),上載的實(shí)現(xiàn)并不會(huì)像其他流行的Web開(kāi)發(fā)語(yǔ)言那樣需要第三方程序的支持,內(nèi)建的機(jī)制可以非常簡(jiǎn)單的處理。不過(guò)這里提及的是一些復(fù)雜的上載功能實(shí)現(xiàn)?疾煲韵乱粋(gè)處理附加文件的流程:




該功能使得用戶(hù)在撰寫(xiě)新的消息時(shí)可以附加其他文件,而且在消息沒(méi)有提交之前可以隨意的對(duì)已經(jīng)附加的文件進(jìn)行刪除或者繼續(xù)增加。這種需求會(huì)體現(xiàn)在許多辦公相關(guān)的系統(tǒng)中,作為有經(jīng)驗(yàn)的系統(tǒng)分析員應(yīng)該在系統(tǒng)設(shè)計(jì)階段制定完成針對(duì)該類(lèi)功能的實(shí)施計(jì)劃。比如在圖中所示的流程中,其實(shí)是通過(guò)一個(gè)或者多個(gè)表單的互相提交完成(具體設(shè)計(jì)不再贅述,提供相關(guān)的PHP文件參考;另外的一個(gè)直觀的例子就是多數(shù)免費(fèi)郵件系統(tǒng)的添加附件功能,如果有興趣可以考察一下)。

至于下載,將文件置于服務(wù)器Web可訪問(wèn)目錄下、提供訪問(wèn)者真實(shí)文件路徑是最簡(jiǎn)單的解決辦法;不過(guò)一些系統(tǒng)中對(duì)文件的下載基于某些安全策略需要進(jìn)行身份方面的判別方可予以下載,這樣的方式就會(huì)帶來(lái)隱患。通常采用的方式也許是將文件放置在Web可訪問(wèn)目錄以外的服務(wù)器文件系統(tǒng)中或者存儲(chǔ)進(jìn)數(shù)據(jù)庫(kù)系統(tǒng)--都需要一個(gè)簡(jiǎn)單的程序取得文件內(nèi)容并直接返回給發(fā)出請(qǐng)求的用戶(hù)(這其中涉及到一些HTTP輸出頭的問(wèn)題請(qǐng)注意,提供一個(gè)PHP文件代替具體敘述)。在系統(tǒng)設(shè)計(jì)時(shí)針對(duì)不同的需求可以采用相應(yīng)的辦法。

4. 客戶(hù)會(huì)話(huà)session的保持
PHP的現(xiàn)有版本已經(jīng)內(nèi)置了對(duì)session的支持,通常項(xiàng)目中都使用這樣的方式;一些特殊需要的項(xiàng)目(比如分布系統(tǒng))也許會(huì)采用復(fù)雜一些的處理方式。

在客戶(hù)端,通常采用的是設(shè)置cookie以識(shí)別特定的客戶(hù),另一個(gè)可以應(yīng)付不支持cookie的客戶(hù)端的方法是采用URL重寫(xiě)加入足夠標(biāo)示特定客戶(hù)的字符串。從這方面來(lái)說(shuō),采用PHP內(nèi)置的session支持最為理想,因?yàn)樗梢宰詣?dòng)的進(jìn)行客戶(hù)端的FALLBACK:如果客戶(hù)端支持cookie,那么就順其自然;如果cookie不被支持,就采用URL重寫(xiě)方式--一切都不需要開(kāi)發(fā)者干預(yù)。如果采用其他session處理方式,或者自己編寫(xiě)適應(yīng)需要的session庫(kù),需要注意的就是怎樣處理客戶(hù)端存儲(chǔ)數(shù)據(jù)的問(wèn)題--cookie還是URL重寫(xiě)還是兩者兼顧。

在服務(wù)器端,簡(jiǎn)單說(shuō)來(lái)只需要針對(duì)以字符串標(biāo)示的每個(gè)特定客戶(hù)存儲(chǔ)相關(guān)的數(shù)據(jù)即可--可以采用的方式多種多樣,通常的方式是文件和數(shù)據(jù)庫(kù)。PHP內(nèi)置的session支持中,默認(rèn)的支持方式是在系統(tǒng)的臨時(shí)目錄或者制定的目錄下為每個(gè)客戶(hù)建立一個(gè)文件存儲(chǔ)其數(shù)據(jù);當(dāng)然也可以修改設(shè)置使其支持?jǐn)?shù)據(jù)庫(kù)的方式或者其他方式。如果自己編寫(xiě)session庫(kù),根據(jù)系統(tǒng)的需要選擇一種合適的存儲(chǔ)方式即可。值得指出的是,對(duì)于分布系統(tǒng),如何共享服務(wù)器端的session信息是需要極大關(guān)注的。

另外,在設(shè)置session變量的時(shí)候,PHP內(nèi)置的session庫(kù)支持對(duì)象作為變量值(實(shí)際上所有的變量值,不論是一般的變量還是數(shù)組或是對(duì)象,都在經(jīng)過(guò)串行化之后被存儲(chǔ)),也就是說(shuō),以下代碼是可用的:





這一點(diǎn)對(duì)于上文提到的一些商用系統(tǒng)是有益的:首先,可以使用關(guān)于用戶(hù)的對(duì)象作為一個(gè)session變量值存儲(chǔ)一套信息,而不是割裂的多個(gè)session變量;其次,如果具有類(lèi)似購(gòu)物車(chē)的功能,可以以非常符合整個(gè)系統(tǒng)設(shè)計(jì)的方式(即前文所述商用系統(tǒng)的設(shè)計(jì)方式,強(qiáng)調(diào)類(lèi)封裝)將該購(gòu)物車(chē)對(duì)象放入session中。

5. ……其他和特定項(xiàng)目有關(guān)的功能點(diǎn)……
如果能在系統(tǒng)設(shè)計(jì)階段就預(yù)見(jiàn)并解決這些功能點(diǎn)固然很好,即使有少量未發(fā)現(xiàn)的功能點(diǎn)遺留到了編碼階段也并不可怕--通常這樣的遺漏并不會(huì)影響整個(gè)系統(tǒng)的架構(gòu),只是需要返回設(shè)計(jì)階段加入相應(yīng)的內(nèi)容和文檔即可。畢竟對(duì)于相關(guān)項(xiàng)目經(jīng)驗(yàn)不太豐富的系統(tǒng)分析員來(lái)說(shuō),做到設(shè)計(jì)階段對(duì)這類(lèi)功能點(diǎn)了然于胸是不太現(xiàn)實(shí)的。

關(guān)于其他
對(duì)于PHP的爭(zhēng)論從前很多,不過(guò)自從Java在Web方面的優(yōu)勢(shì)越來(lái)越進(jìn)入人們的視野之后,這樣的爭(zhēng)論倒偃旗息鼓了--看來(lái)大家都達(dá)成了共識(shí)--PHP對(duì)于嚴(yán)謹(jǐn)?shù)纳逃孟到y(tǒng)還是無(wú)能為力。不過(guò)基于此就一味否定PHP在商用系統(tǒng)中的應(yīng)用也不大客觀,畢竟PHP還具有低成本的優(yōu)勢(shì)(這里的成本包括開(kāi)發(fā)成本、使用成本和維護(hù)成本)。本文的目的除了講述一些PHP系統(tǒng)設(shè)計(jì)的方法之外,也希望吸引一些開(kāi)發(fā)者或者企業(yè)采用PHP構(gòu)建合適的商用系統(tǒng)。

另外,本文僅僅是我自己的一些經(jīng)驗(yàn),如果您看到這里時(shí)候已經(jīng)有了自己的一些想法,我非常樂(lè)意與您分享--能夠推動(dòng)如PHP這樣無(wú)商業(yè)支持的開(kāi)源軟件的發(fā)展,畢竟是一件非常令人興奮的事情。(從這方面來(lái)說(shuō),我甚至想撰寫(xiě)關(guān)于PHP開(kāi)發(fā)的文檔資料和示范項(xiàng)目,就如同Sun Microsystems為J2EE發(fā)布的Blueprint和Java Pet Store--可惜暫時(shí)受到時(shí)間、精力以及個(gè)人能力的限制--也許春節(jié)假期是一個(gè)好時(shí)機(jī):)

參考資料

本文中提及的文章和代碼
代碼:用戶(hù)登錄實(shí)例(包括控制頁(yè)面index.php,login.php;網(wǎng)頁(yè)模板member_index.dwt,member_login.dwt;邏輯類(lèi)Member.inc.php)相關(guān)附件;
代碼:類(lèi)封裝實(shí)例(Item類(lèi)定義Item.inc.php;Item_List類(lèi)定義Item_List.inc.php)相關(guān)附件;
代碼:上載實(shí)例(控制頁(yè)面add.php,attach.php)相關(guān)附件;
代碼:下載實(shí)例(控制頁(yè)面download.php)相關(guān)附件;
代碼:實(shí)例代碼組織中的兩個(gè)全局文件(config.inc.php,security.inc.php)相關(guān)附件;
文章:關(guān)于PHP的未來(lái);
文章:模板在PHP中的使用。
其他參考資料
PHP官方網(wǎng)站--http://www.php.net
包含軟件和文檔以及使用情況等(本文成文時(shí)的最近動(dòng)向是PHP 4.1.0釋出,在某些方面有較大改進(jìn))。
為PHP提供商業(yè)支持的Zend公司--http://www.zend.com
包含PHP相關(guān)的工具和文字資料(可以尋找到一些與本文主題相關(guān)的話(huà)題)。
著名的開(kāi)放源碼項(xiàng)目網(wǎng)站SoureForge--http://www.sourceforge.net
開(kāi)放源碼項(xiàng)目的聚集地,并提供基于Web的各種便利工具(可以尋找到成千上萬(wàn)PHP撰寫(xiě)的項(xiàng)目)。