設計模式的基礎篇相關知識,簡單易懂。
一、 設計模式概述(一)
1) 概要
關於金庸小說中到底是招式重要還是內功重要的爭論從未停止,我們在這裡並不分析張無忌的九陽神功和令狐沖的獨孤九劍到底哪個更厲害,但我想每個武林人士夢寐以求的應該是既有淋漓的招式又有深厚的內功。看到這裡大家可能會產生疑問了?搞什麼,討論什麼招式與內功,我只是個軟體開發人員。別急,正因為你是軟體開發人員我才跟你談這個,因為我們的軟體開發技術也包括一些招式和內功:Java、C#、C++等程式語言,Eclipse、Visual Studio等開發工具,JSP、ASP.net等開發技術,Struts、Hibernate、JBPM等框架技術,所有這些我們都可以認為是招式;而資料結構、演算法、設計模式、重構、軟體工程等則為內功。招式可以很快學會,但是內功的修煉需要更長的時間。我想每一位軟體開發人員也都希望成為一名兼具淋漓招式和深厚內功的“上乘”軟體工程師,而對設計模式的學習與領悟將會讓你“內功”大增,再結合你日益純熟的“招式”,你的軟體開發“功力”一定會達到一個新的境界。既然這樣,還等什麼,趕快行動吧。下面就讓我們正式踏上神奇而又美妙的設計模式之旅。
2) 設計模式從何而來
在介紹設計模式的起源之前,我們先要了解一下模式的誕生與發展。與很多軟體工程技術一樣,模式起源於建築領域,畢竟與只有幾十年歷史的軟體工程相比,已經擁有幾千年沉澱的建築工程有太多值得學習和借鑑的地方。
那麼模式是如何誕生的?讓我們先來認識一個人——Christopher Alexander(克里斯托弗.亞歷山大),哈佛大學建築學博士、美國加州大學伯克利分校建築學教授、加州大學伯克利分校環境結構研究所所長、美國藝術和科學院院士……頭銜真多,微笑,不過他還有一個“暱稱”——模式之父(The father of patterns)。Christopher Alexander博士及其研究團隊用了約20年的時間,對住宅和周邊環境進行了大量的調查研究和資料收集工作,發現人們對舒適住宅和城市環境存在一些共同的認同規律,Christopher Alexander在著作A Pattern Language: Towns, Buildings, Construction中把這些認同規律歸納為253個模式,對每一個模式(Pattern)都從Context(前提條件)、Theme或Problem(目標問題)、 Solution(解決方案)三個方面進行了描述,並給出了從使用者需求分析到建築環境結構設計直至經典例項的過程模型。
在Christopher Alexander的另一部經典著作《建築的永恆之道》中,他給出了關於模式的定義:
每個模式都描述了一個在我們的環境中不斷出現的問題,然後描述了該問題的解決方案的核心,通過這種方式,我們可以無數次地重用那些已有的成功的解決方案,無須再重複相同的工作。這個定義可以簡單地用一句話表示:
模式是在特定環境下人們解決某類重複出現問題的一套成功或有效的解決方案。【A pattern is a successful or efficient solution to a recurring problem within a context】
1990年,軟體工程界開始關注ChristopherAlexander等在這一住宅、公共建築與城市規劃領域的重大突破。最早將模式的思想引入軟體工程方法學的是1991-1992年以“四人組(Gang of Four,簡稱GoF,分別是Erich Gamma, Richard Helm, Ralph Johnson和John Vlissides)”自稱的四位著名軟體工程學者,他們在1994年歸納發表了23種在軟體開發中使用頻率較高的設計模式,旨在用模式來統一溝通面向物件方法在分析、設計和實現間的鴻溝。
GoF將模式的概念引入軟體工程領域,這標誌著軟體模式的誕生。軟體模式(Software Patterns)是將模式的一般概念應用於軟體開發領域,即軟體開發的總體指導思路或參照樣板。軟體模式並非僅限於設計模式,還包括架構模式、分析模式和過程模式等,實際上,在軟體開發生命週期的每一個階段都存在著一些被認同的模式。
軟體模式是在軟體開發中某些可重現問題的一些有效解決方法,軟體模式的基礎結構主要由四部分構成,包括問題描述【待解決的問題是什麼】、前提條件【在何種環境或約束條件下使用】、解法【如何解決】和效果【有哪些優缺點】,如圖1-1所示:
圖1-1 軟體模式基本結構
軟體模式與具體的應用領域無關,也就是說無論你從事的是移動應用開發、桌面應用開發、Web應用開發還是嵌入式軟體的開發,都可以使用軟體模式。
在軟體模式中,設計模式是研究最為深入的分支,設計模式用於在特定的條件下為一些重複出現的軟體設計問題提供合理的、有效的解決方案,它融合了眾多專家的設計經驗,已經在成千上萬的軟體中得以應用。 1995年, GoF將收集和整理好的23種設計模式彙編成Design Patterns: Elements of Reusable Object-Oriented Software【《設計模式:可複用面向物件軟體的基礎》】一書,該書的出版也標誌著設計模式正式成為面向物件(Object Oriented)軟體工程的一個重要研究分支。
從1995年至今,無論是在大型API或框架(如JDK、.net Framework等)、輕量級框架(如Struts、Spring、 Hibernate、JUnit等)、還是應用軟體的開發中,設計模式都得到了廣泛的應用。如果你正在從事面向物件開發或正準備從事面向物件開發,無論你是使用Java、C#、Objective-C、VB.net、Smalltalk等純面向物件程式語言,還是使用C++、PHP、Delphi、JavaScript等可支援面向物件程式設計的語言,如果你一點設計模式也不懂,我可以毫不誇張的說:你真的out了。
二、 設計模式概述(二)
3) 設計模式是什麼
俗話說:站在別人的肩膀上,我們會看得更遠。設計模式的出現可以讓我們站在前人的肩膀上,通過一些成熟的設計方案來指導新專案的開發和設計,以便於我們開發出具有更好的靈活性和可擴充套件性,也更易於複用的軟體系統。
設計模式的一般定義如下: 設計模式(Design Pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結,使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解並且保證程式碼可靠性。
狹義的設計模式是指GoF在《設計模式:可複用面向物件軟體的基礎》一書中所介紹的23種經典設計模式,不過設計模式並不僅僅只有這23種,隨著軟體開發技術的發展,越來越多的新模式不斷誕生並得以應用。
設計模式一般包含模式名稱、問題、目的、解決方案、效果等組成要素,其中關鍵要素是模式名稱、問題、解決方案和效果。模式名稱(Pattern Name)通過一兩個詞來描述模式的問題、解決方案和效果,以便更好地理解模式並方便開發人員之間的交流,絕大多數模式都是根據其功能或模式結構來命名的(GoF設計模式中沒有一個模式用人名命名,微笑);問題(Problem)描述了應該在何時使用模式,它包含了設計中存在的問題以及問題存在的原因;解決方案(Solution)描述了一個設計模式的組成成分,以及這些組成成分之間的相互關係,各自的職責和協作方式,通常解決方案通過UML類圖和核心程式碼來進行描述;效果(Consequences)描述了模式的優缺點以及在使用模式時應權衡的問題。
雖然GoF設計模式只有23個,但是它們各具特色,每個模式都為某一個可重複的設計問題提供了一套解決方案。根據它們的用途,設計模式可分為建立型(Creational),結構型(Structural)和行為型(Behavioral)三種,其中建立型模式主要用於描述如何建立物件,結構型模式主要用於描述如何實現類或物件的組合,行為型模式主要用於描述類或物件怎樣互動以及怎樣分配職責,在GoF 23種設計模式中包含5種建立型設計模式、7種結構型設計模式和11種行為型設計模式。此外,根據某個模式主要是用於處理類之間的關係還是物件之間的關係,設計模式還可以分為類模式和物件模式。我們經常將兩種分類方式結合使用,如單例模式是物件建立型模式,模板方法模式是類行為型模式。
值得一提的是,有一個設計模式雖然不屬於GoF 23種設計模式,但一般在介紹設計模式時都會對它進行說明,它就是簡單工廠模式,也許是太“簡單”了,GoF並沒有把它寫到那本經典著作中,不過現在大部分的設計模式書籍都會對它進行專門的介紹。
表1列出將要介紹的24種設計模式,其中模式的學習難度是我個人在多年模式使用和推廣過程中的經驗總結,僅作參考,模式的使用頻率來自著名的模式推廣和教育網站——http://www.dofactory.net。
表1 常用設計模式一覽表
型別 模式名稱 學習難度 使用頻率
建立型模式 Creational Pattern 單例模式 Singleton Pattern★☆☆☆☆ ★★★★☆
建立型模式 Creational Pattern 簡單工廠模式 Simple Factory Pattern★★☆☆☆ ★★★☆☆
建立型模式 Creational Pattern 工廠方法模式 Factory Method Pattern★★☆☆☆ ★★★★★
建立型模式 Creational Pattern 抽象工廠模式 Abstract Factory Pattern★★★★☆ ★★★★★
建立型模式 Creational Pattern 原型模式 Prototype Pattern★★★☆☆ ★★★☆☆
建立型模式 Creational Pattern 建造者模式 Builder Pattern★★★★☆ ★★☆☆☆
結構型模式 Structural Pattern 介面卡模式 Adapter Pattern★★☆☆☆ ★★★★☆
結構型模式 Structural Pattern 橋接模式 Bridge Pattern★★★☆☆ ★★★☆☆
結構型模式 Structural Pattern 組合模式 Composite Pattern★★★☆☆ ★★★★☆
結構型模式 Structural Pattern 裝飾模式 Decorator Pattern★★★☆☆ ★★★☆☆
結構型模式 Structural Pattern 外觀模式 Fa?ade Pattern★☆☆☆☆ ★★★★★
結構型模式 Structural Pattern 享元模式 Flyweight Pattern★★★★☆ ★☆☆☆☆
結構型模式 Structural Pattern 代理模式 Proxy Pattern ★★★☆☆ ★★★★☆
行為型模式 Behavioral Pattern 職責鏈模式 Chain of Responsibility Pattern★★★☆☆ ★★☆☆☆
行為型模式 Behavioral Pattern 命令模式 Command Pattern★★★☆☆ ★★★★☆
行為型模式 Behavioral Pattern 直譯器模式 Interpreter Pattern★★★★★ ★☆☆☆☆
行為型模式 Behavioral Pattern 迭代器模式 Iterator Pattern★★★☆☆ ★★★★★
行為型模式 Behavioral Pattern 中介者模式 Mediator Pattern★★★☆☆ ★★☆☆☆
行為型模式 Behavioral Pattern 備忘錄模式 Memento Pattern★★☆☆☆ ★★☆☆☆
行為型模式 Behavioral Pattern 觀察者模式 Observer Pattern★★★☆☆ ★★★★★
行為型模式 Behavioral Pattern 狀態模式 State Pattern ★★★☆☆ ★★★☆☆
行為型模式 Behavioral Pattern 策略模式 Strategy Pattern★☆☆☆☆ ★★★★☆
行為型模式 Behavioral Pattern 模板方法模式 Template Method Pattern★★☆☆☆ ★★★☆☆
行為型模式 Behavioral Pattern 訪問者模式 Visitor Pattern★★★★☆ ★☆☆☆☆
三、 設計模式概述(三)
4) 設計模式有什麼用
下面我們來回答最後一個問題:設計模式到底有什麼用?簡單來說,設計模式至少有如下幾個用途:
(1) 設計模式來源眾多專家的經驗和智慧,它們是從許多優秀的軟體系統中總結出的成功的、能夠實現可維護性複用的設計方案,使用這些方案將可以讓我們避免做一些重複性的工作,也許我們冥思苦想得到的一個“自以為很了不起”的設計方案其實就是某一個設計模式。在時間就是金錢的今天,設計模式無疑會為有助於我們提高開發和設計效率,但它不保證一定會提高,微笑。
(2) 設計模式提供了一套通用的設計詞彙和一種通用的形式來方便開發人員之間溝通和交流,使得設計方案更加通俗易懂。交流通常很耗時,任何有助於提高交流效率的東西都可以為我們節省不少時間。無論你使用哪種程式語言,做什麼型別的專案,甚至你處於一個國際化的開發團隊,當面對同一個設計模式時,你和別人的理解並無二異,因為設計模式是跨語言、跨平臺、跨應用、跨國界的,微笑。
(3) 大部分設計模式都兼顧了系統的可重用性和可擴充套件性,這使得我們可以更好地重用一些已有的設計方案、功能模組甚至一個完整的軟體系統,避免我們經常做一些重複的設計、編寫一些重複的程式碼。此外,隨著軟體規模的日益增大,軟體壽命的日益變長,系統的可維護性和可擴充套件性也越來越重要,許多設計模式將有助於提高系統的靈活性和可擴充套件性,讓我們在不修改或者少修改現有系統的基礎上增加、刪除或者替換功能模組。如果一點設計模式都不懂,我想要做到這一點恐怕還是很困難的,微笑。 (4) 合理使用設計模式並對設計模式的使用情況進行文件化,將有助於別人更快地理解系統。如果某一天因為升職或跳槽等原因,別人接手了你的專案,只要他也懂設計模式,我想他應該能夠很快理解你的設計思路和實現方案,讓你升職無後患之憂,跳槽也心安理得,何樂而不為呢?微笑。 (5) 最後一點對初學者很重要,學習設計模式將有助於初學者更加深入地理解面向物件思想,讓你知道:如何將程式碼分散在幾個不同的類中?為什麼要有“介面”?何謂針對抽象程式設計?何時不應該使用繼承?如果不修改原始碼增加新功能?同時還讓你能夠更好地閱讀和理解現有類庫(如JDK)與其他系統中的原始碼,讓你早點脫離面向物件程式設計的“菜鳥期”,微笑。
5) 個人觀點
作為設計模式的忠實粉絲和推廣人員,在正式學習設計模式之前,我結合多年的模式應用和教育培訓經驗與大家分享幾點個人的看法,以作參考:
(1) 掌握設計模式並不是件很難的事情,關鍵在於多思考,多實踐,不要聽到人家說懂幾個設計模式就很“牛”,只要用心學習,設計模式也就那麼回事,你也可以很“牛”的,一定要有信心。
(2) 在學習每一個設計模式時至少應該掌握如下幾點:這個設計模式的意圖是什麼,它要解決一個什麼問題,什麼時候可以使用它;它是如何解決的,掌握它的結構圖,記住它的關鍵程式碼;能夠想到至少兩個它的應用例項,一個生活中的,一個軟體中的;這個模式的優缺點是什麼,在使用時要注意什麼。當你能夠回答上述所有問題時,恭喜你,你瞭解一個設計模式了,至於掌握它,那就在開發中去使用吧,用多了你自然就掌握了。
(3) “如果想體驗一下運用模式的感覺,那麼最好的方法就是運用它們”。正如在本章最開始所說的,設計模式是“內功心法”,它還是要與“實戰招式”相結合才能夠相得益彰。學習設計模式的目的在於應用,如果不懂如何使用一個設計模式,而只是學過,能夠說出它的用途,繪製它的結構,充其量也只能說你瞭解這個模式,嚴格一點說:不會在開發中靈活運用一個模式基本上等於沒學。所以一定要做到:少說多做。
(4) 千萬不要濫用模式,不要試圖在一個系統中用上所有的模式,也許有這樣的系統,但至少目前我沒有碰到過。每個模式都有自己的適用場景,不能為了使用模式而使用模式?【怎麼理解,大家自己思考,微笑】,濫用模式不如不用模式,因為濫用的結果得不到“藝術品”一樣的軟體,很有可能是一堆垃圾程式碼。
(5) 如果將設計模式比喻成“三十六計”,那麼每一個模式都是一種計策,它為解決某一類問題而誕生,不管這個設計模式的難度如何,使用頻率高不高,我建議大家都應該好好學學,多學一個模式也就意味著你多了“一計”,說不定什麼時候一不小心就用上了,微笑。因此,模式學習之路上要不怕困難,勇於挑戰,有的模式雖然難一點,但反覆琢磨,反覆研讀,應該還是能夠征服的。
(6) 設計模式的“上乘”境界:“手中無模式,心中有模式”。模式使用的最高境界是你已經不知道具體某個設計模式的定義和結構了,但你會靈活自如地選擇一種設計方案【其實就是某個設計模式】來解決某個問題,設計模式已經成為你開發技能的一部分,能夠手到擒來,“內功”與“招式”已渾然一體,要達到這個境界並不是看完某本書或者開發一兩個專案就能夠實現的,它需要不斷沉澱與積累,所以,對模式的學習不要急於求成。
(7) 最後一點來自GoF已故成員、我個人最尊敬和崇拜的軟體工程大師之一John Vlissides的著作《設計模式沉思錄》(Pattern Hatching Design Patterns Applied):模式從不保證任何東西,它不能保證你一定能夠做出可複用的軟體,提高你的生產率,更不能保證世界和平,微笑。模式並不能替代人來完成軟體系統的創造,它們只不過會給那些缺乏經驗但卻具備才能和創造力的人帶來希望。
擴充套件 John Vlissides(1961-2005),GoF成員,斯坦福大學電腦科學博士,原IBM研究員,因患腦瘤於2005年11月24日(感恩節)病故,享年44歲,為紀念他的貢獻,ACM SIGPLAN特設立John Vlissides獎。
面向物件設計原則
對於面向物件軟體系統的設計而言,在支援可維護性的同時,提高系統的可複用性是一個至關重要的問題,如何同時提高一個軟體系統的可維護性和可複用性是面向物件設計需要解決的核心問題之一。在面向物件設計中,可維護性的複用是以設計原則為基礎的。每一個原則都蘊含一些面向物件設計的思想,可以從不同的角度提升一個軟體結構的設計水平。
面向物件設計原則為支援可維護性複用而誕生,這些原則蘊含在很多設計模式中,它們是從許多設計方案中總結出的指導性原則。面向物件設計原則也是我們用於評價一個設計模式的使用效果的重要指標之一,在設計模式的學習中,大家經常會看到諸如“XXX模式符合XXX原則”、“XXX模式違反了XXX原則”這樣的語句。
最常見的7種面向物件設計原則如下表所示: 表1 7種常用的面向物件設計原則
設計原則名稱 定 義 使用頻率
單一職責原則 (Single Responsibility Principle, SRP) 一個類只負責一個功能領域中的相應職責 ★★★★☆
開閉原則 (Open-Closed Principle, OCP) 軟體實體應對擴充套件開放,而對修改關閉 ★★★★★
里氏代換原則 (Liskov Substitution Principle, LSP) 所有引用基類物件的地方能夠透明地使用其子類的物件 ★★★★★
依賴倒轉原則 (Dependence Inversion Principle, DIP) 抽象不應該依賴於細節,細節應該依賴於抽象 ★★★★★
介面隔離原則 (Interface Segregation Principle, ISP) 使用多個專門的介面,而不使用單一的總介面 ★★☆☆☆
合成複用原則 (Composite Reuse Principle, CRP) 儘量使用物件組合,而不是繼承來達到複用的目的 ★★★★☆
迪米特法則 (Law of Demeter, LoD) 一個軟體實體應當儘可能少地與其他實體發生相互作用 ★★★☆☆
四、 面向物件設計原則之單一職責原則
單一職責原則是最簡單的面向物件設計原則,它用於控制類的粒度大小。單一職責原則定義如下: 單一職責原則(Single Responsibility Principle, SRP):一個類只負責一個功能領域中的相應職責,或者可以定義為:就一個類而言,應該只有一個引起它變化的原因。
單一職責原則告訴我們:一個類不能太“累”!在軟體系統中,一個類(大到模組,小到方法)承擔的職責越多,它被複用的可能性就越小,而且一個類承擔的職責過多,就相當於將這些職責耦合在一起,當其中一個職責變化時,可能會影響其他職責的運作,因此要將這些職責進行分離,將不同的職責封裝在不同的類中,即將不同的變化原因封裝在不同的類中,如果多個職責總是同時發生改變則可將它們封裝在同一類中。
單一職責原則是實現高內聚、低耦合的指導方針,它是最簡單但又最難運用的原則,需要設計人員發現類的不同職責並將其分離,而發現類的多重職責需要設計人員具有較強的分析設計能力和相關實踐經驗。
下面通過一個簡單例項來進一步分析單一職責原則:
Sunny軟體公司開發人員針對某CRM(Customer Relationship Management,客戶關係管理)系統中客戶資訊圖形統計模組提出瞭如圖1所示初始設計方案:
圖1 初始設計方案結構圖
在圖1中,CustomerDataChart類中的方法說明如下:getConnection()方法用於連線資料庫,findCustomers()用於查詢所有的客戶資訊,createChart()用於建立圖表,displayChart()用於顯示圖表。
現使用單一職責原則對其進行重構。
在圖1中,CustomerDataChart類承擔了太多的職責,既包含與資料庫相關的方法,又包含與圖表生成和顯示相關的方法。如果在其他類中也需要連線資料庫或者使用findCustomers()方法查詢客戶資訊,則難以實現程式碼的重用。無論是修改資料庫連線方式還是修改圖表顯示方式都需要修改該類,它不止一個引起它變化的原因,違背了單一職責原則。因此需要對該類進行拆分,使其滿足單一職責原則,類CustomerDataChart可拆分為如下三個類:
(1) DBUtil:負責連線資料庫,包含資料庫連線方法getConnection();
(2) CustomerDAO:負責操作資料庫中的Customer表,包含對Customer表的增刪改查等方法,如findCustomers();
(3) CustomerDataChart:負責圖表的生成和顯示,包含方法createChart()和displayChart()。
使用單一職責原則重構後的結構如圖2所示:
五、 面向物件設計原則之開閉原則
開閉原則是面向物件的可複用設計的第一塊基石,它是最重要的面向物件設計原則。開閉原則由Bertrand Meyer於1988年提出,其定義如下:
開閉原則(Open-Closed Principle, OCP):一個軟體實體應當對擴充套件開放,對修改關閉。即軟體實體應儘量在不修改原有程式碼的情況下進行擴充套件。
在開閉原則的定義中,軟體實體可以指一個軟體模組、一個由多個類組成的區域性結構或一個獨立的類。
任何軟體都需要面臨一個很重要的問題,即它們的需求會隨時間的推移而發生變化。當軟體系統需要面對新的需求時,我們應該儘量保證系統的設計框架是穩定的。如果一個軟體設計符合開閉原則,那麼可以非常方便地對系統進行擴充套件,而且在擴充套件時無須修改現有程式碼,使得軟體系統在擁有適應性和靈活性的同時具備較好的穩定性和延續性。隨著軟體規模越來越大,軟體壽命越來越長,軟體維護成本越來越高,設計滿足開閉原則的軟體系統也變得越來越重要。
為了滿足開閉原則,需要對系統進行抽象化設計,抽象化是開閉原則的關鍵。在Java、C#等程式語言中,可以為系統定義一個相對穩定的抽象層,而將不同的實現行為移至具體的實現層中完成。在很多面向物件程式語言中都提供了介面、抽象類等機制,可以通過它們定義系統的抽象層,再通過具體類來進行擴充套件。如果需要修改系統的行為,無須對抽象層進行任何改動,只需要增加新的具體類來實現新的業務功能即可,實現在不修改已有程式碼的基礎上擴充套件系統的功能,達到開閉原則的要求。
Sunny軟體公司開發的CRM系統可以顯示各種型別的圖表,如餅狀圖和柱狀圖等,為了支援多種圖表顯示方式,原始設計方案如圖1所示:
圖1 初始設計方案結構圖
在ChartDisplay類的display()方法中存在如下程式碼片段:
......
if (type.equals("pie")) {
PieChart chart = new PieChart();
chart.display();
}
else if (type.equals("bar")) {
BarChart chart = new BarChart();
chart.display();
}
......
在該程式碼中,如果需要增加一個新的圖表類,如折線圖LineChart,則需要修改ChartDisplay類的display()方法的原始碼,增加新的判斷邏輯,違反了開閉原則。
現對該系統進行重構,使之符合開閉原則。
在本例項中,由於在ChartDisplay類的display()方法中針對每一個圖表類程式設計,因此增加新的圖表類不得不修改原始碼。可以通過抽象化的方式對系統進行重構,使之增加新的圖表類時無須修改原始碼,滿足開閉原則。
具體做法如下:
(1) 增加一個抽象圖表類AbstractChart,將各種具體圖表類作為其子類;
(2) ChartDisplay類針對抽象圖表類進行程式設計,由客戶端來決定使用哪種具體圖表。
重構後結構如圖2所示:
圖2 重構後的結構圖
在圖2中,我們引入了抽象圖表類AbstractChart,且ChartDisplay針對抽象圖表類進行程式設計,並通過setChart()方法由客戶端來設定例項化的具體圖表物件,在ChartDisplay的display()方法中呼叫chart物件的display()方法顯示圖表。如果需要增加一種新的圖表,如折線圖LineChart,只需要將LineChart也作為AbstractChart的子類,在客戶端向ChartDisplay中注入一個LineChart物件即可,無須修改現有類庫的原始碼。
注意:因為xml和properties等格式的配置檔案是純文字檔案,可以直接通過VI編輯器或記事本進行編輯,且無須編譯,因此在軟體開發中,一般不把對配置檔案的修改認為是對系統原始碼的修改。如果一個系統在擴充套件時只涉及到修改配置檔案,而原有的Java程式碼或C#程式碼沒有做任何修改,該系統即可認為是一個符合開閉原則的系統。
六、 面向物件設計原則之里氏代換原則
里氏代換原則由2008年圖靈獎得主、美國第一位電腦科學女博士Barbara Liskov教授和卡內基•梅隆大學Jeannette Wing教授於1994年提出。其嚴格表述如下:如果對每一個型別為S的物件o1,都有型別為T的物件o2,使得以T定義的所有程式P在所有的物件o1代換o2時,程式P的行為沒有變化,那麼型別S是型別T的子型別。這個定義比較拗口且難以理解,因此我們一般使用它的另一個通俗版定義: 里氏代換原則(Liskov Substitution Principle, LSP):所有引用基類(父類)的地方必須能透明地使用其子類的物件。
里氏代換原則告訴我們,在軟體中將一個基類物件替換成它的子類物件,程式將不會產生任何錯誤和異常,反過來則不成立,如果一個軟體實體使用的是一個子類物件的話,那麼它不一定能夠使用基類物件。例如:我喜歡動物,那我一定喜歡狗,因為狗是動物的子類;但是我喜歡狗,不能據此斷定我喜歡動物,因為我並不喜歡老鼠,雖然它也是動物。
例如有兩個類,一個類為BaseClass,另一個是SubClass類,並且SubClass類是BaseClass類的子類,那麼一個方法如果可以接受一個BaseClass型別的基類物件base的話,如:method1(base),那麼它必然可以接受一個BaseClass型別的子類物件sub,method1(sub)能夠正常執行。反過來的代換不成立,如一個方法method2接受BaseClass型別的子類物件sub為引數:method2(sub),那麼一般而言不可以有method2(base),除非是過載方法。
里氏代換原則是實現開閉原則的重要方式之一,由於使用基類物件的地方都可以使用子類物件,因此在程式中儘量使用基類型別來對物件進行定義,而在執行時再確定其子類型別,用子類物件來替換父類物件。
在使用里氏代換原則時需要注意如下幾個問題:
(1)子類的所有方法必須在父類中宣告,或子類必須實現父類中宣告的所有方法。根據里氏代換原則,為了保證系統的擴充套件性,在程式中通常使用父類來進行定義,如果一個方法只存在子類中,在父類中不提供相應的宣告,則無法在以父類定義的物件中使用該方法。
(2) 我們在運用里氏代換原則時,儘量把父類設計為抽象類或者介面,讓子類繼承父類或實現父介面,並實現在父類中宣告的方法,執行時,子類例項替換父類例項,我們可以很方便地擴充套件系統的功能,同時無須修改原有子類的程式碼,增加新的功能可以通過增加一個新的子類來實現。里氏代換原則是開閉原則的具體實現手段之一。
(3) Java語言中,在編譯階段,Java編譯器會檢查一個程式是否符合里氏代換原則,這是一個與實現無關的、純語法意義上的檢查,但Java編譯器的檢查是有侷限的。
在Sunny軟體公司開發的CRM系統中,客戶(Customer)可以分為VIP客戶(VIPCustomer)和普通客戶(CommonCustomer)兩類,系統需要提供一個傳送Email的功能,原始設計方案如圖1所示:
圖1原始結構圖
在對系統進行進一步分析後發現,無論是普通客戶還是VIP客戶,傳送郵件的過程都是相同的,也就是說兩個send()方法中的程式碼重複,而且在本系統中還將增加新型別的客戶。為了讓系統具有更好的擴充套件性,同時減少程式碼重複,使用里氏代換原則對其進行重構。
在本例項中,可以考慮增加一個新的抽象客戶類Customer,而將CommonCustomer和VIPCustomer類作為其子類,郵件傳送類EmailSender類針對抽象客戶類Customer程式設計,根據里氏代換原則,能夠接受基類物件的地方必然能夠接受子類物件,因此將EmailSender中的send()方法的引數型別改為Customer,如果需要增加新型別的客戶,只需將其作為Customer類的子類即可。重構後的結構如圖2所示:
圖2 重構後的結構圖
里氏代換原則是實現開閉原則的重要方式之一。在本例項中,在傳遞引數時使用基類物件,除此以外,在定義成員變數、定義區域性變數、確定方法返回型別時都可使用里氏代換原則。針對基類程式設計,在程式執行時再確定具體子類。
七、 面向物件設計原則之依賴倒轉原則
如果說開閉原則是面向物件設計的目標的話,那麼依賴倒轉原則就是面向物件設計的主要實現機制之一,它是系統抽象化的具體實現。依賴倒轉原則是Robert C. Martin在1996年為“C++Reporter”所寫的專欄Engineering Notebook的第三篇,後來加入到他在2002年出版的經典著作“Agile Software Development, Principles, Patterns, and Practices”一書中。依賴倒轉原則定義如下: 依賴倒轉原則(Dependency Inversion Principle, DIP):抽象不應該依賴於細節,細節應當依賴於抽象。換言之,要針對介面程式設計,而不是針對實現程式設計。
依賴倒轉原則要求我們在程式程式碼中傳遞引數時或在關聯關係中,儘量引用層次高的抽象層類,即使用介面和抽象類進行變數型別宣告、引數型別宣告、方法返回型別宣告,以及資料型別的轉換等,而不要用具體類來做這些事情。為了確保該原則的應用,一個具體類應當只實現介面或抽象類中宣告過的方法,而不要給出多餘的方法,否則將無法呼叫到在子類中增加的新方法。
在引入抽象層後,系統將具有很好的靈活性,在程式中儘量使用抽象層進行程式設計,而將具體類寫在配置檔案中,這樣一來,如果系統行為發生變化,只需要對抽象層進行擴充套件,並修改配置檔案,而無須修改原有系統的原始碼,在不修改的情況下來擴充套件系統的功能,滿足開閉原則的要求。
在實現依賴倒轉原則時,我們需要針對抽象層程式設計,而將具體類的物件通過依賴注入(DependencyInjection, DI)的方式注入到其他物件中,依賴注入是指當一個物件要與其他物件發生依賴關係時,通過抽象來注入所依賴的物件。常用的注入方式有三種,分別是:構造注入,設值注入(Setter注入)和介面注入。構造注入是指通過建構函式來傳入具體類的物件,設值注入是指通過Setter方法來傳入具體類的物件,而介面注入是指通過在介面中宣告的業務方法來傳入具體類的物件。這些方法在定義時使用的是抽象型別,在執行時再傳入具體型別的物件,由子類物件來覆蓋父類物件。
擴充套件
軟體工程大師Martin Fowler在其文章Inversion of Control Containers and the Dependency Injection pattern中對依賴注入進行了深入的分析,參考連結: http://martinfowler.com/articles/injection.html
下面通過一個簡單例項來加深對依賴倒轉原則的理解:
Sunny軟體公司開發人員在開發某CRM系統時發現:該系統經常需要將儲存在TXT或Excel檔案中的客戶資訊轉存到資料庫中,因此需要進行資料格式轉換。在客戶資料操作類中將呼叫資料格式轉換類的方法實現格式轉換和資料庫插入操作,初始設計方案結構如圖1所示:
圖1 初始設計方案結構圖
在編碼實現圖1所示結構時,Sunny軟體公司開發人員發現該設計方案存在一個非常嚴重的問題,由於每次轉換資料時資料來源不一定相同,因此需要更換資料轉換類,如有時候需要將TXTDataConvertor改為ExcelDataConvertor,此時,需要修改CustomerDAO的原始碼,而且在引入並使用新的資料轉換類時也不得不修改CustomerDAO的原始碼,系統擴充套件性較差,違反了開閉原則,現需要對該方案進行重構。
在本例項中,由於CustomerDAO針對具體資料轉換類程式設計,因此在增加新的資料轉換類或者更換資料轉換類時都不得不修改CustomerDAO的原始碼。我們可以通過引入抽象資料轉換類解決該問題,在引入抽象資料轉換類DataConvertor之後,CustomerDAO針對抽象類DataConvertor程式設計,而將具體資料轉換類名儲存在配置檔案中,符合依賴倒轉原則。根據里氏代換原則,程式執行時,具體資料轉換類物件將替換DataConvertor型別的物件,程式不會出現任何問題。更換具體資料轉換類時無須修改原始碼,只需要修改配置檔案;如果需要增加新的具體資料轉換類,只要將新增資料轉換類作為DataConvertor的子類並修改配置檔案即可,原有程式碼無須做任何修改,滿足開閉原則。重構後的結構如圖2所示:
圖2重構後的結構圖
在上述重構過程中,我們使用了開閉原則、里氏代換原則和依賴倒轉原則,在大多數情況下,這三個設計原則會同時出現,開閉原則是目標,里氏代換原則是基礎,依賴倒轉原則是手段,它們相輔相成,相互補充,目標一致,只是分析問題時所站角度不同而已。
擴充套件
Robert C. Martin(Bob大叔):Object Mentor公司總裁,面向物件設計、模式、UML、敏捷方法學和極限程式設計領域內的資深顧問。
八、 面向物件設計原則之介面隔離原則
介面隔離原則定義如下:
介面隔離原則(Interface Segregation Principle, ISP):使用多個專門的介面,而不使用單一的總介面,即客戶端不應該依賴那些它不需要的介面。
根據介面隔離原則,當一個介面太大時,我們需要將它分割成一些更細小的介面,使用該介面的客戶端僅需知道與之相關的方法即可。每一個介面應該承擔一種相對獨立的角色,不幹不該乾的事,該乾的事都要幹。這裡的“介面”往往有兩種不同的含義:一種是指一個型別所具有的方法特徵的集合,僅僅是一種邏輯上的抽象;另外一種是指某種語言具體的“介面”定義,有嚴格的定義和結構,比如Java語言中的interface。對於這兩種不同的含義,ISP的表達方式以及含義都有所不同:
(1) 當把“介面”理解成一個型別所提供的所有方法特徵的集合的時候,這就是一種邏輯上的概念,介面的劃分將直接帶來型別的劃分。可以把介面理解成角色,一個介面只能代表一個角色,每個角色都有它特定的一個介面,此時,這個原則可以叫做“角色隔離原則”。
(2) 如果把“介面”理解成狹義的特定語言的介面,那麼ISP表達的意思是指介面僅僅提供客戶端需要的行為,客戶端不需要的行為則隱藏起來,應當為客戶端提供儘可能小的單獨的介面,而不要提供大的總介面。在面向物件程式語言中,實現一個介面就需要實現該介面中定義的所有方法,因此大的總介面使用起來不一定很方便,為了使介面的職責單一,需要將大介面中的方法根據其職責不同分別放在不同的小介面中,以確保每個介面使用起來都較為方便,並都承擔某一單一角色。介面應該儘量細化,同時介面中的方法應該儘量少,每個介面中只包含一個客戶端(如子模組或業務邏輯類)所需的方法即可,這種機制也稱為“定製服務”,即為不同的客戶端提供寬窄不同的介面。
下面通過一個簡單例項來加深對介面隔離原則的理解:
Sunny軟體公司開發人員針對某CRM系統的客戶資料顯示模組設計瞭如圖1所示介面,其中方法dataRead()用於從檔案中讀取資料,方法transformToXML()用於將資料轉換成XML格式,方法createChart()用於建立圖表,方法displayChart()用於顯示圖表,方法createReport()用於建立文字報表,方法displayReport()用於顯示文字報表。
圖1 初始設計方案結構圖
在實際使用過程中發現該介面很不靈活,例如如果一個具體的資料顯示類無須進行資料轉換(原始檔本身就是XML格式),但由於實現了該介面,將不得不實現其中宣告的transformToXML()方法(至少需要提供一個空實現);如果需要建立和顯示圖表,除了需實現與圖表相關的方法外,還需要實現建立和顯示文字報表的方法,否則程式編譯時將報錯。
現使用介面隔離原則對其進行重構。
在圖1中,由於在介面CustomerDataDisplay中定義了太多方法,即該介面承擔了太多職責,一方面導致該介面的實現類很龐大,在不同的實現類中都不得不實現介面中定義的所有方法,靈活性較差,如果出現大量的空方法,將導致系統中產生大量的無用程式碼,影響程式碼質量;另一方面由於客戶端針對大介面程式設計,將在一定程式上破壞程式的封裝性,客戶端看到了不應該看到的方法,沒有為客戶端定製介面。因此需要將該介面按照介面隔離原則和單一職責原則進行重構,將其中的一些方法封裝在不同的小介面中,確保每一個介面使用起來都較為方便,並都承擔某一單一角色,每個介面中只包含一個客戶端(如模組或類)所需的方法即可。
通過使用介面隔離原則,本例項重構後的結構如圖2所示:
圖2 重構後的結構圖
在使用介面隔離原則時,我們需要注意控制介面的粒度,介面不能太小,如果太小會導致系統中介面氾濫,不利於維護;介面也不能太大,太大的介面將違背介面隔離原則,靈活性較差,使用起來很不方便。一般而言,介面中僅包含為某一類使用者定製的方法即可,不應該強迫客戶依賴於那些它們不用的方法。
擴充套件
在《敏捷軟體開發——原則、模式與實踐》一書中,RobertC. Martin從解決“介面汙染”的角度對介面隔離原則進行了詳細的介紹,大家可以參閱該書第12章——介面隔離原則(ISP)進行深入的學習。
九、 面向物件設計原則之合成複用原則
合成複用原則又稱為組合/聚合複用原則(Composition/Aggregate Reuse Principle, CARP),其定義如下:
合成複用原則(Composite Reuse Principle, CRP):儘量使用物件組合,而不是繼承來達到複用的目的。
合成複用原則就是在一個新的物件裡通過關聯關係(包括組合關係和聚合關係)來使用一些已有的物件,使之成為新物件的一部分;新物件通過委派呼叫已有物件的方法達到複用功能的目的。簡言之:複用時要儘量使用組合/聚合關係(關聯關係),少用繼承。
在面向物件設計中,可以通過兩種方法在不同的環境中複用已有的設計和實現,即通過組合/聚合關係或通過繼承,但首先應該考慮使用組合/聚合,組合/聚合可以使系統更加靈活,降低類與類之間的耦合度,一個類的變化對其他類造成的影響相對較少;其次才考慮繼承,在使用繼承時,需要嚴格遵循里氏代換原則,有效使用繼承會有助於對問題的理解,降低複雜度,而濫用繼承反而會增加系統構建和維護的難度以及系統的複雜度,因此需要慎重使用繼承複用。
通過繼承來進行復用的主要問題在於繼承複用會破壞系統的封裝性,因為繼承會將基類的實現細節暴露給子類,由於基類的內部細節通常對子類來說是可見的,所以這種複用又稱“白箱”複用,如果基類發生改變,那麼子類的實現也不得不發生改變;從基類繼承而來的實現是靜態的,不可能在執行時發生改變,沒有足夠的靈活性;而且繼承只能在有限的環境中使用(如類沒有宣告為不能被繼承)。
擴充套件
對於繼承的深入理解,大家可以參考《軟體架構設計》一書作者溫昱先生的文章——《見山只是山見水只是水——提升對繼承的認識》。
由於組合或聚合關係可以將已有的物件(也可稱為成員物件)納入到新物件中,使之成為新物件的一部分,因此新物件可以呼叫已有物件的功能,這樣做可以使得成員物件的內部實現細節對於新物件不可見,所以這種複用又稱為“黑箱”複用,相對繼承關係而言,其耦合度相對較低,成員物件的變化對新物件的影響不大,可以在新物件中根據實際需要有選擇性地呼叫成員物件的操作;合成複用可以在執行時動態進行,新物件可以動態地引用與成員物件型別相同的其他物件。
一般而言,如果兩個類之間是“Has-A”的關係應使用組合或聚合,如果是“Is-A”關係可使用繼承。"Is-A"是嚴格的分類學意義上的定義,意思是一個類是另一個類的"一種";而"Has-A"則不同,它表示某一個角色具有某一項責任。
下面通過一個簡單例項來加深對合成複用原則的理解:
Sunny軟體公司開發人員在初期的CRM系統設計中,考慮到客戶數量不多,系統採用MySQL作為資料庫,與資料庫操作有關的類如CustomerDAO類等都需要連線資料庫,連線資料庫的方法getConnection()封裝在DBUtil類中,由於需要重用DBUtil類的getConnection()方法,設計人員將CustomerDAO作為DBUtil類的子類,初始設計方案結構如圖1所示:
圖1 初始設計方案結構圖
隨著客戶數量的增加,系統決定升級為Oracle資料庫,因此需要增加一個新的OracleDBUtil類來連線Oracle資料庫,由於在初始設計方案中CustomerDAO和DBUtil之間是繼承關係,因此在更換資料庫連線方式時需要修改CustomerDAO類的原始碼,將CustomerDAO作為OracleDBUtil的子類,這將違反開閉原則。【當然也可以修改DBUtil類的原始碼,同樣會違反開閉原則。】
現使用合成複用原則對其進行重構。
根據合成複用原則,我們在實現複用時應該多用關聯,少用繼承。因此在本例項中我們可以使用關聯複用來取代繼承複用,重構後的結構如圖2所示:
圖2 重構後的結構圖
在圖2中,CustomerDAO和DBUtil之間的關係由繼承關係變為關聯關係,採用依賴注入的方式將DBUtil物件注入到CustomerDAO中,可以使用構造注入,也可以使用Setter注入。如果需要對DBUtil的功能進行擴充套件,可以通過其子類來實現,如通過子類OracleDBUtil來連線Oracle資料庫。由於CustomerDAO針對DBUtil程式設計,根據里氏代換原則,DBUtil子類的物件可以覆蓋DBUtil物件,只需在CustomerDAO中注入子類物件即可使用子類所擴充套件的方法。例如在CustomerDAO中注入OracleDBUtil物件,即可實現Oracle資料庫連線,原有程式碼無須進行修改,而且還可以很靈活地增加新的資料庫連線方式。
十、 面向物件設計原則之迪米特法則
迪米特法則來自於1987年美國東北大學(Northeastern University)一個名為“Demeter”的研究專案。迪米特法則又稱為最少知識原則(LeastKnowledge Principle, LKP),其定義如下:
迪米特法則(Law of Demeter, LoD):一個軟體實體應當儘可能少地與其他實體發生相互作用。
如果一個系統符合迪米特法則,那麼當其中某一個模組發生修改時,就會盡量少地影響其他模組,擴充套件會相對容易,這是對軟體實體之間通訊的限制,迪米特法則要求限制軟體實體之間通訊的寬度和深度。迪米特法則可降低系統的耦合度,使類與類之間保持鬆散的耦合關係。
迪米特法則還有幾種定義形式,包括:不要和“陌生人”說話、只與你的直接朋友通訊等,在迪米特法則中,對於一個物件,其朋友包括以下幾類:
(1) 當前物件本身(this);
(2) 以引數形式傳入到當前物件方法中的物件;
(3) 當前物件的成員物件;
(4) 如果當前物件的成員物件是一個集合,那麼集合中的元素也都是朋友;
(5) 當前物件所建立的物件。
任何一個物件,如果滿足上面的條件之一,就是當前物件的“朋友”,否則就是“陌生人”。在應用迪米特法則時,一個物件只能與直接朋友發生互動,不要與“陌生人”發生直接互動,這樣做可以降低系統的耦合度,一個物件的改變不會給太多其他物件帶來影響。
迪米特法則要求我們在設計系統時,應該儘量減少物件之間的互動,如果兩個物件之間不必彼此直接通訊,那麼這兩個物件就不應當發生任何直接的相互作用,如果其中的一個物件需要呼叫另一個物件的某一個方法的話,可以通過第三者轉發這個呼叫。簡言之,就是通過引入一個合理的第三者來降低現有物件之間的耦合度。
在將迪米特法則運用到系統設計中時,要注意下面的幾點:在類的劃分上,應當儘量建立鬆耦合的類,類之間的耦合度越低,就越有利於複用,一個處在鬆耦合中的類一旦被修改,不會對關聯的類造成太大波及;在類的結構設計上,每一個類都應當儘量降低其成員變數和成員函式的訪問許可權;在類的設計上,只要有可能,一個型別應當設計成不變類;在對其他類的引用上,一個物件對其他物件的引用應當降到最低。
下面通過一個簡單例項來加深對迪米特法則的理解:
Sunny軟體公司所開發CRM系統包含很多業務操作視窗,在這些視窗中,某些介面控制元件之間存在複雜的互動關係,一個控制元件事件的觸發將導致多個其他介面控制元件產生響應,例如,當一個按鈕(Button)被單擊時,對應的列表框(List)、組合框(ComboBox)、文字框(TextBox)、文字標籤(Label)等都將發生改變,在初始設計方案中,介面控制元件之間的互動關係可簡化為如圖1所示結構:
圖1 初始設計方案結構圖
在圖1中,由於介面控制元件之間的互動關係複雜,導致在該視窗中增加新的介面控制元件時需要修改與之互動的其他控制元件的原始碼,系統擴充套件性較差,也不便於增加和刪除新控制元件。
現使用迪米特對其進行重構。
在本例項中,可以通過引入一個專門用於控制介面控制元件互動的中間類(Mediator)來降低介面控制元件之間的耦合度。引入中間類之後,介面控制元件之間不再發生直接引用,而是將請求先轉發給中間類,再由中間類來完成對其他控制元件的呼叫。當需要增加或刪除新的控制元件時,只需修改中間類即可,無須修改新增控制元件或已有控制元件的原始碼,重構後結構如圖2所示:
僅供個人學習,如有抄襲請包容.....
相關推薦
設計模式的基礎篇相關知識,簡單易懂。
一、 設計模式概述(一) 1) 概要 關於金庸小說中到底是招式重要還是內功重要的爭論從未停止,我們在這裡並不分析張無忌的九陽神功和令狐沖的獨孤九劍到底哪個更厲害,但我想每個武林人士夢寐以求的應該是既有淋漓的招式又有深厚的內功。看到這裡大家可能會產生疑問了?搞什麼,討論什麼招式
設計模式之十一個行為型模式的相關知識,簡單易懂。
一、 職責鏈模式-Chain of Responsibility Pattern 1) 請求的鏈式處理——職責鏈模式(一) “一對二”,“過”,“過”……這聲音熟悉嗎?你會想到什麼?對!紙牌。在類似“鬥地主”這樣的紙牌遊戲中,某人出牌給他的下家,下家看看手中的牌,
演算法與資料結構之排序演算法的相關知識,簡單易懂。
一、 氣泡排序 1) 概要 本章介紹排序演算法中的氣泡排序,重點講解氣泡排序的思想。 目錄 1. 氣泡排序介紹 2. 氣泡排序圖文說明 3. 氣泡排序的時間複雜度和穩定性 4. 氣泡排序實現 4.1 氣泡排序C實現 4.2 氣泡排序C++實現 4.
演算法與資料結構之堆的相關知識,簡單易懂。
十、 二叉堆之Java的實現 1) 概要 前面分別通過C和C++實現了二叉堆,本章給出二叉堆的Java版本。還是那句話,它們的原理一樣,擇其一瞭解即可。 目錄 1. 二叉堆的介紹 2. 二叉堆的圖文解析 3. 二叉堆的Java實現(完整原始碼) 4. 二叉堆的Java測試程式
演算法與資料結構之圖的相關知識,簡單易懂。
一、 圖的理論基礎 1) 概要 本章介紹資料結構中圖的基本概念。 目錄 1. 圖的基本概念 2. 圖的儲存結構 2) 圖的基本概念 1. 圖的定義 定義:圖(graph)是由一些點(vertex)和這些點之間的連線(edge)所組成的;其中,點通常被
總結了一些SpringMVC的一些重點知識,簡單易懂02
1.處理模型資料 如果跳轉時需要帶資料:V、M,則可以使用以下方式: ModelAndView、ModelMap、Map、Model ——資料放在了request作用域 @SessionAttributes、@ModelAttribute
總結了一些springmvc的一些重點知識,簡單易懂01
檢查不細心錯誤: 1.database.properties 檢視資料庫名稱是否對應 2.applicationContext.xml 檢視mapper.xml對映檔案 3.Pe
java中實現單例模式的幾種方式,簡單易懂
一、餓漢式單例類 public class Singleton { private Singleton(){ } private static Singleton instance = new Singleton();
用C++實現七種排序演算法,可選擇排序方法,簡單易懂。
最近學習演算法,先從簡答的開始學起,用C++做了一個實現七種排序演算法的介面,可選擇想要用的選擇演算法,不過,由於時間倉促,沒有來得及優化和程式碼註釋,後期還會加上程式碼註釋,隨便優化一下程式碼,提高執行效率; /***********************
MVVM設計模式基礎知識--ICommand接口
don ren 命令 chan ring exc fadein 什麽 復制 命令是 Windows Presentation Foundation (WPF) 中的輸入機制,它提供的輸入處理比設備輸入具有更高的語義級別。 命令有若幹用途: 第一個用途
圖層知識,簡單講解一下27種圖層混合模式的用法
圖層混合模式種類較多,即使一些老手也不完全瞭解,今天就跟大家講講,這二十七種混合模式的簡單用法。 嗯,讓我們從最最基礎的說起——畫素。你就把他當成一個個五顏六色的小色塊.這些小色塊們都有一個特定的標記值,比如,RGB值,CMYK值了。當然透明色塊也是畫素,不過
Java設計模式:設計模式基礎知識和原則
設計模式 概念 模式是在某種情景下(Context)下,針對某問題的設計的某種解決方案。 情景:是應用某個模式的情況。這應該是會不斷出現的情況。 問題:是你想在某情景下達到的目標,但也是某種情景下的約束 解決方案:是你所求的一個通用的設計,可用來
關於一些設計模式(面試小問題,Java篇)
今天在某公司(不透露了)寫了一套Java面試題。有個題大概是這樣的。 Which design pattern used in Java.lang.Runtime? 就是說java.lang.Runtime裡,用了什麼設計模式 還有java.utils.collectio
MVVM設計模式基礎知識--ICommand介面
命令是 Windows Presentation Foundation (WPF) 中的輸入機制,它提供的輸入處理比裝置輸入具有更高的語義級別。 命令有若干用途: 第一個用途是將語義以及呼叫命令的物件與執行命令的邏輯分離開來。這使得多個完全不同的源可以呼叫相
設計模式第二篇,鏈式方法模式
大家好,這是設計模式專題的第二篇文章,我們一起來聊聊鏈式方法的設計模式。 鏈式方法也叫做流式方法,是一種相對比較新的概念,經常在流式計算或者類似的場景當中出現。舉個例子,我們之前講過的pyspark當中就用了這個模式,比如當我們處理rdd的時候,經過轉換操作我們得到的仍然是一個rdd,直到遇到執行操作位置。d
【大話設計模式】——淺談設計模式基礎
表示 無用功 隱式 art -s -m 個人 pri one 初學設計模式給我最大的感受是:人類真是偉大啊!單單是設計模式的基礎課程就讓我感受到了強烈的生活氣息。個人感覺《大話設計模式》這本書寫的真好。讓貌似非常晦澀難懂的設計模式變的生活化。趣味化。 以下淺談一
設計模式基礎
avi art 分享 全部 針對 需要 耦合 class 設計 設計模式是一套被重復使用、大多數人知曉的,代碼設計經驗的總結,使用設計模式能提高代碼重用性,可理解性和代碼的可靠性。就好比大廈的設計結構一般。 在學習設計模
JAVA基礎篇—抽象類,抽象方法
java b- rec package rate idt -c over stub class Shape 1 package com.shape; 2 3 public abstract class Shape { 4 double area;//
java的設計模式的一些鏈接,站在巨人的肩膀上,才能看的更遠。(均來源與網上的各個大牛的博客中)
原型 責任 arc .cn java life tar 創建 解釋 創建型抽象工廠模式 http://www.cnblogs.com/java-my-life/archive/2012/03/28/2418836.html工廠方法 http://www.cnblogs.
大戰設計模式【4】—— 簡單工廠模式
ref ins 復雜 nsa sta 對象比較 cto cnblogs 配置文件 簡單工廠模式(Simple Factory) 設計模式使用的例子 https://github.com/LinkinStars/DesignPatternsAllExample 一、