1. 程式人生 > >吐槽 依賴倒置原則/DIP

吐槽 依賴倒置原則/DIP

返回

正文(這個比較難寫,不能夠喜怒笑罵)

本文吐槽 依賴倒置原則(Dependency Inversion Principle、DIP)。

即使yqj2065清楚地知道Robert C. Martin想說的東西,這麼胡言亂語的神經病的東西,要逐一地指出其問題,差一點 把我都搞得神經病了。如果只有一點問題,容易明確地指出來;當一大堆似是而非的問題攪合在一起,可能沒有人願意花時間指出來。錯誤太多,我都不知道該怎樣批駁他了。或者說,錯誤太多的論文,我都有點佩服他了。該論文有資格成為電腦科學史上著名的反例。因為他,後人會覺得不可思議並嘲笑,那個時代的程式設計師(不僅僅說他)怎麼這樣愚蠢

1.幾點說明

1)Robert C. Martin的那篇DIP論文(於1996在一個專欄上發表),可以看成他學習

《設計模式》的學習筆記,就像CSDN上大量的部落格一樣,這種東西,有再多的問題都是正常的。我的部落格,有時候也是想到什麼說什麼,管它邏輯不邏輯。他的論文有錯誤,說明他沒有學習/研究清楚。他希望將針對介面程式設計控制反轉納入一個原則即DIP中,因此形成了一個思路混亂的、不知所云的原則。

2)或許DIP最早的翻譯文章就是我帖出來的,雖然覺得該文章含糊,當時沒有太多人提到DIP,我也就將它等同“針對介面程式設計”。但是,該原則被收入到Robert C. Martin的著作《敏捷軟體開發》中、國內外有大量基於DIP的文章和介紹DIP的書籍,這時就需要好好看一看他寫的是什麼東西了。yqj2065很驚訝,他這麼胡言亂語的東西怎麼就有人相信。所以,我們不妨帶著戲謔的眼神,看看介紹過依賴倒置原則的文章和書籍——特別是一些介紹設計模式的書籍(因此,我通常不推薦大家看垃圾的、報DIP大腿的設計模式方面的書)

。如果有寫過類似文章或書籍的作者路過,請自帶口罩遮面。

3)之所以將DIP拎出來批評,因為它對學習者帶來了困擾【DIP對軟體設計的學習造成的障礙越來越突出(隨便舉一個例子,《設計模式之禪》依賴倒置原則之問)[2];特別是依賴倒置、控制反轉和依賴注入攪合在一起,成為“那些年搞不懂的高深術語”[3]】,因此,yqj2065需要明確地告訴大家:Robert C. Martin的那篇論文是垃圾

4)我的學生,我希望你們能夠從該論文中列舉10個錯誤。(不要文字/語文上的錯誤)。

2.DIP是垃圾

Robert C. Martin於1996在一個專欄上發表的依賴倒置原則(Dependency Inversion Principle、DIP) ,是一個不知所云的、漏洞百出的原則。原因在於, Martin,至少在當時,他不知道自己在說什麼。用中國話形容:以其昏昏 使人昭昭。

他希望將針對介面程式設計和控制反轉納入一個原則中,但這兩個概念(暫且不管他懂不懂)不能夠用一個東西/DIP來裝。一個驢、一個馬,你非要用一個名詞描述,就會驢頭不對馬嘴。

在閱讀本文之前,你需要做下面的實驗,要求走如下5步。

實驗1.框架設計者 

  1. SortorTest->BubbleSort。客戶依賴具體的服務BubbleSort,地球人都知道這不好。
  2. SortorTest->IntSort<=BubbleSort。應用程式SortorTest遵循針對介面程式設計/抽象依賴原則,依賴抽象型別IntSort,而BubbleSort自然地依賴父型別。
  3. 【SortorTest->IntSort】。這一步是關鍵。如果需要,將控制模組SortorTest設計成框架,可以將控制模組SortorTest和它必須依賴的抽象型別IntSort打包。控制模組SortorTest從應用程式(上層模組)變為框架(下層模組),為控制反轉(Inversion of Control)。最好能夠提供JavaDoc,並且將方法名sort 改成soooooort。
  4. Main->【SortorTest->IntSort】<=BubbleSort。(其他程式設計師)使用框架。
  5. 帶main的BubbleSort,BubbleSort->【SortorTest ->IntSort】<=BubbleSort。與第一步“依賴倒置”。

3.DIP的錯誤

(1)起點就錯了。

論文中寫到為什麼我要使用單詞"倒置"(“inversion.。坦白地說,這是因為比較傳統的軟體開發方法——例如結構化分析和設計,傾向於建立這樣的軟體結構:高層模組依賴於低層模組,並且抽象依賴細節。的確,這些方法的一個目標在於定義一個子程式層次以描述高層模組如何呼叫低層模組,....。因此,一個設計良好的面向物件程式的依賴結構,對應於傳統的過程式方法通常會形成的依賴結構,是"倒置"

這暴露了該論文的立論基礎已經錯了。傳統的過程式方法可以設計被調的函式庫,也可以設計框架;設計良好的面向物件程式,可以設計龐大的工具箱類庫,也可以設計更多的框架。在設計工具箱時,過程式和麵向物件依賴結構一樣;在設計框架時,過程式和麵向物件依賴結構一樣。如果有人把女人中的好人和男人中的壞人加以比較,得到結論:女人與男人在人性上是倒置的,顯然是極其荒謬的。在他眼裡,設計良好的面向物件程式都是框架,而傳統的過程式程式都是被調的函式庫。可見,其論文的"倒置",從開始就是錯誤的。

框架=控制反轉(Inversion of Control),框架設計,或者說Inversion,與系統是不是採用“傳統的軟體開發方法”、“過程式方法”、“面向物件”,一點關係都沒有。其論文後面將錯就錯,再怎樣解釋,只會使人不解。正因為DIP所言的倒置,本身就是胡扯,因此其鼓吹者不得不隨意發揮,如《HeadFirst 設計模式》對依賴倒置的解說甚至是“倒置你的思考方式”。

(2) 含混的高層-低層模組(High-low level modules)

在介紹針對介面程式設計/抽象依賴原則時,不管採用什麼術語,即不管是高層模組、控制模組或客戶/Client,依賴者都不應該依賴“具體的”低層模組、步驟模組或Server,而應該依賴抽象型別IServer。使用控制模組通常意味著在框架場合討論問題;而不論在分層場合,同層中或應用程式都使用C-S來進行一般性的討論。

Robert C. Martin使用高層-低層模組,為什麼他既不願意將高層-低層模組等價於C-S,也不願意使用分層架構中含義清晰的上層-下層?這種含義不清晰的術語最適合渾水摸魚。

(3)

劃線下面的文字,你湊合著看。

------------------------------------------------------------------------

 3.對DIP原文的解讀,僅作為[2.1.5 拋棄依賴倒置原則]的素材資料。

本文主旨:

1.既然有program to an interface ,那我們為什麼要這個花裡胡哨、不好理解的DIP?

2.DIP到底說了什麼(為什麼說Robert C. Martin的那篇論文是垃圾)

很多程式設計的初學者困惑於“Java的介面有什麼用”? 因此本章介紹軟體設計的最基礎的原則,依賴於抽象型別原則簡稱為抽象依賴原則[1]。

抽象依賴原則是開放封閉原則的主要內容,是“針對介面程式設計”的一種直觀的說法。yqj2065將它作為軟體設計的一個最為基礎的原則,並將它寫入給大學一年級學生使用的教材中。

一個最基本的原則,必須能夠被讀者簡單明瞭地理解和接受。很多人和書籍討論了Robert C. Martin於1996在一個專欄上發表的依賴倒置原則(Dependency Inversion Principle、DIP) ,該原則被收入到Robert C. Martin的著作《敏捷軟體開發》中。一直以來,筆者雖然對該原則的表述存在疑慮,但簡單地將DIP和針對介面程式設計等同看待。

本節明確提出拋棄依賴倒置原則,基於兩點考慮。①如果DIP等於針對介面程式設計,則它是多餘的、對軟體設計的學習造成障礙的原則;②實際上,DIP是不知所云的、漏洞百出的原則。


看起來很厲害

1.五步說明依賴如何倒置

回顧前面幾節的所做的事情。


這5個步驟中,既有體現抽象依賴原則、進行框架設計的重要內容,也有毫無意義的最後一步:Java程式設計師習慣在具體類中都帶一個main。

的確,從第一步到第五步,SortorTest->BubbleSort變成了BubbleSort->【SortorTest】,依賴關係倒置了。需要為毫無意義的最後一步提出一個設計原則嗎?
事實上,把上面的例子走一遍我們都不需要論證DIP多餘、垃圾....如果有人在第2步,就說依賴關係倒置了,那我們就得看看他如何自圓其說——即DIP的論文。

2.DIP = = program to an interface ?

Robert C. Martin提出的DIP,包含3點內容:

1.        依賴倒置原則、Dependency Inversion Principle。原則的名字

2.        A:高層模組不應該依賴低層模組,兩個都應該依賴抽象。High level modules should not depend upon low level modules, both shoulddepend upon abstractions.

3.        B:抽象不應該依賴細節,細節應該依賴抽象。Abstractions should not depend upon details, details should depend uponabstractions.


大量的書籍、文章將DIP作為針對介面程式設計/抽象依賴原則的花裡胡哨又令人費解的同義詞。此時,對於DIP的3點內容最好的做法是拒絕介紹或解釋,然而這可能嗎?於是出現了各種各樣的DIP的解釋。

“依賴倒置”?很多設計的初學者會問:“哪裡體現依賴的倒置”?而《Head First 設計模式》的解答甚至是“倒置你的思考方式”,能不能再奇葩一點,倒置人的世界觀啊。依賴倒置就是從A依賴B,變成B依賴A。是的,就這麼簡單。

但是,Robert C. Martin不會認為他的原則如此腦殘。【“打包並使用SortorTest和介面IntSort ”,做過這個實驗題的同學可以給出演示程式碼。但是當演示程式碼擺在面前,即使是既無胸又無腦的白痴也不會為它提出一個依賴倒置原則,不就是從SortorTest->BubbleSort變成BubbleSort->SortorTest。Java程式設計師習慣在具體類中都帶一個main,你為帶main的BubbleSort提出一個原則?哪有這樣腦殘的原則。所以,能夠簡單地加以說明的真正的依賴倒置,就死活不敢出來見人。】

以其昏昏使人昭昭。扯淡的要訣就是使用一大堆含義不明確、或者其他使用環境中需要的術語。或者往哲學上扯


關於規則A,不管是高層模組、控制模組或客戶,依賴者都不應該依賴具體的低層模組、步驟模組或Server,而依賴抽象型別IServer。這一點上,規則A只是抽象依賴原則的特例,因為C-S結構不侷限於分層場合,同層中或應用程式都使用C-S來進行一般性的討論。既然Robert C. Martin使用High -low level modules,顯然他希望牽扯到第三步——控制反轉(Inversion of Control)。但是這裡就暴露了DIP的一個致命問題:低層模組為什麼被預設是具體型別,低層模組(具體類和它們的抽象型別)為什麼不能夠被設計成工具箱?

換言之,DIP是否只考慮需要將控制模組設計成框架的場合?那麼對於Java、C++的I/O庫,資料結構的容器庫、Spring依賴注入庫等等,是否不包含在DIP的應用範圍?【或者說,DIP只會一條腿走路?Copy的例子是典型的自打耳光——你有本事讓全世界的程式設計師不要使用Java、C++的I/O庫,讓Java、C++的I/O依賴你的Copy框架。】

規則B,將它置於程式語言中,如同一個毫無價值的廢話,如果有人說“抽象型別IServer不應該依賴子型別Server,Server應該依賴IServer”,聽眾的第一反應這是“廢話”;第二反應,IServer什麼時候依賴過子型別(罕見的現象:父型別依賴子型別,顯然,它不會影響到一般性原則的討論);第三反應會是“大師,你到底想說什麼?”。其實,這個看似廢話的假話,不過是為了他的“倒置”增加支撐/依據。

不管怎樣,我們可以非常善良、或者說寬容地把DIP=面向介面程式設計,接受這個花裡胡哨的裝逼的原則。如果它們規則A作為主體介紹,將DIP解讀成針對介面程式設計/抽象依賴原則,我能夠接受

DIP文章解讀

抽象依賴原則中,

★客戶Client不應該依賴具體類Server,應該依賴抽象型別IServer。
★(顯然地,)子型別依賴父型別。父型別應該是抽象型別。

Robert C. Martin使用High -low level modules,我們將DIP解讀成針對介面程式設計/抽象依賴原則,或者說將高層模組-低層模組換算成Client-Server真的是對DIP的誤解。這一點暫時按下不表。

結論:

原則的名字:依賴倒置原則、Dependency Inversion Principle。得多麼腦殘才會為第一步與第五步的不同,得出一個依賴倒置原則。不知道有沒有人發現,BubbleSort依賴SortorTest本身是違反依賴倒置原則的,BubbleSort應該依賴ITest(能不能再腦殘一些呢?)。

規則A:高層模組/控制模組,不應該依賴具體的底層模組。如果底層模組能夠設計成工具箱,高層模組依賴工具箱中的抽象型別;如果高層模組需要設計成框架,高層模組和抽象型別構成框架。

規則B:假話需要更多的假話來圓場。

下面解讀其論文。

閱讀之前,你手頭必須有DIP原文。令人奇怪的,原文的連結居然找不到了。

  • 如果你將DIP作為面向介面程式設計的花裡胡哨、不好理解的版本,不需要浪費時間繼續閱讀本文

, 

如果我們帶著挑剔的眼光再次閱讀DIP,可能會駭然發現,Robert C. Martin的這篇論文漏洞/ 花樣百出

  • 如果你想知道DIP到底想說什麼,為什麼DIP是Robert C. Martin 自我陶醉的胡扯,你必須真正懂得什麼是IoC。

【下面將和大家一起解讀DIP,yqj2065在本部落格中就釋出過The Dependency Inversion Principle(翻譯)(2005-01-28 00:22)。為了正本清源,yqj2065打起精神重新讀一下,並將明確提出拋棄依賴倒置原則。

很多人將這樣的東西作為金科玉律,yqj2065必須告訴這些人:該論文真的很垃圾。】

DIP = program to an interface ?

【示例: Copy程式】中,Copy模組呼叫"Read Keyboard"等兩個模組,由於"Read Keyboard"等是具體模組,因此Copy模組複用性差。在我上課的例子中,就是SortorTest-具體類BubbleSort的關係。這時,你應該明白,下面我們將應用抽象依賴原則、"program to an interface, not an implementation".

【依賴倒置】中,的確也是這樣做的。SortorTest依賴介面IntSort,而BubbleSort依賴介面IntSort。SortorTest->IntSort<=BubbleSort.

但是,Robert C. Martin使用High -low level modules,"Copy()模組,它包含高層策略,依賴於它控制的底層細節性模組"........."然而Copy類根本不依賴"Keyboard Reader"和"Printer Writer",因此依賴性被反轉了,Copy類和細節的readers、writers都依賴於相同的抽象"。這裡,不管你懂不懂哪裡體現依賴性被反轉了,反正他就這樣說了。

【依賴倒置原則】通過上面的敘述,“”現在可以陳述依賴倒置的一般形式了"

依賴倒置原理、DIP:
1. High level modules should not depend upon low level modules, both should depend upon abstractions.
2. Abstractions should not depend upon details, details should depend upon abstractions.

直到這裡,不看後面(即使看了後面,很多人都會忽略後面的東西),除了我們不太滿意沒有采用我們容易理解的Client-Server,而是使用High -low level modules、不太理解"倒置"外,我們將DIP和面向介面程式設計等同看待。或者用yqj2065的話:

★抽象依賴原則:為了應對需求變化,程式碼中要儘可能地依賴抽象型別,而非具體類。

一直以來,yqj2065就是這樣對待DIP的,而且你上網搜尋"依賴倒置原則",99%的網站、文章裡面也是這樣介紹DIP的。

讓我們捋一捋思路:既然我們有“針對介面程式設計”,為什麼需要這個花裡胡哨、不好理解的DIP呢?這個DIP應該有它獨特的東西才合理,否則沒有存在的必要。這些疑慮我們先埋在心裡。

按照讀到這裡yqj2065的膚淺理解(或者說,DIP=面向介面程式設計這種包括我在內的大眾的膚淺理解),依賴倒置原則除了花裡胡哨的裝逼外,也存在一些問題:

  1. 高層模組和底層模組,既然是程式設計師形容模組價值或某種地位的說法,你憑什麼將底層模組預設為具體型別?JDK中工具庫(非框架)中同樣有大量的抽象型別。你的A原則,是不是要改成“”高層模組不應當依賴具體的底層模組“”,當然,你越改,越顯得=面向介面程式設計,越沒有存在的必要;
  2. 抽象屬於高層模組還是底層模組?這是要害!依賴抽象型別原則不討論這一點,而DIP沒有說明它,而在這篇文章之外,DIP就憑這一點繼續蒙人
  3. “抽象不應該依賴細節。細節應該依賴抽象”,你到底想說什麼?如果抽象-細節是IServer和Server,IServer不可能依賴Server,如同"水不應該向上流",水從來就沒有向上流過!

不管怎樣,大眾非常善良、或者說寬容地把DIP=面向介面程式設計,接受這個花裡胡哨的裝逼的原則。後面yqj2065繼續和你一起閱讀這篇文章,來證明這篇文章為什麼垃圾。

依賴倒置是東施效顰的倒置/反轉

“為什麼我要使用單詞"倒置"(“inversion”.)。坦白地說,這是因為比較傳統的軟體開發方法——例如結構化分析和設計,傾向於建立這樣的軟體結構:高層模組依賴於低層模組,並且抽象依賴細節。的確,這些方法的一個目標在於定義一個子程式層次以描述高層模組如何呼叫低層模組,圖1是一個這樣層次的好例子。因此,一個設計良好的面向物件程式的依賴結構,對應於傳統的過程式方法通常會形成的依賴結構,是"倒置"的。”

DIP文章最垃圾之處,在於東施效顰地借用了控制反轉(Inversion of Control)中的Inversion

控制反轉指控制模組由上層搬到了下層,上課的例子中SortorTest有兩種用法。

(1)在底層包util中或本包有BubbleSort等,而學生編寫SortorTest(包含main)屬於上層或同層模組。上層模組必然依賴下層模組,如同你的app依賴JDK一樣。(2)如果SortorTest是老師編寫的下層模組,要求你編寫屬於上層模組等BubbleSort,並且要求你使用SortorTest進行測試,這個SortorTest和介面IntSort 構成框架。為了體會控制反轉或者說設計框架,實驗題就有打包並使用SortorTest和介面IntSort 。

Robert C. Martin 泛泛而談地把“過程式方法”與“面向物件”依賴結構稱為"倒置",給人一種你想駁斥又抓不到其痛點的困境。我們不談“過程式方法”與“面向物件”是不是"倒置",就算你錯了我們也不管。

但是我們強調一點,你能夠用Java、也能夠用C設計框架,Java通過多型,C通過函式指標。框架=控制反轉(Inversion of Control)。框架設計中,Inversion 與“傳統的軟體開發方法”、“過程式方法”、“面向物件”一點關係都沒有。是的,我們先堵住這篇文章狡辯的退路。

"想想高層模組依賴於低層模組的寓意。...是高層模組應該優於低層模組。...當高層模組不依賴低層模組時,高層模組可以被十分簡單複用。.
正是這個原則,它是框架設計(framework design.)的核心。"

看見沒有,

框架=控制反轉(Inversion of Control),DIP是框架設計(framework design.)的核心。
1996的Robert C. Martin ,把含義清晰的控制模組的Inversion ,變成他的Dependency Inversion。當你考慮分層結構的依賴關係時,因為上層模組必然依賴下層模組,你又糊塗了,依賴怎樣Inversion。

【分層(Layering)】既然該文偷用了控制反轉(Inversion of Control)中的Inversion ,他為什麼敢討論分層?他為什麼要討論分層?

因為按照針對抽象(型別)程式設計,Client->Server變成Client->IServer<=Server,在一個包或層中,怎樣都不好說明倒置。如果A依賴於B,那麼改成B依賴於A,這可以叫倒置,這裡Client->Server變成Client->IServer,怎樣倒?

於是,他討論分層。分層中倒置沒有( 不問 什麼東西倒置沒有)倒置/反轉了!!!因為 框架=控制反轉(Inversion of Control)。


高層類 "Policy" 使用了低層類 "Mechanism","Mechanism" 使用了更細粒度的 "Utility" 類。

按照框架來設計,就是


這就是框架設計中基本的控制反轉(Inversion of Control),上機實驗打包的SortorTest和介面IntSort ,分別對應Policy Layer和Mechanism interface,使用框架時,上層程式設計師需要按照IntSort 編寫各種排序程式碼。

這就再次證明,依賴倒置是東施效顰的倒置/反轉,其實他的意思就是控制反轉。然而,他技巧性的使用了高層、低層,沒有使用分層架構中含義清晰的上層和下層,於是他的解讀就成為了高層(下層)Policy Layer和Mechanism interface不再依賴低層(上層)的Mechanism Layer,而是低層依賴高層!(注意,雖然Mechanism 不依賴Policy,但是按照分層的依賴原則,分層的依賴是以整個層為依賴單元的,因此,他這樣解讀不算錯誤。)

通過各種技巧,搞出來一個“依賴倒置”。如果你將“依賴倒置”的Inversion ,按照控制反轉(Inversion of Control)的Inversion 理解,一切ok。否則各種不適應。

抽象-細節

【C++中的介面與實現相分離】他意識到“不管怎麼說,Policy層僅僅依賴於Mechanism層的介面。Mechanism層的實現(implementation)的變化怎麼會向上最終影響到Policy層呢?在一些面嚮物件語言中,這可能是真的。...”

於是,開始借語言的不同來把水攪渾。前面我們不是困惑 “抽象不應該依賴細節。細節應該依賴抽象”到底什麼意思嗎。看他如何把水攪渾。如果抽象-細節是IServer和Server的關係,水怎樣都攪不渾。但是,

1.-細抽象節就是抽象-細節,

2.“C++中,實現並不是自動與介面相分離的”。

3.再次給一個Client-Server級別的例子,具體Server(他的Lamp)"無論Lamp類何時改變,Button類就必須改變,或者至少要重新編譯。"

[附:關於介面與實現相分離,這篇文章是混亂的.我不在這裡討論。簡單地說,即使Client依賴具體的Server,Server程式碼的實現有變化只要方法的介面不變,也不影響Client。]

。“應用的高層(policy)沒有與低層模組相分離;抽象沒有與細節相分離。沒有這樣的分離,高層自動的依賴於低層模組,並且抽象也自動的依賴於細節”。

好了,你看到這裡,你會感到“抽象也自動的依賴於細節”好像有道理,你忘記了,其實是Server和自己沒有分離,Server自動的依賴於Server。

4.“為了與依賴倒置原則相符,我們必須把這種抽象與問題的細節相隔離。因而我們必須指導依賴性的設計,讓細節依賴於抽象”。

到這裡,一個基本的“子型別依賴父型別”,被他東攪西攪,成為了一個原則B。

那麼,他到底到底想說什麼?

仔細看“抽象不應該依賴細節。細節應該依賴抽象”。令抽象=A,細節=B,這個話就是“A不應該依賴B,B應該依賴A”,看見沒有,多麼完美的依賴倒置

如果你直接說子型別依賴父型別,那他還玩個屁啊。這就是原則B的真正含義,一個毫無價值的廢話,都能夠包裝成令很多人腦洞大開的原則

我前面說了,這篇論文可以看成他學習《設計模式》的探索式筆記,有再多的問題都是正常的。問題是,在《敏捷軟體開發》又玩新花樣。

some important discussion on the concept of a package and interface ownership which are key to distinguishing this principle from the more general advise to "program to an interface, not an implementation" found within the book Design Patterns (Gamma, et. al).

這些東西我懶得窮追猛打了,前面的“這是要害”點明瞭。

同學們只要知道,在你做過的框架實驗中 ,其包的關係是【SortorTest->IntSort】<=BubbleSort,你顯然不會設計出SortorTest->【IntSort<=BubbleSort】這種不可能的東西。我們討論不可能的東西,還不如看幾部鬼片。

返回

[1]本節改編於《程式設計導論(Java)·4.4依賴於抽象型別》】

[2]該文寫到:"依賴倒置原則可以說是六大設計原則中比較難理解的”、"對於“倒置”秦小波老師是從人的思維層面解讀的"。我沒有看秦小波的書,對於依賴倒置中“倒置”的任何解讀,都是徒勞的。太多人在DIP上自由發揮。

[3]《道法自然——面向物件實踐指南》一書的作者寫的一篇文章《向依賴關係宣戰——依賴倒置、控制反轉和依賴注入辨析》【知網上有,csdn上找到一篇轉載的】,完全與我的說法不在一個宇宙空間中,這種將3個自己沒有搞清楚的概念混為一談的文章,我連噴它的興趣都沒有。

[4]Robert C. Martin (Uncle Bob).Agile Software Development Principles,Patterns,and Practices(敏捷軟體開發、3P) .Pearson Education.在《程式設計導論(Java)》中我將它列為推薦讀物