SOLID面向物件設計原則
SRP |
The Single Responsibility Principle |
單一責任原則 |
OCP |
The Open Closed Principle |
開放封閉原則 |
LSP |
The Liskov Substitution Principle |
里氏替換原則 |
ISP |
The Interface Segregation Principle |
介面分離原則 |
DIP |
The Dependency Inversion Principle |
依賴倒置原則 |
SOLID--固體、穩定的
solid是5個原則的縮寫,這樣的設計具有穩定、可靠的特點。
單一職責SRP
一個類有且只有一個職責引起類改變的原因,只能有一個
//介面、方法 更容易實現此原則
任意小的改變都將導致這個單一類的變化。如果遵守 SRP,類將變得簡潔和靈活。
核心是把整個問題分為小部分,並且每個小部分都將通過一個單獨的類負責。
public class Tank
{//坦克類,業務邏輯和介面混合在一起
public void Move()
{
//left
//up
//right
//down
}
public
{
//windows show
}
}
//新需求:linux
面對新的需求,如何複用邏輯程式碼?
因此,應該將職責分離,降低耦合。
開放封閉原則
一個類應該對擴充套件開放,對修改關閉。
一個類已經開始使用,就不應該再修改。因為如果改變它,很可能你的改變會引發系統的崩潰。如果需要一些額外功能,應該擴充套件這個類而不是修改它。
使用這種方式,現有系統不會看到任何新變化的影響。同時,只需要測試新建立的類。
public class Calculator
{ //加減計算器
public double Calculate(double firstNumber, double secondNumber, string operate)
{
double result = 0d;
switch (operate)
{
case “+":
result= firstNumber+ secondNumber;
break;
case “-":
result= firstNumber- secondNumber;
break;
}
return result;
}
}
新需求:加入乘除功能
修改原來的類?影響到原有的功能怎麼辦?
更好的設計:
public class Operation
{ //抽象父類,擁有基本屬性
public double NumberA
{
}
public double NumberB
{
}
public virtual double GetResult()
{
double result = 0;
return result;
}
}
class OperationMul : Operation
{ //拓展的乘法子類,增加新功能時,直接增加一個類,而不是修改原來的程式碼
public override double GetResult()
{
double result = 0;
result = NumberA * NumberB;
return result;
}
}
也就是封裝變化,把頻繁改動的地方抽象
LSP裡式替換
父類出現的地方皆可以換成子類
上層模組不應關心底層模組的是如何工作的;同樣的介面模組之間,可以在不知道底層模組程式碼的情況下,進行替換。即介面或父類出現的地方,實現介面的類或子類可以代入。
class Program
{
static void Main(string[] args)
{
Animal animal = new Animal();//新來一隻牛(貓,狗,羊),要修改整個方法嗎?
animal.Show(); //或者拓展了Animal類,上層客戶端的程式碼都要改動嗎
animal.Eat();
animal.Run();
}
}
public class Cat:Animal
{ //子類繼承Animal,是對父類的拓展
}
//Animal animal = new Cat();//使用繼承&&多型,儘可能修改少的程式碼
正因為子型別的替換,才使得父類無須修改就可以擴充套件,
父類真正被服用,子類在此基礎上增加新的行為
注意:父類和子類須嚴格遵循is-a關係
介面隔離原則(ISP)
類不應該被迫依賴他們不使用的方法,也就是說一個介面應該擁有儘可能少的行為,它是精簡的,也是單一的。
使用多個專門的介面比使用單一的總介面要好
不應該依賴大的介面,應該裁減為小的介面給客戶模組使用,以減少依賴性
public interface Animal
{
public void eat(); // 吃
public void sleep(); // 睡
public void crawl(); // 爬
public void run(); // 跑
}
public class Bird : Animal
{
public void eat()
{
}
public void sleep()
{
}
public void crawl ()
{應單獨抽取出介面!
//鳥是動物,但不會爬
}
public void run()
{應單獨抽取出介面!
//鳥也不會跑
}
}
依賴注入或倒置原則(DIP)
1. 高層模組不應該依賴於低層模組,二者都應該依賴於抽象
2. 抽象不應該依賴於細節,細節應該依賴於抽象
public class Manager
{
public Notify notify = new Notify();//老式的呼叫
}
public class Notify
{
// Notify something
}
第一,當需要追加提供一種新的Notify時,對Manager層進行改動,增加了額外的工作。
第二,這種改動可能會影響到Manager ,帶來風險。
第三,改動後, Manager層必須重新再做測試。
public interface INotify
{
void Notify();
}
public class Manager:INotify
{
//do something
public void Notify()
{//只需要維護一個面向底層的介面,改動變小,儘可能遵守開閉原則
//亦可以使用反射或上層傳參的方式完全封閉這個類
public INotify notify = new EmailNotify();
}
//do somgthing
}
public class EmailNotify:INotify
{
//EmailNotify
}
public class PhoneNotify:INotify
{
//PhoneNotify
}
總結
一個物件只承擔一種責任,所有服務介面只通過它來執行這種任務。
向擴充套件行為開放,向修改行為關閉。
子類應該可以用來替代它所繼承的類。
一個類對另一個類的依賴應該限制在最小化的介面上。
依賴抽象層(介面、抽象類),而不是具體類。
好處:
高內聚低耦合
簡潔明瞭
可讀性高
可維護性高
良好的設計、儘可能避免問題的出現
問題出現時,儘早的解決、低風險
通常,符合這樣的設計規範的程式碼就是簡短、靈活、易懂,也就是,好的程式碼!