1. 程式人生 > >六大設計原則【單一職責】【里氏替換】【 迪米特法則】

六大設計原則【單一職責】【里氏替換】【 迪米特法則】

設計模式:面嚮物件語言開發過程中,遇到種種的場景和問題,提出的解決方案和思路,沉澱下來,設計模式是解決具體問題的套路

設計模式六大原則:面嚮物件語言開發過程中,推薦的一些指導性原則,這些是沒有明確的招數的,而且也經常被忽視或者違背!

 

一:單一職責原則(Single Responsibility Principle)

單一職責原則就是一個類只負責一件事兒,面嚮物件語言開發,類就是一個最基本的單位,單一職責的原則就是封裝的粒度,主要關注的單個類實現的功能

比如我們下面的例子

 1 /// <summary>
 2 /// 封裝
 3 /// 動物類
 4 /// 簡單意味著穩定
5 /// </summary> 6 public class Animal 7 { 8 private string _Name = null; 9 public Animal(string name) 10 { 11 this._Name = name; 12 } 13 14 //應該拆分了 15 public void Action() 16 { 17 if (this._Name.Equals("")) 18 Console.WriteLine($"{this._Name} flying
"); 19 else if (this._Name.Equals("")) 20 Console.WriteLine($"{this._Name} walking"); 21 else if (this._Name.Equals("")) 22 Console.WriteLine($"{this._Name} Swimming"); 23 else if (this._Name.Equals("蚯蚓")) 24 Console.WriteLine($"{this._Name} Crawling
"); 25 } 26 }

我們宣告一個動物,但是每個動物的action是不一樣的,順著我們的思維模式,我們會在action中增加對應的if來判斷,不同的動物有不同的動作,

類似於這樣的,在一個方法中寫分支判斷,然後執行不同的邏輯,這就違背了單一職責原則,但是其實我們想要的功能完全能實現,如果種類比較少且不變的情況下,我們完全可以這樣操作,但是如果種類比較多且經常容易發生改變,那我們這樣寫就有很大的隱患,因為其中的改變有可能會影響到其它的。

我們可以對其進行改變,比如我們可以先建立一個基類

 1  public abstract class AbstractAnimal
 2  {
 3      protected string _Name = null;
 4      public AbstractAnimal(string name)
 5      {
 6          this._Name = name;
 7      }
 8 
 9      public abstract void Breath();
10      public abstract void Action();
11  }

然後可以建立不同動物的類來繼承於這個基類

public class Fish : AbstractAnimal
{
    public Fish() : base("")
    {
    }

    public override void Breath()
    {
        Console.WriteLine($"{base._Name} 呼吸水");
    }
    public override void Action()
    {
        Console.WriteLine($"{base._Name} swimming");
    }
}
public class Chicken : AbstractAnimal
{
  public Chicken() : base("")
    {
    }

    public override void Breath()
    {
        Console.WriteLine($"{base._Name} 呼吸空氣");
    }
    public override void Action()
    {
        Console.WriteLine($"{base._Name} flying");
    }
}

類似於這樣的,然後在自己的類中實現自己的方法,一個類只負責自己的事情,且都比較單一,簡單意味著穩定,意味著強大,這就是所謂的單一職責原則,那麼究竟什麼時候回使用單一職責原則呢?如果型別複雜,方法多,這樣建議使用單一職責原則!

那麼使用單一職責原則也有自己的弊端,具體分為以下兩個方面

1:程式碼量的增加(拆分開類的程式碼明顯比之前增加)

2:使用成本就是所謂的理解成本增高(呼叫者要曉得不同的類)

具體的單一原則分為以下五種

1:方法級別的單一職責原則:一個方法只負責一件事兒(職責分拆小方法,分支邏輯分拆)
2:類級別的單一職責原則:一個類只負責一件事兒
3:類庫級別的單一職責原則:一個類庫應該職責清晰
4:專案級別的單一職責原則:一個專案應該職責清晰(客戶端/管理後臺/後臺服務/定時任務/分散式引擎)
5:系統級別的單一職責原則:為通用功能拆分系統(IP定位/日誌/線上統計)

 

二: 里氏替換原則(Liskov Substitution Principle)

任何使用基類的地方,都可以透明的使用其子類,這主要是指 繼承+透明(安全,不會出現行為不一致)

繼承:子類擁有父類的一切屬性和行為,任何父類出現的地方,都可以用子類來代替,主要是因為:

1:父類有的,子類是必須有的(私有不繼承);如果出現了子類沒有的東西,那麼就應該斷掉繼承;、

2:子類可以有自己的屬性和行為,但是子類出現的地方,父類不一定能代替

3:父類實現的東西,子類就不要再寫了,(就是不要new隱藏),如果想修改父類的行為,通過abstract/virtual

舉個例子:

 1 public class People
 2 {
 3     public int Id { get; set; }
 4     public string Name { get; set; }
 5 
 7     public void Traditional()
 8     {
 9         Console.WriteLine("仁義禮智信 溫良恭儉讓 ");
10     }
11 }
12 
13 public class Chinese : People
14 {
15     public string Kuaizi { get; set; }
16     public void SayHi()
17     {
18         Console.WriteLine("早上好,吃了嗎?");
19     }
20 
21 }
22 
23 public class Hubei : Chinese
24 {
25     public string Majiang { get; set; }
26     public new void SayHi()
27     {
28         Console.WriteLine("早上好,過早了麼?");
29     }
30 }

呼叫的時候:

{
    Chinese people = new Chinese();
    people.Traditional();
    people.SayHi();
}
{
    Chinese people = new Hubei();
    people.Traditional();
    people.SayHi();

}
{
    var people = new Hubei();
    people.Traditional();
    people.SayHi();
}

上面需要注意的是:如果是普通的方法,以左邊為主,就是左邊是什麼類,就呼叫誰的普通方法(編譯時決定),如果是abstract或者virtual則是以右邊為主(執行時決定),所以:父類有的方法,子類就不要再寫了,(就是不要new隱藏),如果想修改父類的方法,通過abstract/virtual來標識!

三:迪米特法則

也叫最少知道原則,就是:一個物件應該對其他物件保持最少的瞭解,只與直接的朋友通訊。

他主要的職責就是關注類與類之間的互動,降低類與類之間的耦合,儘量避免依賴更多的型別

舉例說明:

有一個學生類,班級類,學校類

/// <summary>
/// 學生
/// </summary>
public class Student
{
    public int Id { get; set; }
    public string StudentName { get; set; }
    public int Height { private get; set; }

    public int Salay;

    public void ManageStudent()
    {
        Console.WriteLine(" {0}Manage {1} ", this.GetType().Name, this.StudentName);
    }
}
 /// <summary>
 /// 班級
 /// </summary>
 public class Class
 {
     public int Id { get; set; }
     public string ClassName { get; set; }

     public List<Student> StudentList { get; set; }


     public void ManageClass()
     {
         Console.WriteLine(" {0}Manage {1} ", this.GetType().Name, this.ClassName);
         foreach (Student s in this.StudentList)
         {
             s.ManageStudent();
         }
     }
 }
 1 /// <summary>
 2 /// 學校
 3 /// </summary>
 4 public class School
 5 {
 6     public int Id { get; set; }
 7     public string SchoolName { get; set; }
 8     public List<Class> ClassList { get; set; }
 9 
10     public void Manage()
11     {
12         Console.WriteLine("Manage {0}", this.GetType().Name);
13         foreach (Class c in this.ClassList)
14         {
15             //遵循了迪米特,school直接跟自己的朋友classList通訊,而不是跟自己的朋友的朋友通訊
16             c.ManageClass();
17 
18             #region 違背了迪米特法則(跟自己的朋友的朋友通訊)
19             //List<Student> studentList = c.StudentList;
20             //foreach (Student s in studentList)
21             //{
22             //    Console.WriteLine(" {0}Manage {1} ", s.GetType().Name, s.StudentName);
23             //}
24             #endregion
25 
26         }
27     }
28

現在的關係是:一個學校有多個班級,每個班級有多個學生,現在學校想要管理學生,學校可以直接跟班級通訊,然後班級跟學生通訊,這就是所謂的只與直接朋友通訊,然後避免依賴更多型別(這個型別不包含:基類庫BCL--框架內建)

其實類與類之間的關係可以總結為:

1:縱向:繼承≈實現(最密切)

2:橫向:聚合> 組合> 關聯> 依賴(出現在方法內部)

依賴別人更少,也讓別人瞭解更少,比如我們專案中經常中用到的一些訪問修飾符:

Private:私有
Protected:子類才能獲取到,子類才能訪問到
Internal :當前dll才能看見
Public:公開得
Protected internal 疊加要麼是子類, 要麼是相同類庫

 

其實專案中能體現迪米特法則的地方比如:三層架構(UI-BLL-DAL),還有我們習慣建立的中間層(UI-中間層--(呼叫不同的業務邏輯進行組合)),另外還有門面模式

 另外需要注意的是:單一職責法則只關注單類的功能;迪米特法則關注的是類與類之間的聯絡