<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆-14  評論-25  文章-1  trackbacks-0
    單一職責原則 ——SRP
    就一個類而言,應該僅有一個引起它的變化的原因
    ?
    原則

    最簡單,最單純的事情最容易控制,最有效
    類的職責簡單而且集中,避免相同的職責分散到不同的類之中,避免一個類承擔過多的職責
    減少類之間的耦合
    當需求變化時,只修改一個地方

    組件

    每個組件集中做好一件事情
    組件的顆粒度
    發布的成本
    可重用的成本
    ?
    方法

    避免寫臃腫的方法
    Extract Method
    ?
    重構

    Move Field/Move Class
    Extract Method/Extract Class
    ?
    最簡單的,也是最難以掌握的原則
    ?
    實例分析


    單一職責很容易理解,也很容易實現。所謂單一職責,就是一個設計元素只做一件事。什么是“只做一件事”?簡單說就是少管閑事。現實中就是如此,如果要你專心做一件事情,任何人都有信心可以做得很出色。但如果,你整天被亂七八糟的事所累,還有心思和精力把每件事都作好么?
    fig-1.JPG
    ????? “單一職責”就是要在設計中為每種職責設計一個類,彼此保持正交,互不干涉。這個雕塑(二重奏)就是正交的一個例子,鋼琴家和小提琴家各自演奏自己的樂 譜,而結果就是一個和諧的交響樂。當然,真實世界中,演奏小提琴和彈鋼琴的必須是兩個人,但是在軟件中,我們往往會把兩者甚至更多攪和到一起,很多時候只 是為了方便或是最初設計的時候沒有想到。?

    ??????這樣的例子在設計中很常見,書中就給了一個很好的例子:調制解調器。這是一個調制 解調器最基本的功能。但是這個類事實上完成了兩個職責:連接的建立和中斷、數據的發送和接收。顯然,這違反了SRP。這樣做會有潛在的問題:當僅需要改變 數據連接方式時,必須修改Modem類,而修改Modem類的結果就是使得任何依賴Modem類的元素都需要重新編譯,不管它是不是用到了數據連接功能。 解決的辦法,書中也已經給出:重構Modem類,從中抽出兩個接口,一個專門負責連接、另一個專門負責數據發送。依賴Modem類的元素也要做相應的細 化,根據職責的不同分別依賴不同的接口。最后由ModemImplementation類實現這兩個接口。
    fig-2.JPG ??????從這個例子中,我們不難發現,違反SRP通常是由于過于“真實”地設計了一個類所造成的。因此,解決辦法是往更高一層進行抽象 化提取,將對某個具體類的依賴改變為對一組接口或抽象類的依賴。當然,這個抽象化的提取應該根據需要設計,而不是盲目提取。比如剛才這個Modem的例子 中,如果有必要,還可以把DataChannel抽象為DataSender和DataReceiver兩個接口。


    開放封閉原則 ——OCP
    軟件實體(類,模塊,函數)應該是可以擴展的,但是不可修改的
    ?
    原則
    ?
    對擴展是開放的,當需求改變時我們可以對模塊進行擴展,使其具有新的功能
    對更改是封閉的,對模塊擴展時,不需要改動原來的代碼
    面對抽象而不是面對細節,抽象比細節活的更長
    僵化的設計——如果程序中一處改動產生連鎖反應。

    方法

    條件case?? if/else 語句
    ?
    重構

    Replace Type Code With Class
    Replace Type Code With State/Strategy
    Replace Conditional with polymorphism
    ?
    實例

    開閉原則很簡單,一句話:“Closed for Modification; Open for Extension”——“對變更關閉;對擴展開放”。開閉原則其實沒什么好講的,我將其歸結為一個高層次的設計總則。就這一點來講,OCP的地位應該比SRP優先。

    OCP的動機很簡單:軟件是變化的。不論是優質的設計還是低劣的設計都無法回避這一問題。OCP說明了軟件設計應該盡可能地使架構穩定而又容易滿足不同的需求。

    為什么要OCP?答案也很簡單——重用。

    “重用”,并不是什么軟件工程的專業詞匯,它是工程界所共用的詞匯。早在軟件出現前,工程師們就在實踐“重用”了。比如機械產品,通過零部 件的組裝得到最終的能夠使用的工具。由于機械部件的設計和制造過程是極其復雜的,所以互換性是一個重要的特性。一輛車可以用不同的發動機、不同的變速箱、 不同的輪胎……很多東西我們直接買來裝上就可以了。這也是一個OCP的例子。(可能是由于我是搞機械出身的吧,所以就舉些機械方面的例子^_^)。

    如何在OO中引入OCP原則?把對實體的依賴改為對抽象的依賴就行了。下面的例子說明了這個過程:

    05賽季的時候,一輛F1賽車有一臺V10引擎。但是到了06賽季,國際汽聯修改了規則,一輛F1賽車只能安裝一臺V8引擎。車隊很快投入了新賽車 的研發,不幸的是,從工程師那里得到消息,舊車身的設計不能夠裝進新研發的引擎。我們不得不為新的引擎重新打造車身,于是一輛新的賽車誕生了。但是,麻煩 的事接踵而來,國際汽聯頻頻修改規則,搞得設計師在“賽車”上改了又改,最終變得不成樣子,只能把它廢棄。

    OCP-fig1.JPG

    為了能夠重用這輛昂貴的賽車,工程師們提出了解決方案:首先,在車身的設計上預留出安裝引擎的位置和管線。然后,根據這些設計好的規范設計引擎(或是引擎的適配器)。于是,新的賽車設計方案就這樣誕生了。

    ?OCP-fig2.JPG

    顯然,通過重構,這里應用的是一個典型的Bridge模式。這個實現的關鍵之處在于我們預先給引擎留出了位置!我們不必因為對引擎的規則的頻頻變更而制造相當多的車身,而是盡可能地沿用和改良現有的車身。
    說到這里,想說一說OO設計的一個誤區。
    學 習OO語言的時候,為了能夠說明“繼承”(或者說“is-a”)這個概念,教科書上經常用實際生活中的例子來解釋。比如汽車是車,電車是車,F1賽車是汽 車,所以車是汽車、電車、F1賽車的上層抽象。這個例子并沒有錯。問題是,這樣的例子過于“形象”了!如果OO設計直接就可以將現實生活中的概念引用過 來,那也就不需要什么軟件工程師了!OO設計的關鍵概念是抽象。如果沒有抽象,那所有的軟件工程師的努力都是徒勞的。因為如果沒有抽象,我們只能去構造世 界中每一個對象。上面這個例子中,我們應該看到“引擎”這個抽象的存在,因為車隊的工程師們為它預留了位置,為它制定了設計規范。
    上面這個設計也 實現了后面要說的DIP(依賴倒置原則)。但是請記住,OCP是OO設計原則中高層次的原則,其余的原則對OCP提供了不同程度的支持。為了實現OCP, 我們會自覺或者不自覺地用到其它原則或是諸如Bridge、Decorator等設計模式。然而,對于一個應用系統而言,實現OCP并不是設計目的,我們 所希望的只是一個穩定的架構。所以對OCP的追求也應該適可而止,不要陷入過渡設計。正如Martin本人所說:“No significant program can be 100% closed.”“Closure not complete but strategic”

    Liskov替換原則—— LSP?

    子類型必須能夠替換它的基類型

    原則

    主要針對繼承的設計原則
    所有派生類的行為功能必須和客戶程序對其基類所期望的保持一致。
    派生類必須滿足基類和客戶程序的約定
    IS-A是關于行為方式的,依賴客戶程序的調用方式
    ?
    重構

    Extract Supper Class
    ?
    實例

    長方形和正方形

    OCP作為OO的高層原則,主張使用“抽象(Abstraction)”和“多態(Polymorphism)”將設計中的靜態結構改為動態結構,維持設計的封閉性。

    “抽象”是語言提供的功能。“多態”由繼承語義實現。

    如此,問題產生了:“我們如何去度量繼承關系的質量?”

    Liskov于1987年提出了一個關于繼承的原則“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”——“繼承必須確保超類所擁有的性質在子類中仍然成立。”也就是說,當一個子類的實例應該能夠替換任何其超類的實例時,它們之間才具有 is-A關系。

    該原則稱為Liskov Substitution Principle——里氏替換原則。林先生在上課時風趣地稱之為“老鼠的兒子會打洞”。^_^

    我們來研究一下LSP的實質。學習OO的時候,我們知道,一個對象是一組狀態和一系列行為的組合體。狀態是對象的內在特性,行為是對象的外在特性。LSP所表述的就是在同一個繼承體系中的對象應該有共同的行為特征。

    這一點上,表明了OO的繼承與日常生活中的繼承的本質區別。舉一個例子:生物學的分類體系中把企鵝歸屬為鳥類。我們模仿這個體系,設計出這樣的類和關系。

    ?lsp-fig1.jpg

    類“鳥”中有個方法fly,企鵝自然也繼承了這個方法,可是企鵝不能飛阿,于是,我們在企鵝的類中覆蓋了fly方法,告訴方法的調用者:企 鵝是不會飛的。這完全符合常理。但是,這違反了LSP,企鵝是鳥的子類,可是企鵝卻不能飛!需要注意的是,此處的“鳥”已經不再是生物學中的鳥了,它是軟 件中的一個類、一個抽象。

    有人會說,企鵝不能飛很正常啊,而且這樣編寫代碼也能正常編譯,只要在使用這個類的客戶代碼中加一句判斷就行了。但是,這就是問題所 在!首先,客戶代碼和“企鵝”的代碼很有可能不是同時設計的,在當今軟件外包一層又一層的開發模式下,你甚至根本不知道兩個模塊的原產地是哪里,也就談不 上去修改客戶代碼了。客戶程序很可能是遺留系統的一部分,很可能已經不再維護,如果因為設計出這么一個“企鵝”而導致必須修改客戶代碼,誰應該承擔這部分 責任呢?(大概是上帝吧,誰叫他讓“企鵝”不能飛的。^_^)“修改客戶代碼”直接違反了OCP,這就是OCP的重要性。違反LSP將使既有的設計不能封 閉!

    修正后的設計如下:

    ?lsp-fig2.jpg

    但是,這就是LSP的全部了么?書中給了一個經典的例子,這又是一個不符合常理的例子:正方形不是一個長方形。這個悖論的詳細內容能在網上找到,我就不多廢話了。

    LSP并沒有提供解決這個問題的方案,而只是提出了這么一個問題。

    于是,工程師們開始關注如何確保對象的行為。1988年,B. Meyer提出了Design by Contract(契約式設計)理論。DbC從形式化方法中借鑒了一套確保對象行為和自身狀態的方法,其基本概念很簡單:

    1. 每個方法調用之前,該方法應該校驗傳入參數的正確性,只有正確才能執行該方法,否則認為調用方違反契約,不予執行。這稱為前置條件(Pre-condition)。
    2. 一旦通過前置條件的校驗,方法必須執行,并且必須確保執行結果符合契約,這稱之為后置條件(Post-condition)。
    3. 對象本身有一套對自身狀態進行校驗的檢查條件,以確保該對象的本質不發生改變,這稱之為不變式(Invariant)。

    以上是單個對象的約束條件。為了滿足LSP,當存在繼承關系時,子類中方法的前置條件必須與超類中被覆蓋的方法的前置條件相同或者更寬松;而子類中方法的后置條件必須與超類中被覆蓋的方法的后置條件相同或者更為嚴格。

    一些OO語言中的特性能夠說明這一問題:

    • 繼承并且覆蓋超類方法的時候,子類中的方法的可見性必須等于或者大于超類中的方法的可見性,子類中的方法所拋出的受檢異常只能是超類中對應方法所拋出的受檢異常的子類。
      public?class?SuperClass{
      ????
      public?void?methodA()?throws?IOException{}
      }


      public?class?SubClassA?extends?SuperClass{
      ????
      //this?overriding?is?illegal.
      ????private?void?methodA()?throws?Exception{}
      }


      public?class?SubClassB?extends?SuperClass{
      ????
      //this?overriding?is?OK.
      ????public?void?methodA()?throws?FileNotFoundException{}
      }

    • 從Java5開始,子類中的方法的返回值也可以是對應的超類方法的返回值的子類。這叫做“協變”(Covariant)
      public?class?SuperClass?{
      ????
      public?Number?caculate(){
      ????????
      return?null;
      ????}

      }


      public?class?SubClass?extends?SuperClass{
      ????
      //only?compiles?in?Java?5?or?later.
      ????public?Integer?caculate(){
      ????????
      return?null;
      ????}

      }

    可以看出,以上這些特性都非常好地遵循了LSP。但是DbC呢?很遺憾,主流的面向對象語言(不論是動態語言還是靜態語言)還沒有加入對DbC的支持。但是隨著AOP概念的產生,相信不久DbC也將成為OO語言的一個重要特性之一。


    依賴倒置原則—— DIP?

    a:高層模塊不應依賴于底層模塊,兩者都應該依賴于抽象
    b:抽象不應該依賴于細節,細節應該依賴于抽象

    ?
    原則

    如何解釋倒置
    高層依賴底層,重用變得困難,而最經常重用的就是framework和各個獨立的功能組件
    高層依賴底層,底層的改動直接反饋到高層,形成依賴的傳遞
    面向接口的編程
    ?

    實例

    Ioc模式
    DomainObject / DomianObjectDataService
    ?

    接口隔離原則—— ISP?

    使用多個專門的接口比使用單一的總接口總要好。換而言之,從一個客戶類的角度來講:一個類對另外一個類的依賴性應當是建立在最小接口上的。

    原則

    過于臃腫的接口是對接口的污染。不應該強迫客戶依賴于它們不用的方法。

    My object-oriented umbrella(摘自Design Patterns Explained)

    Let me tell you about my great umbrella. It is large enough to get into! In fact, three or four other people can get in it with me. While we are in it, staying out of the rain, I can move it from one place to another. It has a stereo system to keep me entertained while I stay dry. Amazingly enough, it can also condition the air to make it warmer or colder. It is one cool umbrella.

    My umbrella is convenient. It sits there waiting for me. It has wheels on it so that I do not have to carry it around. I don't even have to push it because it can propel itself. Sometimes, I will open the top of my umbrella to let in the sun. (Why I am using my umbrella when it is sunny outside is beyond me!)

    In Seattle, there are hundreds of thousands of these umbrellas in all kinds of colors. Most people call them cars.

    實現方法:
    1、?使用委托分離接口
    2、?使用多重繼承分離接口

    想到一個朋友說的話:所有的接口都只有一個方法,具體的類根據自己需要什么方法去實現接口

    posted on 2006-03-17 10:00 混沌中立 閱讀(442) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發表評論。


    網站導航:
    博客園   IT新聞   Chat2DB   C++博客   博問  
     
    主站蜘蛛池模板: 国产jizzjizz视频免费看| 日本免费xxxx色视频| 亚洲国产婷婷香蕉久久久久久| 亚洲色大18成人网站WWW在线播放| 成人免费的性色视频| 亚洲一级毛片免观看| A级毛片内射免费视频| 亚洲欧美成人综合久久久| 大香人蕉免费视频75| 女bbbbxxxx另类亚洲| 亚洲国产精品人人做人人爱| 五月天婷婷精品免费视频| 亚洲片一区二区三区| 曰批全过程免费视频在线观看无码| 亚洲VA中文字幕无码一二三区| a级毛片在线免费看| 亚洲高清免费在线观看| 免费无码又黄又爽又刺激| 亚洲色精品三区二区一区| 四虎精品亚洲一区二区三区| h片在线观看免费| 亚洲成年轻人电影网站www| 色播精品免费小视频| 亚洲A∨精品一区二区三区下载 | 亚洲视频在线免费播放| 亚洲成a人片77777群色| 在线免费观看韩国a视频| 一级毛片免费全部播放| 亚洲AV电影院在线观看| 99精品全国免费观看视频 | 99视频在线精品免费| 亚洲中文字幕AV每天更新| 亚洲精品无码99在线观看| 久久99精品视免费看| 33333在线亚洲| 亚洲综合色成在线播放| 亚州免费一级毛片| 无码日韩人妻AV一区免费l| 亚洲AV电影院在线观看| 国产一区二区三区免费看| 爱丫爱丫影院在线观看免费 |