国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
敏捷思維: 架構(gòu)設(shè)計中的方法學(xué)(5)

敏捷思維: 架構(gòu)設(shè)計中的方法學(xué)(5)

簡單設(shè)計

級別: 初級

林星

2002 年 7 月 01 日

XP非常強調(diào)簡單的設(shè)計原則:能夠用數(shù)組實現(xiàn)的功能決不用鏈表。在其它Agile方法中,簡單的原則也被反復(fù)的強調(diào)。在這一章,我們就對簡單性做一個全面的了解。

Context

架構(gòu)應(yīng)該設(shè)計到什么程度?





Problem

軟件的架構(gòu)都是非常的復(fù)雜的,帶有大量的文檔和圖表。開發(fā)人員花在理解架構(gòu)本身上的時間甚至超出了實現(xiàn)架構(gòu)的時間。在前面的文章中,我們提到了一些反對象牙塔式架構(gòu)的一個原因,而其中的一個原因就是象牙塔式架構(gòu)的設(shè)計者往往在設(shè)計時參雜進過多的自身經(jīng)驗,而不是嚴(yán)格的按照需求來進行設(shè)計。

在軟件開發(fā)領(lǐng)域,最為常見的設(shè)計就是"Code and Fix"方式的設(shè)計,設(shè)計隨著軟件開發(fā)過程而增長。或者,我們可以認(rèn)為這種方式根本就不能算是設(shè)計,它抱著一種船到橋頭自然直的態(tài)度,可是在設(shè)計不斷改動之后,代碼變得臃腫且難以理解,到處充滿著重復(fù)的代碼。這樣的情形下,架構(gòu)的設(shè)計也就無從談起,軟件就像是在風(fēng)雨中的破屋,瀕臨倒塌。

針對于這種情形,新的設(shè)計方式又出現(xiàn)了,Martin Fowler稱這種方式為"Planned Design"。和建筑的設(shè)計類似,它強調(diào)在編碼之前進行嚴(yán)格的設(shè)計。這也就是我們在團隊設(shè)計中談到的架構(gòu)設(shè)計師的典型做法。設(shè)計師們通常不會去編程,理由是在土木工程中,你不可能看到一位設(shè)計師還要砌磚頭。

"Planned Design"較之"Code and Fix"進步了許多,但是還是會存在很多問題。除了在團隊設(shè)計中我們談的問題之外,需求變更將會導(dǎo)致更大的麻煩。因此,我們理所當(dāng)然的想到進行"彈性設(shè)計":彈性的設(shè)計能夠滿足需求的變更。而彈性的設(shè)計所付出的代價就是復(fù)雜的設(shè)計。

題外話:

這里我們談?wù)?Planned Design"引出的一些問題,并沒有任何排斥這種方式的意思。"Planned Design"還是有很多可取之處的,但也有很多需要改進的地方。事實上,本文中我們討論的架構(gòu)設(shè)計方式,本質(zhì)上也是屬于"Planned Design"方式。和"Planned Design"相對應(yīng)的方式是XP所主張的"Evolutionary Design"方式,但是這種方式還有待于實踐的檢驗,并不能簡單的說他就一定要比"Planned Design"先進或落后。但可以肯定的一點是:"Evolutionary Design"方式中有很多的思想和技巧是值得"Planned Design"借鑒的。





Solution

XP中有兩個非常響亮的口號:"Do The Simplest Thing that Could Possibly Work"和"You Aren‘t Going to Need It"(通常稱之為YAGNI)。他們的核心思想就是不要為了考慮將來,把目前并不需要的功能加到軟件中來。

粗看之下,會有很多開發(fā)人員認(rèn)為這是不切實際的口號。我能理解這種想法,其實,在我熱衷于模式、可重用組件技術(shù)的時候,我對XP提倡的簡單的口號嗤之以鼻。但在實際中,我的一些軟件因為復(fù)雜設(shè)計導(dǎo)致開發(fā)成本上升的時候,我重新思考這個問題,發(fā)現(xiàn)簡單的設(shè)計是有道理的。

降低開發(fā)的成本

不論是模式,可重用組件,或是框架技術(shù),目的都是為了降低開發(fā)的成本。但是他們的方式是先進行大量的投入,然后再節(jié)省后續(xù)的開發(fā)成本。因此,架構(gòu)設(shè)計方面的很多思路都是圍繞著這種想法展開的,這可能也是導(dǎo)致開發(fā)人員普遍認(rèn)為架構(gòu)設(shè)計高不可攀的原因。

XP的方式恰恰相反,在處理第一個問題的時候,不必要也不可能就設(shè)計出具有彈性、近乎完美的架構(gòu)來。這項工作應(yīng)該是隨著開發(fā)的演進,慢慢成熟起來的。我不敢說這種方式肯定正確,但是如果我們把生物的結(jié)構(gòu)視同為架構(gòu),這種方式不是很類似于自然界中生物的進化方式嗎?

在一開始就制作出完美的架構(gòu)的設(shè)想并沒有錯,關(guān)鍵是很難做到這一點。總是會有很多的問題是你在做設(shè)計時沒有考慮到的。這樣,當(dāng)一開始花費大量精力設(shè)計出的"完美無缺"的架構(gòu)必然會遇到意想不到的問題,這時候,復(fù)雜的架構(gòu)反而會影響到設(shè)計的改進,導(dǎo)致開發(fā)成本的上升。這就好比如果方向錯了,交通工具再快,反而導(dǎo)致錯誤的快速擴大。Martin Fowler在他的論文中說,"Working on the wrong solution early is even more wasteful than working on the right solution early"(提前做一件錯事要比提前做一件對的事更浪費時間),相信也是這個道理。

更有意思的是,通常我們更有可能做錯。在我們進行架構(gòu)設(shè)計的時候,我們不可能完全取得詳細(xì)的需求。事實上,就算你已經(jīng)取得了完整的需求,也有可能發(fā)生變化。這種情況下做出的架構(gòu)設(shè)計是不可能不出錯的。這樣,浪費大量的時間在初始階段設(shè)計不可能達(dá)到的"完美架構(gòu)",倒不如把時間花在后續(xù)的改進上。

提升溝通的效率

我們在團隊設(shè)計中已經(jīng)談過了團隊設(shè)計的目標(biāo)之一就是為了降低溝通的成本,以期讓所有人都能夠理解架構(gòu)。但是如果架構(gòu)如果過于復(fù)雜,將會重新導(dǎo)致溝通成本的上升,而且,這個成本并不會隨著項目進行而降低,反而會因為上面我們提到的遇到新的問題導(dǎo)致溝通成本的持續(xù)上升。

簡單的架構(gòu)設(shè)計可以加快開發(fā)團隊理解架構(gòu)的速度。我們可以通過兩種方式來理解簡單的含義。首先,簡單意味著問題的解不會非常的復(fù)雜,架構(gòu)是解決需求的關(guān)鍵,無論需求再怎么復(fù)雜多變,總是可以找出簡單穩(wěn)定的部分,我們可以把這個簡單穩(wěn)定的部分做為基礎(chǔ),再根據(jù)需要進行改進擴展,以解決復(fù)雜的問題。在示例中,我們提到了measurement pattern,它就是按照這種想法來進行設(shè)計的。

其次,簡單性還體現(xiàn)在表示的簡單上。一份5頁的文檔就能夠表達(dá)清楚的架構(gòu)設(shè)計為什么要花費50頁呢?同樣的道理,能夠用一副簡單的圖形就能夠表示的架構(gòu)設(shè)計也沒有必要使用文檔。畢竟,面對面的溝通才是最有效率的溝通,文檔不論如何的復(fù)雜,都不能被完全理解,而且,復(fù)雜的文檔,維護起來也需要花費大量的時間。只有在兩種情況下,我們提倡使用復(fù)雜的文檔:一是開發(fā)團隊沒有辦法做到面對面溝通;二是開發(fā)成果要作為團隊的知識積累起來,為下一次開發(fā)所用。

考慮未來

我們之所以考慮未來,主要的原因就是需求的不穩(wěn)定。因此,我們?nèi)绻紤]未來可能發(fā)生的需求變化,就會不知覺的在架構(gòu)設(shè)計中增加復(fù)雜的成分。這違背的簡單的精神。但是,如果你不考慮可能出現(xiàn)的情況,那些和目前設(shè)計格格不入的改變,將會導(dǎo)致大量的返工。

還記得YAGNI嗎?原則上,我們?nèi)匀粓猿植灰诂F(xiàn)有的系統(tǒng)中為將來可能的情況進行設(shè)計。但是,我們必須思考,必須要為將來可能出現(xiàn)的情況做一些準(zhǔn)備。其實,軟件中了不起的接口的思想,不就是源于此嗎?因此,思考未來,但等到需要時再實現(xiàn)。

變更案例有助于我們思考未來,變更案例就是你在將來可能要(或可能不要)滿足的,但現(xiàn)在不需要滿足的需求。當(dāng)我們在做架構(gòu)設(shè)計的時候,變更案例也將會成為設(shè)計的考慮因素之一,但它不可能成為進行決策的唯一考慮因素。很多的時候,我們沉迷于設(shè)計通用系統(tǒng)給我們帶來的挑戰(zhàn)之中,其實,我們所做的工作對用戶而言是毫無意義的。

架構(gòu)的穩(wěn)定

架構(gòu)簡單化和架構(gòu)的穩(wěn)定性有什么關(guān)系嗎?我們說,架構(gòu)越簡單,其穩(wěn)定性就越好。理由很簡單,1個擁有4個方法和3個屬性的類,和1個擁有20個方法和30屬性的類相比,哪一個更穩(wěn)定?當(dāng)然是前者。而架構(gòu)最終都是要映射到代碼級別上的,因此架構(gòu)的簡單將會帶來架構(gòu)的穩(wěn)定。盡可能的讓你的類小一些,盡可能的讓你的方法短一些,盡可能的讓類之間的關(guān)系少一些。這并不是我的忠告,很多的設(shè)計類的文章都是這么說的。在這個話題上,我們可以進一步的閱讀同類的文章( 關(guān)于 refactoring 的思考)。

辨正的簡單

因此,對我們來說,簡單的意義就是不要把未來的、或不需要實現(xiàn)的功能加入到目前的軟件中,相應(yīng)的架構(gòu)設(shè)計也不需要考慮這些額外的需求,只要剛好能夠滿足當(dāng)前的需求就好了。這就是簡單的定義??墒窃诂F(xiàn)實之中,總是有這樣或者那樣的原因,使得設(shè)計趨向復(fù)雜。一般來說,如果一個設(shè)計對團隊而言是有價值的,那么,付出一定的成本來研究、驗證、發(fā)展、文檔化這個設(shè)計是有意義的。反之,如果一個設(shè)計沒有很大的價值或是發(fā)展它的成本超過了其能夠提供的價值,那就不需要去考慮這個設(shè)計。

價值對不同的團隊來說具有不同的含義。有時候可能是時間,有時候可能是用戶價值,有時候可能是為了團隊的設(shè)計積累和代碼重用,有時候是為了獲得經(jīng)驗,有時候是為了研究出可重用的框架(FrameWork)。這些也可以稱為目的,因此,你在設(shè)計架構(gòu)時,請注意先確定好你的目的,對實現(xiàn)目的有幫助的事情才考慮。

Scott W.Ambler在他的文章中提到一個他親身經(jīng)歷的故事,在軟件開發(fā)的架構(gòu)設(shè)計過程中,花了很多的時間來設(shè)計數(shù)據(jù)庫到業(yè)務(wù)邏輯的映射架構(gòu),雖然這是一件任何開發(fā)人員都樂意專研的事情(因為它很酷)。但他不得不承認(rèn),對用戶來說,這種設(shè)計先進的架構(gòu)是沒有太大的意義的,因為用戶并不關(guān)心具體的技術(shù)。當(dāng)看到這個故事的時候,我的觸動很大。一個開發(fā)人員總是熱衷于新奇的技術(shù),但是如果這個新奇技術(shù)的成本由用戶來承擔(dān),是不是合理呢?雖然新技術(shù)的采用能夠為用戶帶來效益,但是沒有人計算過效益背后的成本。就我開發(fā)過的項目而言,這個成本往往是大于效益的。這個問題可能并沒有確定的答案,只能是見仁見智了。

例1.Java的IO系統(tǒng)

從Java的IO系統(tǒng)設(shè)計中,我們可以感受到簡單設(shè)計的困難。

IO系統(tǒng)設(shè)計的困難性向來是公認(rèn)的。Java的IO設(shè)計的一個目的就是使IO的使用簡單化。在Java的1.0中,Java的IO系統(tǒng)主要是把IO系統(tǒng)分為輸入輸出兩個大部分,并分別定義了抽象類InputStream和OutputStream。從這兩個的抽象類出發(fā),實現(xiàn)了一系列不同功能的輸入輸出類,同時,Java的IO系統(tǒng)還在輸入輸出中實現(xiàn)了FilterInputStream和FilterOutputStream的抽象類以及相關(guān)的一系列實現(xiàn),從而把不同的功能的輸入輸出函數(shù)連接在一起,實現(xiàn)復(fù)雜的功能。這個實現(xiàn)其實是Decorator模式(由于沒有看過源碼和相關(guān)的資料,這里僅僅是根據(jù)功能和使用技巧推測,如果大家有不同的意見,歡迎來信討論)。

因此,我們可以把多個對象疊加在一起,提供復(fù)雜的功能:

DataInpuStream in =
new DataInputStream(
new BufferedInputStream(
new FileInputStream("test.txt");

上面的代碼使用了兩個FilterInputStream:DataInpuStream和BufferedInputStream,以實現(xiàn)讀數(shù)據(jù)和緩沖的功能,同時使用了一個InputStream:FileInputStream,從文件中讀取流數(shù)據(jù)。雖然使用起來不是很方便,但是應(yīng)該還是非常清晰的設(shè)計。

令設(shè)計混亂的是既不屬于InputStream,也不屬于OutputStream的類,例如RandomAccessFile,這正表明,由于功能的復(fù)雜化,使得原先基于輸入輸出分類的設(shè)計變得混亂,根據(jù)我們的經(jīng)驗,我們說設(shè)計需要Refactoring了。因此,在Java1.1中,IO系統(tǒng)被重新設(shè)計,采用了Reader和Writer位基礎(chǔ)的設(shè)計,并增加了新的特性。但是目前的設(shè)計似乎更加混亂了,因為我們需要同時使用1.0和1.1兩種不同的IO設(shè)計。

簡單并不等于實現(xiàn)簡單

說到這里,如果大家有一個誤解,認(rèn)為一個簡單的架構(gòu)也一定是容易設(shè)計的,那就錯了。簡單的架構(gòu)并不等于實現(xiàn)起來也簡單。簡單的架構(gòu)需要設(shè)計者花費大量的心血,也要求設(shè)計者對技術(shù)有很深的造詣。在我們正在進行的一個項目中,一開始設(shè)計的基礎(chǔ)架構(gòu)在實現(xiàn)中被修改了幾次,但每修改一次,代碼量都減少一分,代碼的可讀性也就增強一分。從心理的角度上來說,對自己的架構(gòu)進行不斷的修改,確實是需要一定的勇氣的。因為不論是設(shè)計還是代碼,都是開發(fā)人員的心血。但跨出這一步是值得的。

右側(cè)的例子討論了Java的IO設(shè)計,Java類庫的設(shè)計應(yīng)該來說是非常優(yōu)秀的,但是仍然避免不了重新的修改。實際上,在軟件開發(fā)領(lǐng)域,由于原先的設(shè)計失誤而導(dǎo)致后來設(shè)計過于復(fù)雜的情況比比皆是(例如微軟的OLE)。同樣的,我們在設(shè)計軟件的時候,也需要對設(shè)計進行不斷的修改。能夠?qū)崿F(xiàn)復(fù)雜功能,同時自身又簡單的設(shè)計并不是一件容易的事情。

簡單設(shè)計需要什么樣的設(shè)計師

簡單的架構(gòu)需要全面的設(shè)計師。什么才是全面的設(shè)計師,我的定義是既能夠設(shè)計,又能夠編碼。我們在團隊設(shè)計模式中就已經(jīng)談過象牙塔式架構(gòu)和象牙塔式架構(gòu)設(shè)計師。他們最容易犯的一個毛病就是設(shè)計和代碼的脫離。從我們自己的經(jīng)驗來看,即使在設(shè)計階段考慮的非常完美的架構(gòu),在編碼階段也會出現(xiàn)這樣或那樣的問題。從而導(dǎo)致架構(gòu)實現(xiàn)變得復(fù)雜。最明顯的特征就是在編碼時出現(xiàn)了有大量方法的類,或是方法很長的類。這表明架構(gòu)和代碼脫鉤了。在我們的開發(fā)過程中,不只一次出現(xiàn)這種現(xiàn)象,或者說,出現(xiàn)了壞味道(Bad Smell)。Refactoring的技巧也同樣有助于識別壞味道。

一次的架構(gòu)設(shè)計完成后,開發(fā)人員可以按照設(shè)計,快速的編程??稍谝欢螘r間之后,新的特色不斷的加入,我們發(fā)現(xiàn)代碼開始混亂,代碼量增大,可讀性下降,調(diào)試變得困難,代碼不可控制的征兆開始出現(xiàn)。我們就知道,架構(gòu)的設(shè)計需要調(diào)整了。這屬于我們在后面所提到的Refactoring模式。而我們在這里要說的是,如果架構(gòu)的設(shè)計師不參與編碼,它是無法感受到壞味道的,因此也就不會主動的對設(shè)計進行改進。要解決這個問題,最好的辦法是讓設(shè)計師參與代碼的編寫,尤其是重要架構(gòu)的現(xiàn)實部分需要設(shè)計師的參與。如果設(shè)計師沒有辦法參與編碼,那就需要一種機制,能夠把代碼反饋給設(shè)計師,讓他在適當(dāng)?shù)臅r候,重新考慮改進架構(gòu)。一個可能的辦法是Code Review。讓設(shè)計師審核代碼,以確保編碼者真正了解了架構(gòu)設(shè)計的意圖。

例2. measurement pattern

在分析模式一書中有一個measurement pattern(測量模式),原來它是為了要解決現(xiàn)實中各種各樣紛繁復(fù)雜的可測量的屬性。例如,一個醫(yī)療系統(tǒng)中,可能會有身高多高,體重多種,血壓多少等上千種可測量的屬性。如果分別表示它們,必然導(dǎo)致系統(tǒng)復(fù)雜性的上升。因此measurement pattern就從這些屬性的可測量的共性出發(fā),研究新的解決方法,提出了measurement pattern的想法:

如圖所示,把可測量的屬性(Measurement)做為Phenomenon Type的實例,此外,每一個的Person可以擁有多個的Measurement,同時,Measurement還對應(yīng)處理的屬性,例如圖中的Quantity,就表示了Measurement的數(shù)量和單位。比如,一個人的體重是65公斤,那么,Phenomenon Type就是體重,Quantity的amount是65,units是公斤。


圖 5.牋 measurement pattern 的類圖

這其實是一個很簡單的設(shè)計,但它清楚的表示了屬性之間的關(guān)系,簡化了數(shù)千種的屬性帶來的復(fù)雜性。此外,我們進一步思考,就會發(fā)現(xiàn),這種架構(gòu)只是針對目前出現(xiàn)屬性眾多的問題的基本解決方法,它還可以根據(jù)具體的需要進行擴展,例如,實現(xiàn)動態(tài)添加單位,或?qū)崿F(xiàn)不同單位的轉(zhuǎn)化等問題。

因此,我們這里展示的其實是一種思考的方法,假想一下,當(dāng)你在面對一個復(fù)雜的醫(yī)療系統(tǒng)時,大量的屬性和不同的處理方式,你是不是可以從這樣復(fù)雜的需求中找出簡單的部分來呢?在我們架構(gòu)設(shè)計的第一篇中,我們談到架構(gòu)設(shè)計的本質(zhì)在于抽象,這里例子就是最典型的一個例子,在我們傳統(tǒng)的想法中,我們都會把身高、體重等概念做為屬性或是類,但是為了滿足這里的需求,我們對這些具體的概念做一個抽象,提出可測量類別的概念,并把它設(shè)計為類(Phenomenon Type),而把具體的概念做為實例。這種抽象的思想在軟件設(shè)計中無處不在,例如元類的概念。

更深入的理解

下一章中我們將會討論迭代設(shè)計,其中還會涉及到簡單設(shè)計的相關(guān)知識。建議可以將兩章的內(nèi)容結(jié)合起來看。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
框架模塊設(shè)計經(jīng)驗總結(jié)
關(guān)于專用處理器設(shè)計,這篇說全了
敏捷思維: 架構(gòu)設(shè)計中的方法學(xué)(1)
microdot - 一個開源 .NET 微服務(wù)框架
預(yù)約系統(tǒng)的設(shè)計與實現(xiàn)心得怎么寫
福利到!Rafy(原OEA)領(lǐng)域?qū)嶓w框架 2.22.2067 發(fā)布!
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服