日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
面向?qū)ο笤O(shè)計(jì)原則

一 Single Responsibility Principle——單一職責(zé)原則

創(chuàng)新互聯(lián)公司是一家專業(yè)提供始興企業(yè)網(wǎng)站建設(shè),專注與成都做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、H5頁面制作、小程序制作等業(yè)務(wù)。10年已為始興眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)絡(luò)公司優(yōu)惠進(jìn)行中。

核心思想: 一個(gè)類應(yīng)該只有一個(gè)引起它變化的原因.

假設(shè)存在這樣的設(shè)計(jì). Rectangle類具有兩個(gè)方法,一個(gè)方法是計(jì)算矩形的面積 , 另一個(gè)方法是把矩形繪制在屏幕上.

CaculateArea方法只會(huì)進(jìn)行簡單的數(shù)學(xué)運(yùn)算,而Draw方法則調(diào)用GUI組件實(shí)現(xiàn)繪制矩形的功能. 顯然,這個(gè)類就包含了兩個(gè)不同的職責(zé)了. 那這樣又會(huì)帶來什么問題呢? 考慮這樣一個(gè)場景:現(xiàn)在有一個(gè)幾何學(xué)應(yīng)用程序調(diào)用了這一個(gè)類,已便實(shí)現(xiàn)計(jì)算面積的功能,在這個(gè)程序中不需要用到繪制矩形的功能. 問題一:部署幾何應(yīng)用程序需要把GUI組件一同部署,而且這個(gè)組件根本沒有使用到.問題二:對Rectangle類的改變,比如Draw方法改用另外一套GUI組件,必須對幾何應(yīng)用程序進(jìn)行一次重新部署.

可見,一個(gè)類如果承擔(dān)的職責(zé)過多,就等于把職責(zé)耦合在一起了,容易導(dǎo)致脆弱的設(shè)計(jì),帶來額外的麻煩. 在實(shí)際開發(fā)中, 業(yè)務(wù)規(guī)則的處理和數(shù)據(jù)持久化一般是不同時(shí)存在同一個(gè)類中的,業(yè)務(wù)規(guī)則往往會(huì)頻繁地變化,而持久化的方式卻不會(huì)經(jīng)常性地變化.如果這兩個(gè)職責(zé)混合在同一個(gè)類中,業(yè)務(wù)規(guī)則頻繁變化導(dǎo)致類的修改,只調(diào)用持久化方法的類也必須跟著重新編譯,部署的次數(shù)常常會(huì)超過我們希望的次數(shù). 對業(yè)務(wù)規(guī)則和持久化任務(wù)的職責(zé)分離就是遵循單一職責(zé)原則的體現(xiàn).

對上述Recangle類可進(jìn)行這樣的修改:

二 Open Closed Principle——開放封閉原則

核心思想:對擴(kuò)展開放,對修改封閉.

"需求總是變化的." 擁抱變化似乎就是軟件開發(fā)的真理之一. 經(jīng)常會(huì)有這樣令人沮喪的情景出現(xiàn):新的需求來了,對不起,我的代碼設(shè)計(jì)必須大幅度推倒重來. 設(shè)計(jì)的壞味道讓我們深受其害,那么怎樣的設(shè)計(jì)才能面對需求的改變卻可以保持相對穩(wěn)定呢?

針對這樣的問題,OCP給了我們?nèi)缦碌慕ㄗh:在發(fā)生變化的時(shí)候,不要修改類的源代碼,要通過添加新代碼來增強(qiáng)現(xiàn)有類的行為.

對擴(kuò)展開放,對修改封閉,這兩個(gè)特征似乎就是相互矛盾的. 通常觀念來講,擴(kuò)展不就是修改源代碼嗎?怎么可能在不改動(dòng)源代碼的情況下去更改它的行為呢?

答案就是抽象(Interface 和 抽象基類).實(shí)現(xiàn)OCP的核心思想就是對抽象編程. 讓類依賴于固定的抽象,對修改就是封閉的; 而通過面向?qū)ο蟮睦^承和多態(tài)機(jī)制,通過覆寫方法改變固有行為,實(shí)現(xiàn)新的擴(kuò)展方法,對于擴(kuò)展就是開放的.

來看一個(gè)例子. 實(shí)現(xiàn)一個(gè)能夠根據(jù)客戶端的調(diào)用要求繪制圓形和長方形的應(yīng)用程序. 初始設(shè)計(jì)如下:

 
 
 
  1. public class Draw  
  2. {  
  3.     public void DrawRectangle()  
  4.     {  
  5.         //繪制長方形  
  6.     }  
  7.  
  8.     public void DrawCircle()  
  9.     {  
  10.         //繪制圓形  
  11.     }  
  12. }  
  13.  
  14. public enum Sharp  
  15. {  
  16.     ///   
  17.     /// 長方形  
  18.     ///   
  19.     Rectangle ,  
  20.  
  21.     ///   
  22.     /// 圓形  
  23.     ///   
  24.     Circle ,  
  25. }  
  26.  
  27. public class DrawProcess  
  28. {  
  29.     private Draw _draw = new Draw();  
  30.  
  31.     public void Draw(Sharp sharp)  
  32.     {  
  33.         switch (sharp)  
  34.         {  
  35.             case Sharp.Rectangle:  
  36.                 _draw.DrawRectangle();  
  37.                 break;  
  38.             case Sharp.Circle:  
  39.                 _draw.DrawCircle();  
  40.                 break;  
  41.             default:  
  42.                 throw new Exception("調(diào)用出錯(cuò)!");  
  43.         }  
  44.     }  
  45. }  
  46.  
  47. //調(diào)用代碼  
  48. DrawProcess draw = new DrawProcess();  
  49. draw.Draw(Sharp.Circle); 

現(xiàn)在的代碼可以正確地運(yùn)行. 一切似乎都趨近于理想. 然而,需求的變更總是讓人防不勝防. 現(xiàn)在程序要求要實(shí)現(xiàn)可以繪制正方形. 在原本的代碼設(shè)計(jì)下,必須做如下的改動(dòng).

 
 
 
  1. //在Draw類中添加  
  2. public void DrawSquare()  
  3. {  
  4.      //繪制正方形  
  5. }  
  6.  
  7. //在枚舉Sharp中添加  
  8.  ///   
  9.  /// 正方形  
  10.  ///   
  11.  Square ,  
  12.  
  13. //在DrawProcess類的switch判斷中添加  
  14. case Sharp.Square:  
  15.      _draw.DrawSquare();  
  16.      break; 

需求的改動(dòng)產(chǎn)生了一系列相關(guān)模塊的改動(dòng),設(shè)計(jì)的壞味道悠然而生. 現(xiàn)在運(yùn)用OCP, 來看一下如何對代碼進(jìn)行一次重構(gòu).

 
 
 
  1. ///   
  2. /// 繪制接口  
  3. ///   
  4. public interface IDraw  
  5. {  
  6.     void Draw();  
  7. }  
  8.  
  9. public class Circle:IDraw  
  10. {  
  11.     public void Draw()  
  12.     {  
  13.         //繪制圓形  
  14.     }  
  15. }  
  16.  
  17. public class Rectangle:IDraw  
  18. {  
  19.     public void Draw()  
  20.     {  
  21.         //繪制長方形  
  22.     }  
  23. }  
  24.  
  25. public class DrawProcess  
  26. {  
  27.     private IDraw _draw;  
  28.  
  29.     public IDraw Draw { set { _draw = value; } }  
  30.       
  31.     private DrawProcess() { }  
  32.  
  33.     public DrawProcess(IDraw draw)  
  34.     {  
  35.         _draw = draw;  
  36.     }  
  37.  
  38.     public void DrawSharp()  
  39.     {  
  40.         _draw.Draw();  
  41.     }  
  42. }   
  43.  
  44.  
  45. //調(diào)用代碼  
  46. IDraw circle = new Circle();  
  47. DrawProcess draw = new DrawProcess(circle);  
  48. draw.DrawSharp(); 

假如現(xiàn)在需要有繪制正方形的功能,則只需添加一個(gè)類Square 即可.

 
 
 
  1. public class Square:IDraw  
  2. {  
  3.     public void Draw()  
  4.     {  
  5.         //繪制正方形  
  6.     }  

只需新增加一個(gè)類且對其他的任何模塊完全沒有影響,OCP出色地完成了任務(wù).

如果一開始就采用第二種代碼設(shè)計(jì),在需求的暴雨來臨時(shí),你會(huì)欣喜地發(fā)現(xiàn)你已經(jīng)到家了, 躲過了被淋一身濕的悲劇. 所以在一開始設(shè)計(jì)的時(shí)候,就要時(shí)刻地思考,根據(jù)對應(yīng)用領(lǐng)域的理解來判斷最有可能變化的種類,然后構(gòu)造抽象來隔離那些變化. 經(jīng)驗(yàn)在這個(gè)時(shí)候會(huì)顯得非常寶貴,可能會(huì)幫上你的大忙.

OCP很美好,然而絕對的對修改關(guān)閉是不可能的,都會(huì)有無法對之封閉的變化. 同時(shí)必須清楚認(rèn)識到遵循OCP的代價(jià)也是昂貴的,創(chuàng)建適當(dāng)?shù)某橄笫且ㄙM(fèi)開發(fā)時(shí)間和精力的. 如果濫用抽象的話,無疑引入了更大的復(fù)雜性,增加維護(hù)難度.

三 Liskov Subsitution Principle——里氏替換原則

核心思想: 子類必須能夠替換掉它們的父類型.

考慮如下情況:

 
 
 
  1. public class ProgrammerToy  
  2. {  
  3.     private int _state;  
  4.  
  5.     public  int State  
  6.     {  
  7.         get { return _state; }  
  8.     }  
  9.  
  10.     public virtual void SetState(int state)  
  11.     {  
  12.         _state = state;  
  13.     }  
  14. }  
  15.  
  16. public class CustomProgrammerToy:ProgrammerToy  
  17. {  
  18.     public override void SetState(int state)  
  19.     {  
  20.         //派生類缺乏完整訪問能力,即無法訪問父類的私有成員_state  
  21.         //因此該類型也許不能完成其父類型能夠滿足的契約  
  22.     }  
  23. }   
  24.  
  25.  
  26.  
  27.  
  28. //控制臺應(yīng)用程序代碼  
  29. class Program  
  30. {  
  31.     static void Main(string[] args)  
  32.     {  
  33.         ProgrammerToy toy = new CustomProgrammerToy();  
  34.         toy.SetState(5);  
  35.         Console.Write(toy.State.ToString());  
  36.     }  

從語法的角度來看, 代碼沒有任何問題. 不過從行為的角度來看 , 二者卻存在不同. 在使用CustomProgrammerToy替換父類的時(shí)候, 輸出的是0而不是5, 與既定的目標(biāo)相差千里. 所以不是所有的子類都能安全地替換其父類使用.

前面談到的開發(fā)封閉原則和里氏替換原則存在著密切的關(guān)系. 實(shí)現(xiàn)OCP的核心是對抽象編程, 由于子類型的可替換性才使得使用父類類型的模塊在無需修改的情況下就可以擴(kuò)展, 所以違反了里氏替換原則也必定違反了開放封閉原則.

慶幸的是, 里氏替換原則還是有規(guī)律可循的.父類盡可能使用接口或抽象類來實(shí)現(xiàn),同時(shí)必須從客戶的角度理解,按照客戶程序的預(yù)期來保證子類和父類在行為上的相容.

四 InterFace Segregation Principle——接口隔離原則

核心思想:使用多個(gè)小的專門的接口,而不要使用一個(gè)大的總接口.

直接來看一個(gè)例子: 假設(shè)有一個(gè)使用電腦的接口

         

程序員類實(shí)現(xiàn)接口IComputerUse, 玩游戲,編程,看電影, 多好的事情.

現(xiàn)在有一個(gè)游戲發(fā)燒友,他也要使用電腦, 為了重用代碼 , 實(shí)現(xiàn)OCP, 他也實(shí)現(xiàn)接口IComputerUse

看出什么問題了嗎? GamePlayer PlayGame無可厚非,WatchMovies小消遣, 但要編程干什么?

這就是胖接口帶來的弊端,會(huì)導(dǎo)致實(shí)現(xiàn)的類必須完全實(shí)現(xiàn)接口的所有方法, 而有些方法對客戶來說是無任何用處的,在設(shè)計(jì)上這是一種"浪費(fèi)". 同時(shí),如果對胖接口進(jìn)行修改, 比如程序員要使用電腦配置為服務(wù)器, 在IComputerUse上添加Server方法, 同樣GamePlayer也要修改(這種修改對GamePlayer是毫無作用的),是不是就引入了額外的麻煩?

所以應(yīng)該避免出現(xiàn)胖接口,要使接口實(shí)現(xiàn)高內(nèi)聚(高內(nèi)聚是指一個(gè)模塊中各個(gè)部分都是為完成一項(xiàng)具體功能而協(xié)同工作,緊密聯(lián)系,不可分割). 當(dāng)出現(xiàn)了胖接口,就要考慮重構(gòu).優(yōu)先推薦的方法是使用多重繼承分離,即實(shí)現(xiàn)小接口.

將IComputerUse拆分為IComputerBeFun和IComputerProgram, Progammer類則同時(shí)實(shí)現(xiàn)IComputerBeFun和IComputerProgram接口,現(xiàn)在就各取所需了.

與OCP類似, 接口也并非拆分地越小越好, 因?yàn)樘嗟慕涌跁?huì)影響程序的可讀性和維護(hù)性,帶來難以琢磨的麻煩. 所以設(shè)計(jì)接口的時(shí)刻要著重考慮高內(nèi)聚性, 如果接口中的方法都?xì)w屬于同一個(gè)邏輯劃分而協(xié)同工作,那么這個(gè)接口就不應(yīng)該再拆分.

五 Dependency Inversion Principle——依賴倒置原則

核心思想: 高層模塊不應(yīng)該依賴底層模塊,兩者都應(yīng)該依賴抽象。抽象不應(yīng)該依賴細(xì)節(jié),細(xì)節(jié)應(yīng)該依賴抽象。

當(dāng)一個(gè)類A存在指向另一個(gè)具體類B的引用的時(shí)候,類A就依賴于類B了。如:

 
 
 
  1. ///   
  2. /// 商品類  
  3. ///   
  4. public class Product  
  5. {  
  6.     public int Id { get; set; }  
  7. }  
  8.  
  9. ///   
  10. /// 商品持久化類  
  11. ///   
  12. public class ProductRepository  
  13. {  
  14.     public IList FindAll()  
  15.     {  
  16.         //假設(shè)從SQL Server數(shù)據(jù)庫中獲取數(shù)據(jù)  
  17.         return null;  
  18.     }  
  19. }  
  20.  
  21. ///   
  22. /// 商品服務(wù)類  
  23. ///   
  24. public class ProductService  
  25. {  
  26.     private ProductRepository _productRepository;  
  27.  
  28.     public IList GetProducts()  
  29.     {  
  30.         _productRepository = new ProductRepository();  
  31.  
  32.         return _productRepository.FindAll();  
  33.     }  

(在前面單一職責(zé)原則中有提到,業(yè)務(wù)邏輯處理和對象持久化分屬兩個(gè)職責(zé),所以應(yīng)該拆分為兩個(gè)類。)高層模塊ProductService類中引用了底層模塊具體類ProductRepository,所以ProductService類就直接依賴于ProductRepository了。那么這樣的依賴會(huì)帶來什么問題呢?

"需求總是那么不期而至"。原本ProductRepository是從SQL Server數(shù)據(jù)庫中讀存數(shù)據(jù),現(xiàn)在要求從MySQL數(shù)據(jù)庫中讀存數(shù)據(jù)。由于高層模塊依賴于底層模塊,現(xiàn)在底層模塊ProductRepository發(fā)生了更改,高層模塊ProductService也需要跟著一起修改,回顧之前談到的設(shè)計(jì)原則,這是不是就違反了OCP呢?OCP的核心思想是對抽象編程,DIP的思想是依賴于抽象,這也讓我們更清楚地認(rèn)識到,面向?qū)ο笤O(shè)計(jì)的時(shí)候,要綜合所有的設(shè)計(jì)原則考慮。DIP給出了解決方案:在依賴之間定義一個(gè)接口,使得高層模塊調(diào)用接口,而底層模塊實(shí)現(xiàn)接口,以此來控制耦合關(guān)系。(在上面OCP的例子中,也是使用了這一個(gè)方法。)所以可以對代碼做如下的重構(gòu):

 
 
 
  1. View Code   
  2.  
  3. ///   
  4. /// 商品持久化接口  
  5. ///   
  6. public interface IProductRepository  
  7. {  
  8.     List FindAll();  
  9. }  
  10.  
  11. ///   
  12. /// 商品持久化類  
  13. ///   
  14. public class ProductRepository:IProductRepository  
  15. {  
  16.     public IList FindAll()  
  17.     {  
  18.         //假設(shè)從SQL Server數(shù)據(jù)庫中獲取數(shù)據(jù)  
  19.         return null;  
  20.     }  
  21. }  
  22.  
  23. ///   
  24. /// 商品服務(wù)類  
  25. ///   
  26. public class ProductService  
  27. {  
  28.     private IProductRepository _productRepository;  
  29.  
  30.     private ProductService() { }  
  31.  
  32.     //使用構(gòu)造函數(shù)依賴注入  
  33.     public ProductService(IProductRepository productRepository)  
  34.     {  
  35.         _productRepository = productRepository;  
  36.     }  
  37.  
  38.     public IList GetProducts()  
  39.     {  
  40.         return _productRepository.FindAll();  
  41.     }  

現(xiàn)在已對變化進(jìn)行了抽象隔離,再根據(jù)OCP,我相信實(shí)現(xiàn)從MySQL數(shù)據(jù)庫中讀存數(shù)據(jù)的需求已經(jīng)可以被輕松地解決掉了。


網(wǎng)站題目:面向?qū)ο笤O(shè)計(jì)原則
當(dāng)前路徑:http://www.dlmjj.cn/article/cojodcc.html