SOLID 原則基本概念: 程式設計領域, SOLID (單一功能、開閉原則、里氏替換、介面隔離以及依賴反轉)是由羅伯特·C·馬丁在21世紀早期 引入的記憶術首字母縮略字,指代了面向對象編程和麵向對象設計的五個基本原則。當這些原則被一起應用時,它們使得一個程式員開發一個容易進行軟體維護和擴展的系統變 ...
SOLID 原則基本概念:
程式設計領域, SOLID (單一功能、開閉原則、里氏替換、介面隔離以及依賴反轉)是由羅伯特·C·馬丁在21世紀早期 引入的記憶術首字母縮略字,指代了面向對象編程和麵向對象設計的五個基本原則。當這些原則被一起應用時,它們使得一個程式員開發一個容易進行軟體維護和擴展的系統變得更加可能SOLID被典型的應用在測試驅動開發上,並且是敏捷開發以及自適應軟體開發的基本原則的重要組成部分。
[S] Single Responsibility Principle (單一功能原則)
單一功能原則 :單一功能原則 認為對象應該僅具有一種單一功能的概念。
換句話說就是讓一個類只做一種類型責任,當這個類需要承擔其他類型的責任的時候,就需要分解這個類。在所有的SOLID原則中,這是大多數開發人員感到最能完全理解的一條。嚴格來說,這也可能是違反最頻繁的一條原則了。單一責任原則可以看作是低耦合、高內聚在面向對象原則上的引申,將責任定義為引起變化的原因,以提高內聚性來減少引起變化的原因。責任過多,可能引起它變化的原因就越多,這將導致責任依賴,相互之間就產生影響,從而極大的損傷其內聚性和耦合度。單一責任,通常意味著單一的功能,因此不要為一個模塊實 現過多的功能點,以保證實體只有一個引起它變化的原因。
namespace SOLID { public class Users { /// <summary> /// 支付 /// </summary> public void Pay(){} /// <summary> /// 資料庫操作 /// </summary> public void DataAccess(){} /// <summary> /// 日誌操作 /// </summary> public void Logger(){} } }
在這個用戶類中有這三個功能:1.支付邏輯,2資料庫邏輯,3.日誌操作。如果將這三個功能結合在一個類中,可能會出現修改部分代碼時會破壞其他的部分。多個功能也使這個用戶類難以理解,降低了內聚性。所以最好就是將這個類分離為三個分離的類,每個類僅僅有一個功能。
namespace SOLID { /// <summary> /// 資料庫操作 /// </summary> class DataAccess { }
/// <summary> /// 日誌 /// </summary> class Logger { }
/// <summary> /// 支付 /// </summary> class Pay { } }
[o] Open Close Principle (開閉原則)
開閉原則(ocp) 認為“軟體體應該是對於擴展開放的,但是對於修改封閉的”的概念。
軟體實體應該是可擴展,而不可修改的。也就是說,對擴展是開放的,而對修改是封閉的。這個原則是諸多面向對象編程原則中最抽象、最難理解的一個。
對擴展開放,意味著有新的需求或變化時,可以對現有代碼進行擴展,以適應新的情況。
對修改封閉,意味著類一旦設計完成,就可以獨立完成其工作,而不要對類進行任何修改。
可以使用變化和不變來說明:封裝不變部分,開放變化部分,一般使用介面繼承實現方式來實現“開放”應對變化,說大白話就是:你不是要變化嗎?,那麼我就讓你繼承實現一個對象,用一個介面來抽象你的職責,你變化越多,繼承實現的子類就越多。
abstract class DataAccess { public abstract void OpenConnection(); public abstract void CloseConnection(); public abstract void ExecuteCommand(); } /// <summary> /// SQL /// </summary> class SqlDataAccess : DataAccess { /// <summary> /// 打開SQL資料庫 /// </summary> public override void OpenConnection(){} /// <summary> /// 關閉Sql數據連接 /// </summary> public override void CloseConnection(){} /// <summary> /// 執行Sql數據命令 /// </summary> public override void ExecuteCommand(){} } /// <summary> /// ORACLE /// </summary> class OracleDataAccess : DataAccess { /// <summary> /// 打開Oracle數據連接 /// </summary> public override void OpenConnection(){} /// <summary> /// 關閉Oracle數據連接 /// </summary> public override void CloseConnection(){} /// <summary> /// 執行Oracle數據命令 /// </summary> public override void ExecuteCommand(){} }
[L] Liskov Substitution Principle(里氏替換原則)
里氏替換原則 :里氏替換原則 認為“程式中的對象應該是可以在不改變程式正確性的前提下被它的子類所替換的”的概念。
軟體工程大師Robert C. Martin把里氏替換原則最終簡化為一句話:“Subtypes must be substitutable for their base types”。也就是,子類必須能夠替換成它們的基類。即:子類應該可以替換任何基類能夠出現的地方,並且經過替換以後,代碼還能正常工作。另外,不應該 在代碼中出現if/else之類對子類類型進行判斷的條件。里氏替換原則LSP是使代碼符合開閉原則的一個重要保證。正是由於子類型的可替換性才使得父類 型的模塊在無需修改的情況下就可以擴展。在很多情況下,在設計初期我們類之間的關係不是很明確,LSP則給了我們一個判斷和設計類之間關係的基準:需不需 要繼承,以及怎樣設計繼承關係。
當一個子類的實例應該能夠替換任何其超類的實例時,它們之間才具有is-A關係。繼承對於OCP,就相當於多態性對於里氏替換原則。子類可以代替基類,客戶使用基類,他們不需要知道派生類所做的事情。這是一個針對行為職責可替代的原則,如果S是T的子類型,那麼S對象就應該在不改變任何抽象屬性情況下替換所有T對象。
class Rectangle { protected int width = 0; protected int height = 0; public virtual void SetWidth(int width) { this.width = width; } public virtual void SetHeight(int height) { this.height = height; } public virtual int GetArea() { return this.width * this.height; } } class Square : Rectangle { public override void SetHeight(int height) { this.height = height; this.width = height; } public override void SetWidth(int width) { this.height = width; this.width = width; } }
[I] Interface Segregation Principle(介面隔離原則)
介面隔離原則 :介面隔離原則 認為“多個特定客戶端介面要好於一個寬泛用途的介面”的概念。
不能強迫用戶去依賴那些他們不使用的介面。換句話說,使用多個專門的介面比使用單一的總介面總要好。註意:在代碼中應用ISP並不一定意味著服務就是絕對安全的。仍然需要採用良好的編碼實踐,以確保正確的驗證與授權。
這個原則起源於施樂公司,他們需要建立了一個新的印表機系統,可以執行諸如裝訂的印刷品一套,傳真多種任務。此系統軟體創建從底層開始編製,並實現了這些 任務功能,但是不斷增長的軟體功能卻使軟體本身越來越難適應變化和維護。每一次改變,即使是最小的變化,有人可能需要近一個小時的重新編譯和重新部署。這 是幾乎不可能再繼續發展,所以他們聘請羅伯特Robert幫助他們。他們首先設計了一個主要類Job,幾乎能夠用於實現所有任務功能。只要調用Job類的 一個方法就可以實現一個功能,Job類就變動非常大,是一個胖模型啊,對於客戶端如果只需要一個列印功能,但是其他無關列印的方法功能也和其耦合,ISP 原則建議在客戶端和Job類之間增加一個介面層,對於不同功能有不同介面,比如列印功能就是Print介面,然後將大的Job類切分為繼承不同介面的子 類,這樣有一個Print Job類,等等。
interface IDataAccess { void OpenConnection(); void CloseConnection(); } interface ISqlDataAccess : IDataAccess { void ExecuteSqlCommand(); } interface IOracleDataAccess : IDataAccess { void ExecuteOracleCommand(); } class SqlDataAccess : ISqlDataAccess { /// <summary> /// 執行Sql數據命令 /// </summary> public void ExecuteSqlCommand(){} /// <summary> /// 打開Sql數據連接 /// </summary> public void OpenConnection(){} /// <summary> /// 關閉Sql數據連接 /// </summary> public void CloseConnection(){} } class OracleDataAccess : IOracleDataAccess { /// <summary> /// 執行Oracle數據命令 /// </summary> public void ExecuteOracleCommand(){} /// <summary> /// 打開Oracle數據連接 /// </summary> public void OpenConnection(){} /// <summary> /// 關閉Oracle數據連接 /// </summary> public void CloseConnection(){} }
[D] Dependency Inversion Principle(依賴反轉原則)
依賴反轉原則: 依賴反轉原則 認為一個方法應該遵從“依賴於抽象而不是一個實例” 的概念。依賴註入是該原則的一種實現方式。
依賴倒置原則(Dependency Inversion Principle,DIP)規定:代碼應當取決於抽象概念,而不是具體實現。
高層模塊不應該依賴於低層模塊,二者都應該依賴於抽象
抽象不應該依賴於細節,細節應該依賴於抽象
類可能依賴於其他類來執行其工作。但是,它們不應當依賴於該類的特定具體實現,而應當是它的抽象。這個原則實在是太重要了,社會的分工化,標準化都 是這個設計原則的體現。顯然,這一概念會大大提高系統的靈活性。如果類只關心它們用於支持特定契約而不是特定類型的組件,就可以快速而輕鬆地修改這些低級 服務的功能,同時最大限度地降低對系統其餘部分的影響。
interface IBankAccount { long BankNumber { get; set; } // 卡號 decimal Balance { get; set; } // 餘額 }
// 轉賬人 interface ITransferSource : IBankAccount { void CutPayment(decimal value); }
// 收款人 interface ITransferDestination : IBankAccount { void AddMoney(decimal value); }
class BankAccout : IBankAccount, ITransferSource, ITransferDestination { public long BankNumber { get; set; } public decimal Balance { get; set; } public void CutPayment(decimal value) { Balance -= value; } public void AddMoney(decimal value) { Balance += value; } }
class TransferAmount { public decimal Amount { get; set; } public void Transfer(ITransferSource source, ITransferDestination dest) { source.CutPayment(Amount); dest.AddMoney(Amount); } }
參考於:
http://www.cnblogs.com/Ax0ne/p/3619481.html
http://www.cnblogs.com/shanyou/archive/2009/09/21/1570716.html