面向?qū)ο蟮乃季S方式
發(fā)表時(shí)間:2024-05-28 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]我是從學(xué)習(xí)Java編程開始接觸OOP(面向?qū)ο缶幊?,剛開始使用Java編寫程序的時(shí)候感覺很別扭,因?yàn)槲以缫粤?xí)慣用C來編寫程序,很欣賞C的簡潔性和高效性,喜歡C簡練而表達(dá)能力豐富的風(fēng)格,特別忍受不了Java運(yùn)行起來慢吞吞的速度,相對冗長的代碼,而且一個(gè)很簡單的事情,要寫好多類,一個(gè)類調(diào)用一個(gè)類,心...
我是從學(xué)習(xí)Java編程開始接觸OOP(面向?qū)ο缶幊?,剛開始使用Java編寫程序的時(shí)候感覺很別扭,因?yàn)槲以缫粤?xí)慣用C來編寫程序,很欣賞C的簡潔性和高效性,喜歡C簡練而表達(dá)能力豐富的風(fēng)格,特別忍受不了Java運(yùn)行起來慢吞吞的速度,相對冗長的代碼,而且一個(gè)很簡單的事情,要寫好多類,一個(gè)類調(diào)用一個(gè)類,心里的抵觸情緒很強(qiáng)。
我對Java的面向?qū)ο蟮奶匦宰聊チ季,自認(rèn)為有所領(lǐng)悟,也開始有意識的運(yùn)用OOP風(fēng)格來寫程序,然而還是經(jīng)常會覺得不知道應(yīng)該怎樣提煉類,面對一個(gè)具體的問題的時(shí)候,會覺得腦子里千頭萬緒的,不知道怎么下手,一不小心,又會回到原來的思路上去。
舉個(gè)例子,要發(fā)廣告郵件,廣告郵件列表存在數(shù)據(jù)庫里面。倘若用C來寫的話,一般會這樣思考,先把郵件內(nèi)容讀入,然后連接數(shù)據(jù)庫,循環(huán)取郵件地址,調(diào)用本機(jī)的qmail的sendmail命令發(fā)送。
然后考慮用Java來實(shí)現(xiàn),既然是OOP,就不能什么代碼都塞到main過程里面,于是就設(shè)計(jì)了三個(gè)類:
一個(gè)類是負(fù)責(zé)讀取數(shù)據(jù)庫,取郵件地址,調(diào)用qmail的sendmail命令發(fā)送;
一個(gè)類是讀郵件內(nèi)容,MIME編碼成HTML格式的,再加上郵件頭;
一個(gè)主類負(fù)責(zé)從命令讀參數(shù),處理命令行參數(shù),調(diào)用發(fā)email的類。
把一件工作按照功能劃分為3個(gè)模塊分別處理,每個(gè)類完成一件模塊任務(wù)。
仔細(xì)的分析一下,就會發(fā)現(xiàn)這樣的設(shè)計(jì)完全是從程序員實(shí)現(xiàn)程序功能的角度來設(shè)計(jì)的,或者說,設(shè)計(jì)類的時(shí)候,是自低向上的,從機(jī)器的角度到現(xiàn)實(shí)世界的角度來分析問題的。因此在設(shè)計(jì)的時(shí)候,就已經(jīng)把程序編程實(shí)現(xiàn)的細(xì)節(jié)都考慮進(jìn)去了,企圖從底層實(shí)現(xiàn)程序這樣的出發(fā)點(diǎn)來達(dá)到滿足現(xiàn)實(shí)世界的軟件需求的目標(biāo)。
這樣的分析方法其實(shí)是不適用于Java這樣面向?qū)ο蟮木幊陶Z言,因?yàn)椋绻挠肅語言,封裝兩個(gè)C函數(shù),都會比Java實(shí)現(xiàn)起來輕松的多,邏輯上也清楚的多。
我覺得面向?qū)ο蟮木柙谟诳紤]問題的思路是從現(xiàn)實(shí)世界的人類思維習(xí)慣出發(fā)的,只要領(lǐng)會了這一點(diǎn),就領(lǐng)會了面向?qū)ο蟮乃季S方法。
舉一個(gè)非常簡單的例子:假使現(xiàn)在需要寫一個(gè)網(wǎng)頁計(jì)數(shù)器,客戶訪問一次頁面,網(wǎng)頁計(jì)數(shù)器加1,計(jì)數(shù)器是這樣來訪問的
如:http://hostname/count.cgi?id=xxx
后臺有一個(gè)數(shù)據(jù)庫表,保存每個(gè)id(一個(gè)id對應(yīng)一個(gè)被統(tǒng)計(jì)訪問次數(shù)的頁面)的計(jì)數(shù)器當(dāng)前值,請求頁面一次,對應(yīng)id的計(jì)數(shù)器的字段加1(這里我們忽略并發(fā)更新數(shù)據(jù)庫表,出現(xiàn)的表鎖定的問題)。
如果按照一般從程序?qū)崿F(xiàn)的角度來分析,我們會這樣考慮:首先是從HTTP GET請求取到id,然后按照id查數(shù)據(jù)庫表,獲得某id對應(yīng)的訪問計(jì)數(shù)值,然后加1,更新數(shù)據(jù)庫,最后向頁面顯示訪問計(jì)數(shù)。
現(xiàn)在假設(shè)一個(gè)沒有程序設(shè)計(jì)經(jīng)驗(yàn)的人,他會怎樣來思考這個(gè)問題的呢?他會提出什么樣的需求呢?他很可能會這樣想:我需要有一個(gè)計(jì)數(shù)器,這個(gè)計(jì)數(shù)器應(yīng)該有這樣的功能,刷新一次頁面,訪問量就會加1,另外最好還有一個(gè)計(jì)數(shù)器清0的功能,當(dāng)然計(jì)數(shù)器如果有一個(gè)可以設(shè)為任意值的功能的話,我就可以作弊了。
做為一個(gè)沒有程序設(shè)計(jì)經(jīng)驗(yàn)的人來說,他完全不會想到對數(shù)據(jù)庫應(yīng)該如何操作,對于HTTP變量該如何傳遞,他考慮問題的角度就是我有什么需求,我的業(yè)務(wù)邏輯是什么,軟件應(yīng)該有什么功能。
按照這樣的思路(請注意,他的思路其實(shí)就是我們平時(shí)在生活中習(xí)慣的思維方式),我們知道需要有一個(gè)計(jì)數(shù)器類 Counter,有一個(gè)必須的和兩個(gè)可選的方法:
getCount() // 取計(jì)數(shù)器值方法
resetCounter() // 計(jì)數(shù)器清0方法
setCount() // 設(shè)計(jì)數(shù)器為相應(yīng)的值方法
把Counter類完整的定義如下:
public class Counter {
public int getCount(int id) {}
public void resetCounter(int id) {}
public void setCount(int id, int currentCount) {}
}
解決問題的框架已經(jīng)有了,來看一下如何使用Counter。 在count.cgi里面調(diào)用Counter來計(jì)數(shù),程序片斷如下:
// 這里從HTTP環(huán)境里面取id值
...
Counter myCounter = new Counter(); // 獲得計(jì)數(shù)器
int currentCount = myCounter.getCount(id); // 從計(jì)數(shù)器中取計(jì)數(shù)
// 這里向客戶瀏覽器輸出
...
程序的框架全都寫好了,剩下的就是實(shí)現(xiàn)Counter類方法里面具體的代碼了,此時(shí)才去考慮具體的程序語言實(shí)現(xiàn)的細(xì)節(jié),比如,在getCount()方法里面訪問數(shù)據(jù)庫,更新計(jì)數(shù)值。
從上面的例子中看到,面向?qū)ο蟮乃季S方法其實(shí)就是我們在現(xiàn)實(shí)生活中習(xí)慣的思維方式,是從人類考慮問題的角度出發(fā),把人類解決問題的思維方式逐步翻譯成程序能夠理解的思維方式的過程,在這個(gè)翻譯的過程中,軟件也就逐步被設(shè)計(jì)好了。
在運(yùn)用面向?qū)ο蟮乃季S方法進(jìn)行軟件設(shè)計(jì)的過程中,最容易犯的錯(cuò)誤就是開始分析的時(shí)候,就想到了程序代碼實(shí)現(xiàn)的細(xì)節(jié),因此封裝的類完全是基于程序?qū)崿F(xiàn)邏輯,而不是基于解決問題的業(yè)務(wù)邏輯。
學(xué)習(xí)JDBC編程的經(jīng)典錯(cuò)誤問法是:“我怎樣封裝對數(shù)據(jù)庫的select操作?”
面向?qū)ο蟮脑O(shè)計(jì)是基于解決業(yè)務(wù)問題的設(shè)計(jì),而不是基于具體編程技術(shù)的設(shè)計(jì)。我不會去封裝select語句的,我只封裝解決問題的業(yè)務(wù)邏輯,對數(shù)據(jù)庫的讀取是在業(yè)務(wù)邏輯的編碼實(shí)現(xiàn)階段才去考慮的問題。
回過頭看上面那個(gè)發(fā)廣告郵件的例子,應(yīng)該如何應(yīng)用面向?qū)ο蟮乃季S方法呢?
對于一個(gè)郵件來說,有郵件頭,郵件體,和郵件地址這三個(gè)屬性,發(fā)送郵件,需要一個(gè)發(fā)送的方法,另外還需要一個(gè)能把所有郵件地址列出來的方法。所以應(yīng)該如下設(shè)計(jì):
類JunkMail
屬性:
head
body
address
方法:
sendMail() // 發(fā)送郵件
listAllMail() // 列郵件地址