1. 程式人生 > >SOLID面向物件設計原則

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

void Show()

    {

        //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

    }

總結

個物件只承擔一種責任,所有服務介面只通過它來執行這種任務。

擴充套件行為開放,向修改行為關閉。

子類應該可以用來替代它所繼承的類。

一個類對另一個類的依賴應該限制在最小化的介面上。

依賴抽象層(介面、抽象類),而不是具體類。

 

好處:

高內聚低耦合

簡潔明瞭

可讀性高

可維護性高

良好的設計、儘可能避免問題的出現

問題出現時,儘早的解決、低風險

 

通常,符合這樣的設計規範的程式碼就是簡短、靈活、易懂,也就是,好的程式碼!