設計模式與XP
概述
模式和極端程式設計(XP )都為軟體設計、開發者提供了無法用金錢衡量的幫助。但是迄今為止XP 大量關注於重構(refactoring ),而對模式隻字不提。在這篇文章中,我問“為什麼”,並且最終描述出模式怎樣以XP 的方式更好地實現、以及XP 怎樣因為包含對模式的使用而變得更好。
非常感謝Kent Beck 、Martin Fowler 和Ward Cunningham ,他們為這篇文章提出了友善的評論。
仍在所知不多的時候我們就開始了自己的程式設計生涯,生產出的軟體也反映出了我們的缺乏經驗:
我們建立的程式碼臃腫、錯誤百出、脆弱、難以維護、難以擴充套件。隨著時間的流逝,我們成為了更好的軟體設計者:我們從技術作家、專家那裡學習,我們從自己的錯誤中學習。現在我們編寫具有高度靈活性的軟體,它適應廣泛而且堅固。當被請求編寫一個新的系統時,我們知道查明當前和將來的需求,這樣我們可以設計軟體來處理當前和將來的需要。
在軟體開發生涯的這個階段,極端程式設計告訴我們,我們經常對軟體過分設計(over-engineer )了。我 們從自己的錯誤中學到了太多,我們不希望重複這些錯誤,所以我們在系統生命週期的早期做了大量的努力來創造靈活而堅固的設計。不幸的是,我們沒有認識到:如果這個系統永遠不需要這個程度的靈活性和 堅固性,那麼我們所有的工作就都沒有意義了。我們過分設計了。
我也曾經過分設計過。說實話,與其他設計者坐在一間房間裡考慮如何設計軟體來適應許多當前和將來的需求,這的確是一種樂趣。我們把自己學到的所有東西——尤其是那些最好的經驗——應用在設計中。
我們常常知道需求的列表會改變,但使用者或客戶總是改變需求。不過,我們認為我們可以足夠聰明地設計軟體,使軟體足夠靈活,使它能應付所有的需求變化。
典型的過分設計。
今天,極端程式設計將告訴你這是多麼愚蠢的做法。XP 說,我們必須讓設計自己顯現出來,而不是去預測設計將是什麼樣子。XP 說,“做可能起作用的最簡單的事”,因為“你將不再需要它你將不再需要它你將不再需要它你將不再需要它”。另外,Kent Beck
說:
你需要在一個強調溝通、簡單、反饋和勇氣的價值系統中選擇最好的工作方法,這樣你才能你需要在一個強調溝通、簡單、反饋和勇氣的價值系統中選擇最好的工作方法,這樣你才能你需要在一個強調溝通、簡單、反饋和勇氣的價值系統中選擇最好的工作方法,這樣你才能你需要在一個強調溝通、簡單、反饋和勇氣的價值系統中選擇最好的工作方法,這樣你才能勇敢的脫離過分設計。勇敢的脫離過分設計。勇敢的脫離過分設計。勇敢的脫離過分設計。Beck1 00?
同意。但是,現在我必須提到我的朋友Norm Kerth 。Norm 在軟體開發領域有豐富的經驗和見識。一年 以前我問他“對XP 有什麼想法”。他說:
我喜歡我喜歡我喜歡我喜歡XP 裡的每樣東西。我關心的是:還有什麼不在裡的每樣東西。Kerth 99?
當時,我只認為Norm 是一個保守派。但現在我不能確定了。XP 明顯缺少的就是使用模式的經驗。儘管一些XP 的創始人幫助建設了模式社團,但沒有哪一個堅定清楚的說明模式如何適應XP 。
一開始,這還沒有讓我感到迷惑。但現在,我的確感到迷惑。
我感到迷惑,因為我在XP 和模式上的經驗讓我相信:在XP 的場景中模式會工作得更好;並且當XP 包
含模式時,XP 也會工作得更好。
這需要一些解釋。我將從描述我自己使用模式和XP 的一些經驗開始。
從1995 年開始,我開始沉浸入模式之中。我學習模式文獻、主辦了一個每週一次的模式學習組、使用模式設計和開發軟體、並進行UP (一個關於使用模式的國際學術會議)的組織和運轉工作。說我“熱衷於 模式”實在是一種保守的說法。
當時,就象很多第一次學習模式的人一樣,我有一點過分渴望使用它們。這不是一件好事,因為它會讓你的設計比需要的更復雜。但我沒有意識到這一點,直到我開始學習重構。
大概在1996 年,我第一次接觸到了重構。我開始實證它並很快觀察到重構帶我離開了我在模式學習中學到的某些原則。
舉個例子,那本里程碑式的書——《設計模式:可複用物件導向軟體的基礎》中的一個原則是:
針對介面程式設計,而不是針對實現程式設計。針對介面程式設計,而不是針對實現程式設計。GHJV1 95?
《設計模式》的作者們做了相當精彩的工作來解釋為什麼我們需要遵循這條建議。幾乎在所有的模式中,都討論了當你針對某個特定實現程式設計時你的軟體如何變得缺少靈活性和可修改性。幾乎每一次都是介面過來幫忙。
但如果我們不需要靈活性和可修改性,情況又是怎樣?為什麼我們要在開始設計時預料一些可能永遠 不會出現的需要?這是我的一次覺悟。所以隨後我記錄下了下面這個J AVA 技巧:
不要分離類和介面不要分離類和介面不要分離類和介面不要分離類和介面
我曾經習慣於在我的介面名字後面加上一個“I ”。但當我繼續學習更多的重構技術時,我開始看到一 種明智的做法:把類名和介面名設計成一樣。下面是原因:在開發過程中,你知道你可以使用一個介面來 讓某些東西變得靈活(使實現多樣化),但可能你現在根本不需要讓實現多樣化。所以,放下預測太多的“過分設計”吧,你仍然保持簡單,仍然把東西放在一個類中。在某個地方你會寫一個方法語句來使用這 個類的物件。然後,幾天、幾星期、幾個月之後,你明確“需要”一個介面。因此你就將原來的類轉換成
一個介面,再建立一個實現類(實現新的介面),並且讓你原來的語句保持不變。 Kerievsky 96?
我繼續學習類似於重構的課程,逐漸的,我使用模式的方式開始改變了。我不再預先考慮使用模式。
現在,我更加明智了:如果某個模式可以解決某個設計問題,如果它提供一種方法來實現一個需求,我就 會使用它,但我將從可以編碼出的模式的最簡單實現開始。晚些時候,當我需要增加或修改時,我將讓這 個實現更加靈活、穩固。
這種使用模式的新方法是一種更好的方法。它節約了我的時間,並讓我的設計更簡單。
由於我繼續學到更多關於XP 的知識,我很快開始考慮這樣一個事實:那些清楚介紹“XP 是什麼”和“XP 如何工作”的人毫不提及模式。看起來,焦點已經全部從開發轉向了重構。構造一點,測試一點,重 構一點,然後再重複。
那麼,模式怎麼了?
我收到的一般的答案是:模式鼓勵過分設計,而重構保持事情簡單、輕量級。
現在,我和其他任何人一樣喜歡重構——我回顧了Martin Fowler 的書的關於這個主題的兩份手稿,然 後知道重構將成為一個標準。但我仍然喜歡模式,我發現模式在“幫助人們學會如何設計更好的軟體”方 面是無價之寶。所以,XP 怎麼能不包括模式呢?!
我小心的在Portland Pattern Repository 上寫下了我的不安。我問:是否完美的XP 模式應該由完全不知 道模式的程式設計師和指導者組成,是否他們應該完全依賴重構來“讓程式碼去它該去的地方”。Ron Jeffries ,世
界上最有經驗的XP 實踐者,與我爭論了這個主題,並且這樣寫:
一個初學者不能傾聽程式碼所說的話。他需要學習程式碼質量的模式(在一般意義上)。他需要一個初學者不能傾聽程式碼所說的話。他需要學習程式碼質量的模式(在一般意義上)。他需要一個初學者不能傾聽程式碼所說的話。他需要學習程式碼質量的模式(在一般意義上)。他需要一個初學者不能傾聽程式碼所說的話。他需要學習程式碼質量的模式(在一般意義上)。他需要
看好的程式碼(以及,我猜,差的程式碼),這樣他才能學會寫出好的程式碼。
一個問題,我的意思是一個可能的問題是,現在的模式是否被用於幫助提高程式碼的質。我想Beck 的Smalltalk Best Practice Patterns 會有幫助,因為那些都是非常小型的模式。我想會有幫助,因為那些都是非常小型的模式。設計模式都更值得懷疑,因為模式和討論有時變得相當大,而且它們可能造成看起來合理的龐大設計模式都更值得懷疑解決方案。Martin Fowler 的精彩的分析模式也有同樣的危險:在可以選擇的精彩的分析模式也有同樣的危險:在可以選擇一個小規模解決方案的時候選擇了大規模的解決方案。Jeffries 99?
一個非常有趣的關於模式的觀點。儘管我已經看到模式可以被明智的實現、使用,但Ron 看起來卻認
為它們是危險的,因為它們“讓龐大的解決方案看起來合理”。在其他地方,Ron 觀察了一件經常發生的事
情:第一次學習模式的人們如何過分渴望使用它們。
我無法不同意後面這個觀察結果。就象任何新事物——甚至是XP ——一樣,人們可能會過分渴望使
用它們。但模式真的鼓勵在可以使用小規模解決方案時使用大規模解決方案嗎?
我想這主要取決於你如何定義、使用模式。舉個例子,我觀察了許多模式的初級使用者,他們認為一
個模式與它的結構圖(或類圖)是完全相同的。只有在我向他們指出“模式可以根據需要以不同的方式實
現”之後,他們才開始發現這些圖只是表示實現模式的一種方式。
模式的實現有簡單的也有複雜的。訣竅是:發現模式針對的問題,將這個問題與你當前的問題進行比 較,然後將這個模式最簡單的實現(解決方案)與你的問題進行比較。當你這樣做時,你就不會在可以使 用小規模解決方案的時候使用大規模解決方案。你獲得瞭解決問題最好的平衡。
當人們沒有受過模式的良好訓練時,困難就可能出現。Ron 提到人們使用模式的方式是“現在構成的”
——這就是說,他們如何與現在的作者溝通。我同意模式文獻有一些缺點。關於模式的書很多,你可以花 一些時間來理解模式解決的問題,這樣你就可以聰明的根據自己的特定需要選擇模式。
這種選擇是極其重要的。如果你選擇了錯誤的模式,你可能過分設計或僅僅把你的設計揉在一起。有 經驗的模式使用者也會犯錯誤,並且經常看到這樣的結果。但這些專家有其他的模式作為裝備,這些模式 可以幫助他們面對自己的錯誤。所以他們最終經常把自己真正需要的模式換成了不那麼理想的模式。
那麼,你將怎樣成為一個有經驗的模式使用者呢?我發現除非人們投身於大量模式的學習中,否則他 們就有可能陷入誤解它們、過分使用它們以及用它們過分設計的危險之中。
但這是避免使用模式的一個原因嗎?
我想,不。我發現模式在如此多的專案中如此有用,以至於我無法想象不使用它們來進行軟體設計和 開發。我相信對模式的徹底的學習是非常值得的。
那麼,XP 對模式保持沉默是因為感覺到它們將被誤用嗎?
如果情況是這樣,也許問題已經變成:我們怎樣使用模式中的智慧,而避免模式在我們怎樣使用模式中的智慧,而避免模式在我們怎樣使用模式中的智慧,而避免模式在我們怎樣使用模式中的智慧,而避免模式在XP 開發場景中的開發場景中的開發場景中的開發場景中的 誤用呢?誤用呢?誤用呢?誤用呢?
在這裡,我想我必須回到《設計模式》。在“結論”一章、“設計模式將帶來什麼”一節、“重構的目標”小節中,作者寫道:
我們的設計模式記錄了許多重構產生的設計結構。在設計初期使用這些模式可以防止以我們的設計模式記錄了許多重構產生的設計結構。不過即使是在系統建成之後才瞭解如何使用這些模式,它們仍可以教你如何修改你的後的重構。不過即使是在系統建成之後才瞭解如何使用這些模式,它們仍可以教你如何修改你的後的重構。設計模式為你的重構提供了目標。GHJV2 95?
這就是我們需要的觀點:重構的目標。這就是重構和模式之間的橋樑。它完美的描述了我自己在如何 使用模式方面的進步:從簡單開始,考慮模式但將它們保持在次要地位,小規模重構,只有在真正需要模 式的時候才把重構轉移為模式。
這個需要訓練和仔細判斷的過程將很好的適應XP 所包含的最好的習慣。
而且這個途徑很明顯與“故意不知道或不使用模式而只依賴重構來改善設計”的方法非常不同。
只依賴重構的危險是:沒有目標,人們可能使設計小小進步,但他們的全面設計將最終受損害,因為 這種方法缺乏順序、簡單性和效力,而聰明的使用模式則可以讓開發者擁有這些。
引用Kent Beck 自己的話:模式生成體系結構模式生成體系結構模式生成體系結構模式生成體系結構。Beck2 94?
但模式不保證有紀律的使用。如果我們在設計中過多、過早的使用它們,我們就又回到了過分設計的 問題。因此,我們必須回答這個問題:“在設計的生命週期中,何時引入模式是安全的?”請回憶上面對 《設計模式》的引用:
在設計初期使用這些模式可以防止以後的重構。在設計初期使用這些模式可以防止以後的重構。在設計初期使用這些模式可以防止以後的重構。在設計初期使用這些模式可以防止以後的重構。
這是一個聰明的主張。如果我們不知道“何時配置一個模式”的基本規則,那麼我們就很容易在設計 週期的早期就陷入過分設計。
再一次,問題又全部集中在一起:如何將專案中的問題與一個合適的模式相匹配。
在這裡,我必須講述我為不同行業開發軟體得到的經驗。
有一家客戶要求我和我的團隊用J AVA 為他們的網站構造軟體,這將是一個很酷的互動式版本。這個 客戶沒有任何J AVA 程式設計師,但仍然要求能在他們需要的任何時候、任何地方修改軟體的行為,而不必做程 序的修改。多麼高的要求!
在對他們的需要做了一些分析之後,我們發現Command 模式將在這個設計中扮演一個非常重要的角 色。我們將編寫命令物件,並讓這些命令物件控制軟體的整個行為。使用者將可以引數化這些命令、將它們 排序、並選擇命令執行的時間和地點。
這個解決方案工作得很完美,Command 模式正是成功的關鍵。所以在這裡,我們不會等到重構的時候 才使用Command 模式。相反,我們預先看到了使用它的需要,並從一開始就用Command 模式來設計軟體。
在另一個專案中,系統需要作為獨立應用程式和WEB 應用程式執行。Builder 模式在這個系統中發揮了 巨大的作用。如果沒有它,我不敢想象我們會拼湊出一個多麼臃腫的設計。Builder 模式的作用就是解決“多平臺、多環境執行”這樣的問題。所以在設計早期就選擇它是正確的。
現在,我必須宣告:即使在設計的早期引入了模式,但一開始仍然應該按照它們最原始的樣子來實現它們。只有在晚些時候,當需要附加的功能時,模式的實現才能被替換或升級。
一個例子會讓你更清楚這一點。
上面提到的由命令物件控制的軟體是用多執行緒的程式碼實現的。有時候兩個執行緒會使用同一個巨集命令來 執行一系列命令。但一開始我們並沒有被巨集命令的執行緒安全問題困擾。所以,當我們開始遇到執行緒安全造 成的莫名其妙的問題時,我們必須重新考慮我們的實現。問題是,我們應該花時間構造巨集命令的執行緒安全 嗎?或者有沒有更簡單的方法來解決這個問題?
我們用更簡單的方法解決了這個問題,並且避免了過分設計:為每個執行緒提供一個獨立的巨集命令例項。
我們可以在30 秒內實現這個解決方案。請把這個時間與設計一個執行緒安全的巨集命令所需的時間做一下比 較。
這個例子描述了XP 的哲學怎樣在使用模式的情況下保持事情簡單。沒有這種簡單化的驅動,過分設 計的解決方案——就象執行緒安全的巨集命令——很容易出現。
因此,簡單化和模式之間的關聯是很重要的。
當程式設計師需要做出設計決策時,很重要的一件事就是:他們應該試圖保持設計簡單,因為簡單的設計 通常比龐大而複雜的設計更容易維護和擴充套件。我們已經知道,重構意味著將我們保持在簡單的路上:它鼓 勵我們以小而簡單步驟逐漸改進我們的設計,並避免過分設計。
但是模式呢?難道它們不是幫助我們保持簡單嗎?
有些人會說“不”。他們認為模式儘管有用,但容易造成複雜的設計。他們認為模式會造成物件快速 增加,並導致過分依賴物件組合。
這種觀點是由於對使用模式的方法的錯誤理解。有經驗的模式使用者會避免複雜的設計、物件的快速 增長和過多的物件組合。
實際上,在使用模式的時候,有經驗的模式使用者會讓他們的設計更簡單。我將再用一個例子來說明 我的觀點。
JUnit 是一個簡單而有用的J AVA 測試框架,它的作者是Kent Beck 和Erich Gamma 。這是一個精彩的軟體,其中滿是精心選擇的簡單的模式。
最近一些人要求我對JUnit 進行DeGo?F ,也就是說,將JUnit 中的設計模式移除掉,以觀察沒有模式的JUnit 是什麼樣子。這是一次非常有趣的練習,因為它讓參與者認真考慮應該在什麼時候在系統中引入模式。
為了描述他們學到的東西,我們將對JUnit 2.1 版中的一些擴充套件進行DeGo?F 。
JUnit 中有一個叫做Test Case 的抽象類,所有的具體測試類都派生自它。TestCase? 類沒有提供任何多次執行的方法,也沒有提供在自己的執行緒中執行測試的方法。Erich 和Kent 用Decorator 模式很優雅的實現了可重複測試和基於執行緒的測試。但是如果設計團隊不知道Decorator 模式呢?讓我們看看他們會開發出什麼, 並評估一下他們的設計有多簡單。
這是Test Case 在JUnit 框架1.0 版本中的樣子(為了簡化,我們忽略了註釋和很多方法):
--------------------------------------------------------------------------------1
2 public abstract class TestCase implements Test {
3 private String fName;
4 public TestCase(String name) {
5 fName= name;
6 }
7 public void run(TestResult result) {
8 result.startTest(this);
9 setUp();
10 try {
11 runTest();
12 }
13 catch (AssertionFailedError e) {
14 result.addFailure(this, e);
15 }
16 catch (Throwable e) {
17 result.addError(this, e);
18 } tearDown();
19 result.endTest(this);
20 }
21 public TestResult run() {
22 TestResult result= defaultResult();
23 run(result);
24 return result;
25 }
26 protected void runTest() throws Throwable {
27 Method runMethod= null;
28 try {
29 runMethod= getClass().getMethod(fName, new Class[0]);
30 } catch (NoSuchMethodException e) {
31 e.fillInStackTrace();
32 throw e;
33 }
34 try {
35 runMethod.invoke(this, new Class[0]);
36 }
37 catch (InvocationTargetException e) {
38 e.fillInStackTrace();
39 throw e.getTargetException();
40 }
41 catch (IllegalAccessException e) {
42 e.fillInStackTrace();
43 throw e;
44 }
45 }
46 public int countTestCases() {
47 return 1;
48 }
49 }
--------------------------------------------------------------------------------
新的需求要求允許測試重複進行、或在它們各自的執行緒中進行、或以上兩者。
沒有經驗的程式設計師通常在遇到這樣的新需求時進行子型別化。但是在這裡,因為知道TestCase? 物件將需要能夠在同一個執行緒中重複執行、或在各自獨立的執行緒中重複執行,所以程式設計師知道:他們需要考慮得更多。
一種實現方法是:將所有的功能都新增給TestCase? 本身。許多開發者——尤其是那些不瞭解設計模式的開發者——將會這樣做,而不考慮這會使他們的類變得臃腫。他們必須新增功能,所以他們將功能新增到任何可以新增的地方。下面的程式碼可能就是他們的實現:
--------------------------------------------------------------------------------1
2 public abstract class TestCase implements Test {
3 private String fName;
4 private int fRepeatTimes;
5 public TestCase(String name) {
6 this(name, 0);
7 }
8 public TestCase(String name, int repeatTimes) {
9 fName = name;
10 fRepeatTimes = repeatTimes;
11 }
12 public void run(TestResult result) {
13 for (int i=0; i < fRepeatTimes; i++) {
14 result.startTest(this);
15 setUp();
16 try {
17 runTest();
18 }
19 catch (AssertionFailedError e) {
20 result.addFailure(this, e);
21 }
22 catch (Throwable e) {
23 result.addError(this, e);
24 }
25 tearDown();
26 result.endTest(this);
27 }
28 }
29 public int countTestCases() {
30 return fRepeatTimes;
31 }
32 }
--------------------------------------------------------------------------------
請注意run (TestResult? result )方法變大了一些。他們還為TestCase? 類新增了另外的構造子。到目前為 止,這還不算什麼大事。並且,我們可以說:如果這就是所有必須做的事情,那麼使用Decorator 模式就是多餘的。
現在,如果要讓每個TestCase? 物件在其自己的執行緒中執行又怎樣呢?這裡也有一個可能的實現:
--------------------------------------------------------------------------------1
2 public abstract class TestCase implements Test {
3 private String fName;
4 private int fRepeatTimes;
5 private boolean fThreaded;
6 public TestCase(String name) {
7 this(name, 0, false);
8 }
9 public TestCase(String name, int repeatTimes) {
10 this(name, repeatTimes, false);
11 }
12 public TestCase(String name, int repeatTimes, boolean threaded) {
13 fName = name;
14 fRepeatTimes = repeatTimes;
15 fThreaded = threaded;
16 }
17 public void run(TestResult result) {
18 if (fThreaded) {
19 final TestResult finalResult= result;
20 final Test thisTest = this;
21 Thread t= new Thread() {
22 public void run() {
23 for (int i=0; i < fRepeatTimes; i++) {
24 finalResult.startTest(thisTest);
25 setUp();
26 try {
27 runTest();
28 }
29 catch (AssertionFailedError e) {
30 finalResult.addFailure(thisTest, e);
31 }
32 catch (Throwable e) {
33 finalResult.addError(thisTest, e);
34 } tearDown();
35 finalResult.endTest(thisTest);
36 }
37 }
38 };
39 t.start();
40 result = finalResult;
41 } else {
42 for (int i=0; i < fRepeatTimes; i++) {
43 result.startTest(this);
44 setUp();
45 try {
46 runTest();
47 }
48 catch (AssertionFailedError e) {
49 result.addFailure(this, e);
50 }
51 catch (Throwable e) {
52 result.addError(this, e);
53 } tearDown();
54 result.endTest(this);
55 }
56 }
57 }
58 public int countTestCases() {
59 return fRepeatTimes;
60 }
61 }
--------------------------------------------------------------------------------
唔,這看起來開始變得更壞了。為了支援兩個新的特徵,我們現在擁有了三個構造子,而且run
(TestResult? result )方法的大小迅速的膨脹起來。
即使不管所有這些新程式碼,我們這些程式設計師還沒有滿足這些需求:我們仍然不能在各自的執行緒中重複 執行測試。為了這個目的,我們必須新增更多的程式碼。算了,我就放過你吧。
重構可以幫助這些程式碼減小尺寸。但是隻需要稍做思考:如果再接到一個新的需求,我們要怎麼辦?
現在JUnit 3.1 支援四種不同的TestCase? 修飾器,它們可以輕鬆的隨意組合以獲取所需的功能。同時,JUnit 的實現仍然簡單——沒有混亂的程式碼。這種設計保持Test Case 類的簡單、輕量級,使用者只需要在需要的時
候對TestCase? 物件進行裝飾即可,而且可以選擇任何組合順序。
很清楚,這是一個模式幫助簡化設計的例子。這個例子也說明了缺乏經驗的開發者怎樣改善他們的設計——如果他們知道模式指出的重構目標。
使用模式來開發軟體是聰明之舉,但如果你缺乏使用模式的經驗,它也可能是危險的。出於這個原因,
我極力提倡模式學習組。這些學習組讓人們在同伴的幫助下穩步前進而精通模式。
當人們瞭解模式並以受過訓練的方式使用它們時,模式是最有用的——這種受過訓練的方式就是XP 的方式。以XP 的方式使用模式鼓勵開發者保持設計的簡單、並完全根據需要對模式進行重構。它鼓勵在設 計早期使用關鍵的模式。它鼓勵將問題與能幫助解決問題的模式相匹配。最後,它鼓勵開發者編寫模式的 簡單實現,然後根據需要發展它們。
在XP 的場景中,模式的確更有用;而在包含對模式的使用時,XP 開發則更有可能成功。
參考書目參考書目參考書目參考書目
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14639675/viewspace-582375/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 模式與XP模式
- javascript 與 設計模式JavaScript設計模式
- Oracle與設計模式Oracle設計模式
- XP設計原則
- 《Head First 設計模式》:與設計模式相處設計模式
- 工廠設計模式與代理設計模式【帶樣例】設計模式
- 追MM與設計模式設計模式
- 說透設計模式-代理模式與Proxy設計模式
- JavaScript設計模式與實踐--代理模式JavaScript設計模式
- 《JAVA與設計模式》之單例模式Java設計模式單例
- 12、Python與設計模式–策略模式Python設計模式
- JAVA設計模式 1 設計模式介紹、單例模式的理解與使用Java設計模式單例
- javascript設計模式與應用JavaScript設計模式
- 反射與工廠設計模式反射設計模式
- JavaScript設計模式與實踐–工廠模式JavaScript設計模式
- JavaScript設計模式與實踐--工廠模式JavaScript設計模式
- Scala 與設計模式(四):Factory 工廠模式設計模式
- 15、Python與設計模式–中介者模式Python設計模式
- 17、Python與設計模式–迭代器模式Python設計模式
- 10、Python與設計模式–享元模式Python設計模式
- Scala 與設計模式(三):Prototype 原型模式設計模式原型
- Scala 與設計模式(二):Builder 建立者模式設計模式UI
- Scala 與設計模式(一):Singleton 單例模式設計模式單例
- 6、Python與設計模式–裝飾器模式Python設計模式
- 18、Python與設計模式–訪問者模式Python設計模式
- react 設計模式與最佳實踐React設計模式
- 設計模式與前端工程師設計模式前端工程師
- Kubernetes與容器設計模式設計模式
- java抽象類與介面——設計模式Java抽象設計模式
- 軟體設計原則與模式模式
- 設計模式與系統階段設計模式
- 設計模式之單例模式(《JavaScript設計模式與開發實踐》讀書筆記)設計模式單例JavaScript筆記
- 設計模式----工廠設計模式設計模式
- 設計模式-工廠設計模式設計模式
- Java設計模式——模板設計模式Java設計模式
- 設計模式---外觀設計模式設計模式
- [設計模式]單例設計模式設計模式單例
- 設計模式-裝飾設計模式設計模式