* 軟體腐化的原因: 問題所在 設計目標 過於僵硬 可擴展性(新性能可以很容易加入系統)過於脆弱 靈活性(修改不會波及其它)復用率低 粘度過高 可插入性(新功能容易加入系統(氣囊加入方向盤)) * 提高系統可復用性的幾點原則:傳統復用:1. 代碼的粘帖復用2. 演算法的復用3. 數據結構的復用 * 可 ...
* 軟體腐化的原因:
問題所在 設計目標
----------------------------------------------------------------------------
過於僵硬 可擴展性(新性能可以很容易加入系統)
過於脆弱 靈活性(修改不會波及其它)
復用率低
粘度過高 可插入性(新功能容易加入系統(氣囊加入方向盤))
* 提高系統可復用性的幾點原則:
傳統復用:
1. 代碼的粘帖復用
2. 演算法的復用
3. 數據結構的復用
* 可維護性與可復用性並不完全一致
* 對可維護性的支持:
一、 "開放-封閉"原則(OCP)
Open-Closed Principle原則講的是:一個軟體實體應當對擴展開放,對修改關閉。
優點:
通過擴展已有軟體系統,可以提供新的行為,以滿足對軟體的新的需求,使變化中的軟體有一定的適應性和靈活性。
已有軟體模塊,特別是最重要的抽象層模塊不能再修改,這使變化中的軟體系統有一定的穩定性和延續性。
例子:玉帝招安美猴王
當年大鬧天宮便是美猴王對玉帝的新挑戰。美猴王說:"'皇帝輪流做,明年到我家。'只教他搬出去,將天宮讓於我!"對於這項挑戰,太白金星給玉皇大帝提出的建議是:"降一道招安聖旨,宣上界來…,一則不勞師動眾,二則收仙有道也。"
換而言之,不勞師動眾、不破壞天規便是"閉",收仙有道便是"開"。招安之道便是玉帝天庭的"開放-封閉"原則。
招安之法的關鍵便是不允許更改現有的天庭秩序,但允許將妖猴納入現有秩序中,從而擴展了這一秩序。用面向對象的語言來講,不允許更改的是系統的抽象層,而允許更改的是系統的實現層。
二、 里氏代換原則(LSP)
Liskov Substitution Principle(里氏代換原則):子類型(subtype)必須能夠替換它們的基類型。
白馬、黑馬
反過來的代換不成立
《墨子·小取》說:"娣,美人也,愛娣,非愛美人也……"娣便是妹妹,哥哥喜愛妹妹,是因為兩人是兄妹關係,而不是因為妹妹是個美人。因此,喜愛妹妹不等同於喜愛美人。用面向對象語言描述,美人是基類,妹妹是美人的子類。哥哥作為一個有"喜愛()"方法,接受妹妹作為參數。那麼,這個"喜愛()"方法一般不能接受美人的實例。
一個違反LSP的簡單例子(長方形和正方形)
public class Rectangle { private long width; private long height; public void setWidth(long width) { this.width = width; } public long getWidth() { return this.width; } public void setHeight(long height) { this.height = height; } public long getHeight() { return this.height; } } public class Square { private long side; public void setSide(long side) { this.side = side; } public long getSide() { return side; } } 正方形不可以做長方形的子類 using System; public class Rectangle { private long width; private long height; public void setWidth(long width) { this.width = width; } public long getWidth() { return this.width; } public void setHeight(long height) { this.height = height; } public long getHeight() { return this.height; } } public class Square : Rectangle { private long side; public void setWidth(long width) { setSide(width); } public long getWidth() { return getSide(); } public void setHeight(long height) { setSide(height); } public long getHeight() { return getSide(); } public long getSide() { return side; } public void setSide(long side) { this.side = side; } } public class SmartTest { public void resize(Rectangle r) { while (r.getHeight() >= r.getWidth() ) { r.setWidth(r.getWidth() + 1); } } }
在執行SmartTest的resize方法時,如果傳入的是長方形對象,當高度大於寬度時,會自動增加寬度直到超出高度。但是如果傳入的是正方形對象,則會陷入死迴圈。
代碼重構
public interface Quadrangle { public long getWidth(); public long getHeight(); } public class Rectangle : Quadrangle { private long width; private long height; public void setWidth(long width) { this.width = width; } public long getWidth() { return this.width; } public void setHeight(long height) { this.height = height; } public long getHeight() { return this.height; } } public class Square : Quadrangle { private long side; public void setSide(long side) { this.side = side; } public long getSide() { return side; } public long getWidth() { return getSide(); } public long getHeight() { return getSide(); } }
三、 依賴倒置原則(DIP)
依賴倒置(Dependence Inversion Principle)原則講的是:要依賴於抽象,不要依賴於具體。
簡單的說,依賴倒置原則要求客戶端依賴於抽象耦合。原則表述:
1,抽象不應當依賴於細節;細節應當依賴於抽象;
2,要針對介面編程,不針對實現編程。
反面例子:
缺點:耦合太緊密,Light發生變化將影響ToggleSwitch。
解決辦法一:
將Light作成Abstract,然後具體類繼承自Light。
優點:ToggleSwitch依賴於抽象類Light,具有更高的穩定性,而BulbLight與TubeLight繼承自Light,可以根據"開放-封閉"原則進行擴展。只要Light不發生變化,BulbLight與TubeLight的變化就不會波及ToggleSwitch。
缺點:如果用ToggleSwitch控制一臺電視就很困難了。總不能讓TV繼承自Light吧。
解決方法二:
優點:更為通用、更為穩定。
結論:
使用傳統過程化程式設計所創建的依賴關係,策略依賴於細節,這是糟糕的,因為策略受到細節改變的影響。依賴倒置原則使細節和策略都依賴於抽象,抽象的穩定性決定了系統的穩定性。
四、 介面隔離原則(ISP)
介面隔離原則(Interface Segregation Principle)講的是:使用多個專門的介面比使用單一的總介面總要好。換而言之,從一個客戶類的角度來講:一個類對另外一個類的依賴性應當是建立在最小介面上的。
過於臃腫的介面是對介面的污染。不應該強迫客戶依賴於它們不用的方法。
My object-oriented umbrella(摘自Design Patterns Explained)
Let me tell you about my great umbrella. It is large enough to get into! In fact, three or four other people can get in it with me. While we are in it, staying out of the rain, I can move it from one place to another. It has a stereo system to keep me entertained while I stay dry. Amazingly enough, it can also condition the air to make it warmer or colder. It is one cool umbrella.
My umbrella is convenient. It sits there waiting for me. It has wheels on it so that I do not have to carry it around. I don't even have to push it because it can propel itself. Sometimes, I will open the top of my umbrella to let in the sun. (Why I am using my umbrella when it is sunny outside is beyond me!)
In Seattle, there are hundreds of thousands of these umbrellas in all kinds of colors. Most people call them cars.
實現方法:
1、 使用委托分離介面
2、 使用多重繼承分離介面
五、 合成/聚合復用原則(CARP)
合成/聚合復用原則(Composite/Aggregate Reuse Principle或CARP)經常又叫做合成復用原則(Composite Reuse Principle或CRP),就是在一個新的對象裡面使用一些已有的對象,使之成為新對象的一部分;新對象通過向這些對象的委派達到復用已有功能的目的。
簡而言之,要儘量使用合成/聚合,儘量不要使用繼承。
o Design to interfaces.
o Favor composition over inheritance.
o Find what varies and encapsulate it.
(摘自:Design Patterns Explained)
區分"Has-A"與"Is-A"
"Is-A"是嚴格的分類學意義上定義,意思是一個類是另一個類的"一種"。而"Has-A"則不同,它表示某一個角色具有某一項責任。
導致錯誤的使用繼承而不是合成/聚合的一個常見的原因是錯誤的把"Has-A"當作"Is-A"。
例如:
實際上,雇員、經理、學生描述的是一種角色,比如一個人是"經理"必然是"雇員",另外一個人可能是"學生雇員",在上面的設計中,一個人無法同時擁有多個角色,是"雇員"就不能再是"學生"了,這顯然是不合理的。
錯誤源於把"角色"的等級結構與"人"的等級結構混淆起來,誤把"Has-A"當作"Is-A"。解決辦法:
六、 迪米特法則(LoD)
迪米特法則(Law of Demeter或簡寫LoD)又叫最少知識原則(Least Knowledge Principle或簡寫為LKP),也就是說,一個對象應當對其它對象有儘可能少的瞭解。
其它表述:
只與你直接的朋友們通信
不要跟"陌生人"說話
每一個軟體單位對其它的單位都只有最少的知識,而且局限於那些與本單位密切相關的軟體單位。
迪米特法則與設計模式
Facade模式、Mediator模式
使民無知
《老子》第三章曰:"是以聖人之治,虛其心,實其腹,弱其志,常使民無知無欲。"使被"統治"的對象"愚昧"化,處於"無知"的狀態,可以使"統治"的成本降低。
所謂"最少知識"原則,實際上便是老子的"使民無知"的統治之術。
不相往來
《老子》雲:"小國寡民……鄰國相望,雞犬之聲相聞,民至老死,不相往來。"將被統治的對象隔離開來,使它們沒有直接的通信,可以達到分化瓦解,繼而分而治之的效果。迪米特法則與老子的"小國寡民"的統治之術不謀而合。
參考文獻:
閻巨集,《Java與模式》,電子工業出版社
[美]James W. Cooper,《C#設計模式》,電子工業出版社
[美]Alan Shalloway James R. Trott,《Design Patterns Explained》,中國電力出版社
[美]Robert C. Martin,《敏捷軟體開發-原則、模式與實踐》,清華大學出版社
[美]Don Box, Chris Sells,《.NET本質論 第1捲:公共語言運行庫》,中國電力出版社
http://www.dofactory.com/Patterns/Patterns.aspx
以上內容摘抄自:http://www.cnblogs.com/zhenyulu/articles/36061.html