日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

C#实践设计模式原则SOLID

發布時間:2023/12/4 C# 56 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C#实践设计模式原则SOLID 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

理論跟實踐的關系,說遠不遠,說近不近。能不能把理論用到實踐上,還真不好說。

?

通常講到設計模式,一個最通用的原則是SOLID:

  • S - Single Responsibility Principle,單一責任原則

  • O - Open Closed Principle,開閉原則

  • L - Liskov Substitution Principle,里氏替換原則

  • I - Interface Segregation Principle,接口隔離原則

  • D - Dependency Inversion Principle,依賴倒置原則

  • 嗯,這就是五大原則。

    后來又加入了一個:Law of Demeter,迪米特法則。于是,就變成了六大原則。

    ?

    原則好理解。怎么用在實踐中?

    一、單一責任原則

    單一責任原則,簡單來說就是一個類或一個模塊,只負責一種或一類職責。

    看代碼:

    public?interface?IUser {void?AddUser();void?RemoveUser();void?UpdateUser();void?Logger();void?Message(); }

    根據原則,我們會發現,對于IUser來說,前三個方法:AddUser、RemoveUser、UpdateUser是有意義的,而后兩個Logger和Message作為IUser的一部分功能,是沒有意義的并不符合單一責任原則的。

    所以,我們可以把它分解成不同的接口:

    public?interface?IUser {void?AddUser();void?RemoveUser();void?UpdateUser(); } public?interface?ILog {void?Logger(); } public?interface?IMessage {void?Message(); }

    拆分后,我們看到,三個接口各自完成自己的責任,可讀性和可維護性都很好。

    ?

    下面是使用的例子,采用依賴注入來做:

    public?class?Log?:?ILog {public?void?Logger(){Console.WriteLine("Logged?Error");} } public?class?Msg?:?IMessage {public?void?Message(){Console.WriteLine("Messaged?Sent");} } class?Class_DI {private?readonly?IUser?_user;private?readonly?ILog?_log;private?readonly?IMessage?_msg;public?Class_DI(IUser?user,?ILog?log,?IMessage?msg){this._user?=?user;this._log?=?log;this._msg?=?msg;}public?void?User(){this._user.AddUser();this._user.RemoveUser();this._user.UpdateUser();}public?void?Log(){this._log.Logger();}public?void?Msg(){this._msg.Message();} } public?static?void?Main() {Class_DI?di?=?new?Class_DI(new?User(),?new?Log(),?new?Msg());di.User();di.Log();di.Msg(); }

    這樣的代碼,看著就漂亮多了。

    二、開閉原則

    開閉原則要求類、模塊、函數等實體應該對擴展開放,對修改關閉。

    ?

    我們先來看一段代碼,計算員工的獎金:

    public?class?Employee {public?int?Employee_ID;public?string?Name;public?Employee(int?id,?string?name){this.Employee_ID?=?id;this.Name?=?name;}public?decimal?Bonus(decimal?salary){return?salary?*?.2M;} } class?Program {static?void?Main(string[]?args){Employee?emp?=?new?Employee(101,?"WangPlus");Console.WriteLine("Employee?ID:?{0}?Name:?{1}?Bonus:?{2}",?emp.Employee_ID,?emp.Name,?emp.Bonus(10000));} }

    現在假設,計算獎金的公式做了改動。

    要實現這個,我們可能需要對代碼進行修改:

    public?class?Employee {public?int?Employee_ID;public?string?Name;public?string?Employee_Type;public?Employee(int?id,?string?name,?string?type){this.Employee_ID?=?id;this.Name?=?name;this.Employee_Type?=?type;}public?decimal?Bonus(decimal?salary){if?(Employee_Type?==?"manager")return?salary?*?.2M;elsereturnsalary?*?.1M;} }

    顯然,為了實現改動,我們修改了類和方法。

    這違背了開閉原則。

    ?

    那我們該怎么做?

    我們可以用抽象類來實現 - 當然,實際有很多實現方式,選擇最習慣或自然的方式就成:

    public?abstract?class?Employee {public?int?Employee_ID;public?string?Name;public?Employee(int?id,?string?name){this.Employee_ID?=?id;this.Name?=?name;}public?abstract?decimal?Bonus(decimal?salary); }

    然后,我們再實現最初的功能:

    public?class?GeneralEmployee?:?Employee {public?GeneralEmployee(int?id,?string?name)?:?base(id,?name){}public?override?decimal?Bonus(decimal?salary){return?salary?*?.2M;} } class?Program {public?static?void?Main(){Employee?emp?=?new?GeneralEmployee(101,?"WangPlus");Console.WriteLine("Employee?ID:?{0}?Name:?{1}?Bonus:?{2}",?emp.Employee_ID,?emp.Name,?emp.Bonus(10000));} }

    在這兒使用抽象類的好處是:如果未來需要修改獎金規則,則不需要像前邊例子一樣,修改整個類和方法,因為現在的擴展是開放的。

    代碼寫完整了是這樣:

    public?abstract?class?Employee {public?int?Employee_ID;public?string?Name;public?Employee(int?id,?string?name){this.Employee_ID?=?id;this.Name?=?name;}public?abstract?decimal?Bonus(decimal?salary); }public?class?GeneralEmployee?:?Employee {public?GeneralEmployee(int?id,?string?name)?:?base(id,?name){}public?override?decimal?Bonus(decimal?salary){return?salary?*?.1M;} } public?class?ManagerEmployee?:?Employee {public?ManagerEmployee(int?id,?string?name)?:?base(id,?name){}public?override?decimal?Bonus(decimal?salary){return?salary?*?.2M;} } class?Program {public?static?void?Main(){Employee?emp?=?new?GeneralEmployee(101,?"WangPlus");Employee?emp1?=?new?ManagerEmployee(102,?"WangPlus1");Console.WriteLine("Employee?ID:?{0}?Name:?{1}?Bonus:?{2}",?emp.Employee_ID,?emp.Name,?emp.Bonus(10000));Console.WriteLine("Employee?ID:?{0}?Name:?{1}?Bonus:?{2}",?emp1.Employee_ID,?emp1.Name,?emp1.Bonus(10000));} }

    三、里氏替換原則

    里氏替換原則,講的是:子類可以擴展父類的功能,但不能改變基類原有的功能。它有四層含義:

  • 子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法;

  • 子類中可以增加自己的特有方法;

  • 當子類重載父類的方法時,方法的前置條件(形參)要比父類的輸入參數更寬松;

  • 當子類實現父類的抽象方法時,方法的后置條件(返回值)要比父類更嚴格。

  • 在前邊開閉原則中,我們的例子里,實際上也遵循了部分里氏替換原則,我們用GeneralEmployee和ManagerEmployee替換了父類Employee。

    ?

    還是拿代碼來說。

    假設需求又改了,這回加了一個臨時工,是沒有獎金的。

    public?class?TempEmployee?:?Employee {public?TempEmployee(int?id,?string?name)?:?base(id,?name){}public?override?decimal?Bonus(decimal?salary){throw?new?NotImplementedException();} } class?Program {public?static?void?Main(){Employee?emp?=?new?GeneralEmployee(101,?"WangPlus");Employee?emp1?=?new?ManagerEmployee(101,?"WangPlus1");Employee?emp2?=?new?TempEmployee(102,?"WangPlus2");Console.WriteLine("Employee?ID:?{0}?Name:?{1}?Bonus:?{2}",?emp.Employee_ID,?emp.Name,?emp.Bonus(10000));Console.WriteLine("Employee?ID:?{0}?Name:?{1}?Bonus:?{2}",?emp1.Employee_ID,?emp1.Name,?emp1.Bonus(10000));Console.WriteLine("Employee?ID:?{0}?Name:?{1}?Bonus:?{2}",?emp2.Employee_ID,?emp2.Name,?emp2.Bonus(10000));Console.ReadLine();} }

    顯然,這個方式不符合里氏替原則的第四條,它拋出了一個錯誤。

    所以,我們需要繼續修改代碼,并增加兩個接口:

    interface?IBonus {decimal?Bonus(decimal?salary); } interface?IEmployee {int?Employee_ID?{?get;?set;?}string?Name?{?get;?set;?}decimal?GetSalary(); } public?abstract?class?Employee?:?IEmployee,?IBonus {public?int?Employee_ID?{?get;?set;?}public?string?Name?{?get;?set;?}public?Employee(int?id,?string?name){this.Employee_ID?=?id;this.Name?=?name;}public?abstract?decimal?GetSalary();public?abstract?decimal?Bonus(decimal?salary); } public?class?GeneralEmployee?:?Employee {public?GeneralEmployee(int?id,?string?name)?:?base(id,?name){}public?override?decimal?GetSalary(){return?10000;}public?override?decimal?Bonus(decimal?salary){return?salary?*?.1M;} } public?class?ManagerEmployee?:?Employee {public?ManagerEmployee(int?id,?string?name)?:?base(id,?name){}public?override?decimal?GetSalary(){return?10000;}public?override?decimal?Bonus(decimal?salary){return?salary?*?.1M;} } public?class?TempEmployee?:?IEmployee {public?int?Employee_ID?{?get;?set;?}public?string?Name?{?get;?set;?}public?TempEmployee(int?id,?string?name){this.Employee_ID?=?id;this.Name?=?name;}public?decimal?GetSalary(){return?5000;} } class?Program {public?static?void?Main(){Employee?emp?=?new?GeneralEmployee(101,?"WangPlus");Employee?emp1?=?new?ManagerEmployee(102,?"WangPlus1");Console.WriteLine("Employee?ID:?{0}?Name:?{1}?Salary:?{2}?Bonus:{3}",?emp.Employee_ID,?emp.Name,?emp.GetSalary(),?emp.Bonus(emp.GetSalary()));Console.WriteLine("Employee?ID:?{0}?Name:?{1}?Salary:?{2}?Bonus:{3}",?emp1.Employee_ID,?emp1.Name,?emp1.GetSalary(),?emp1.Bonus(emp1.GetSalary()));List<IEmployee>?emp_list?=?new?List<IEmployee>();emp_list.Add(new?GeneralEmployee(101,?"WangPlus"));emp_list.Add(new?ManagerEmployee(102,?"WangPlus1"));emp_list.Add(new?TempEmployee(103,?"WangPlus2"));foreach?(var?obj?in?emp_list){Console.WriteLine("Employee?ID:?{0}?Name:?{1}?Salary:?{2}?",?obj.EmpId,?obj.Name,?obj.GetSalary());}} }

    四、接口隔離原則

    接口隔離原則要求客戶不依賴于它不使用的接口和方法;一個類對另一個類的依賴應該建立在最小的接口上。

    通常的做法,是把一個臃腫的接口拆分成多個更小的接口,以保證客戶只需要知道與它相關的方法。

    這個部分不做代碼演示了,可以去看看上邊單一責任原則里的代碼,也遵循了這個原則。

    五、依賴倒置原則

    依賴倒置原則要求高層模塊不能依賴于低層模塊,而是兩者都依賴于抽象。另外,抽象不應該依賴于細節,而細節應該依賴于抽象。

    看代碼:

    public?class?Message {public?void?SendMessage(){Console.WriteLine("Message?Sent");} } public?class?Notification {private?Message?_msg;public?Notification(){_msg?=?new?Message();}public?void?PromotionalNotification(){_msg.SendMessage();} } class?Program {public?static?void?Main(){Notification?notify?=?new?Notification();notify.PromotionalNotification();} }

    這個代碼中,通知完全依賴Message類,而Message類只能發送一種通知。如果我們需要引入別的類型,例如郵件和SMS,則需要修改Message類。

    下面,我們使用依賴倒置原則來完成這段代碼:

    public?interface?IMessage {void?SendMessage(); } public?class?Email?:?IMessage {public?void?SendMessage(){Console.WriteLine("Send?Email");} } public?class?SMS?:?IMessage {public?void?SendMessage(){Console.WriteLine("Send?Sms");} } public?class?Notification {private?IMessage?_msg;public?Notification(IMessage?msg){this._msg?=?msg;}public?void?Notify(){_msg.SendMessage();} } class?Program {public?static?void?Main(){Email?email?=?new?Email();Notification?notify?=?new?Notification(email);notify.Notify();SMS?sms?=?new?SMS();notify?=?new?Notification(sms);notify.Notify();} }

    通過這種方式,我們把代碼之間的耦合降到了最小。

    六、迪米特法則

    迪米特法則也叫最少知道法則。從稱呼就可以知道,意思是:一個對象應該對其它對象有最少的了解。

    在寫代碼的時候,盡可能少暴露自己的接口或方法。寫類的時候,能不public就不public,所有暴露的屬性、接口、方法,都是不得不暴露的,這樣能確保其它類對這個類有最小的了解。

    這個原則沒什么需要多講的,調用者只需要知道被調用者公開的方法就好了,至于它內部是怎么實現的或是有其他別的方法,調用者并不關心,調用者只關心它需要用的。反而,如果被調用者暴露太多不需要暴露的屬性或方法,那么就可能導致調用者濫用其中的方法,或是引起一些其他不必要的麻煩。

    ?

    最后說兩句:所謂原則,不是規則,不是硬性的規定。在代碼中,能靈活應用就好,不需要非拘泥于形式,但是,用好了,會讓代碼寫得很順手,很漂亮。

    ?

    (全文完)

    喜歡就來個三連,讓更多人因你而受益

    總結

    以上是生活随笔為你收集整理的C#实践设计模式原则SOLID的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。