麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

重拾《重構(gòu)-改善既有代碼的設(shè)計》

2019-11-17 01:28:22
字體:
供稿:網(wǎng)友

重拾《重構(gòu)-改善既有代碼的設(shè)計》

什么是重構(gòu)?

  • 是在不改變系統(tǒng)行為的前提下,對內(nèi)部代碼的重新組織,提高可理解性和降低修改成本。

為什么要重構(gòu)?

  • 一個小修改牽涉到了多個地方,且這些點處于未知狀態(tài)
  • 不易讀懂代碼(包括讀懂自己1個月前的代碼)
  • 新手修改代碼上手慢,需要很久才能進行有信心的代碼修改
  • 需求變化時,代碼層面響應(yīng)慢

什么時候需要重構(gòu)?

  • 隨時隨地的重構(gòu),也就是從一開始就進行小范圍的重構(gòu),就不至于時間久后沒法平滑的重構(gòu)了
  • 上面這句實際上是個方法論級別的,真實中,還是沒辦法判斷什么時候要進行重構(gòu),于是換成:當(dāng)代碼中出現(xiàn)了壞味道時需要重構(gòu)
  • 什么是壞味道:
    • 存在重復(fù)代碼時
    • 函數(shù)體太長
    • 函數(shù)參數(shù)太長
    • 無法直觀的看出代碼邏輯
    • 類太大
    • 對一個常量存在了多個副本
    • 很多很多的if/else/switch語句
    • 類名、函數(shù)名、方法名不友好

重構(gòu)與性能

  • 重構(gòu)為先,調(diào)優(yōu)其次
  • 重構(gòu)能組織良好的結(jié)構(gòu),良好的結(jié)構(gòu)能讓調(diào)優(yōu)工作更輕松

重新組織函數(shù)

  • Extract Method(提煉函數(shù))
    • 當(dāng)內(nèi)部邏輯過分纏繞在一起時,需要將一些代碼抽取到子函數(shù)中
  • Inline Method(內(nèi)聯(lián)函數(shù))
    • 如果一個函數(shù)體很少,并且沒有被其他函數(shù)使用到,就可以考慮將這個小函數(shù)內(nèi)聯(lián)到父函數(shù)中
  • Inline Temp(內(nèi)聯(lián)臨時變量)
    • 如果一個變量只被使用到了1次,并且這個變量所代表的邏輯很少,此時可以考慮將這個臨時變量所代表的邏輯直接拷貝到父函數(shù)中
  • Replace Temp with Query(以查詢?nèi)〈R時變量)
    • 如果去除了臨時變量后,更加利于后續(xù)的重構(gòu)改動,則會使用這種方法,將臨時變量所代表的邏輯抽取成單獨一個函數(shù)
    • 雖然對性能有影響,但是重構(gòu)過去后,如果不是很嚴重的性能影響,則還是建議改成這樣,因為重構(gòu)過去后對后續(xù)重構(gòu)更有利,更便于以后的重構(gòu)
  • Introduce Explaining Variable(引入解釋性變量)
    • 將邏輯碎片賦給命名友好的變量名,這樣代碼的可讀性、理解性更強
  • Split Temporary variable(分解臨時變量)
    • 一個邏輯目的只賦給一個臨時變量,不要合用臨時變量,如:
    • int temp=x+y;
    • //some logic to PRocess temp varialbe
    • temp=getBase()+100;
    • //some logic to process the new temp varialbe
  • Remove Assignments to Parameters(移除對參數(shù)的賦值)
    • 禁止對傳入?yún)?shù)的賦值,要用增加臨時變量的方式來
  • Replace Method with method Object(以函數(shù)對象取代函數(shù))
    • 針對大函數(shù)、邏輯復(fù)雜、局部變量多時
    • 思想是將這個函數(shù)獨立成為一個類,在類中進行復(fù)雜邏輯的處理
  • Substitute Algorithm(替換算法)
    • 將函數(shù)內(nèi)部的算法替換掉,比如:為了更高的效率或者更好的可理解性
    • 意圖是提升效率或者可理解性
  • 大方向上都是讓語義更加清晰

在對象之間搬移特性

  • Move Method(搬移函數(shù))
    • 如果發(fā)現(xiàn)某個函數(shù)主要依賴于其他類的數(shù)據(jù),則有必要將這個函數(shù)move到那個類中
  • Move Field(搬移字段)
    • 和上面的類似,至于是用哪個方法重構(gòu),需要看情況,比如看類的名稱、職責(zé)定義
  • Extract Class(提煉類)
    • 當(dāng)類包含大量函數(shù)、數(shù)據(jù)時,需要考慮拆分類
  • Inline Class(將類內(nèi)聯(lián)化)
    • 當(dāng)某個類的職責(zé)不足以成為一個類時,考慮將這個類合并到其他類中
    • 比如這種情況發(fā)生在重構(gòu)行為后,弱化了某個類的職責(zé)
  • Hide Delegate(隱藏“委托關(guān)系”)
    • 在server端隱藏某個類,這樣客戶端只需要知道1個類就能做邏輯操作,而不需要同時知道多個類才能進行邏輯操作了
  • Remove Middle Man(移除中間人)
    • 暴露更多的類來供客戶端調(diào)用
    • “中間人”的移除與否比較難定,一般模塊之間是盡量少暴露,模塊內(nèi)部要看情況而定
  • Introduce Foreign Method(引入外加函數(shù))
    • 當(dāng)提供的函數(shù)不能修改時,可以在客戶端增加一個函數(shù)來包裝這個目標函數(shù),完成額外邏輯的插入轉(zhuǎn)換
    • 這種額外函數(shù)不多
    • 用多了不好,最終需要合并到目標函數(shù)所在的server端
  • Introduce Local Extension(引入本地擴展)
    • 如果發(fā)生上述情況,并且擴展的比較多,則可以在客戶端新建一個類,通過繼承或者Wrapper的方式導(dǎo)入原始方法或類,進行額外方法、函數(shù)、邏輯的加工

重新組織數(shù)據(jù)

  • Self Encapsulate Field(自封裝字段)
    • C#中使用屬性來解決,不引用字段,要引用屬性,以便在需要覆寫變量值的時候嵌入邏輯
  • Replace Data Value with Object(以對象取代數(shù)據(jù)值)
    • 當(dāng)對某個基元數(shù)據(jù)有更多的普遍常用功能時,需要將基元數(shù)據(jù)替換為對象類型,進而在這個對象中實現(xiàn)一些常用功能,方便調(diào)用方的調(diào)用
  • Change Value to Reference(將值對象改為引用對象)
    • 如果當(dāng)前的某個值對象被多個地方用到,并且此時希望更改了一處后,其他地方的引用也跟著改變,此時需要將這個值對象轉(zhuǎn)換為引用對象
    • 場景:項目剛開始時用了值對象,但是后來認為用引用類型更好,此時就需要轉(zhuǎn)換
  • Change Reference to Value(將引用對象改為值對象)
    • 如果存在一個引用類型,而且這個引用類型較小,且不需要實現(xiàn)實例間的互相更改,此時可以把這個引用類型改為值類型,這樣能保證這個對象的不可變性
  • Replace Array with Object(以對象取代數(shù)組)
    • 當(dāng)一個數(shù)組被用在了傳遞對象屬性用途時,可以采用類來替代這個數(shù)組
  • Duplicate Observed Data(復(fù)制“被監(jiān)視的數(shù)據(jù)”)
    • 層與層之間的纏繞調(diào)用,沒有劃分好層導(dǎo)致的
    • 層與層之間通過DTO的方式進行傳輸數(shù)據(jù)
  • Change Unidirectional Association to Bidirectional(將單向關(guān)聯(lián)改為雙向關(guān)聯(lián))
    • 謹慎使用,盡量使單向關(guān)聯(lián)
    • 需要在雙方對象中加入維護對方的代碼,如:Customer.AddOrder/Order.AddCustomer,都要成對出現(xiàn)
  • Change Bidirectional Association to Unidirectional(將雙向關(guān)聯(lián)改為單向關(guān)聯(lián))
    • 隨著需求的演化,在某時間段,發(fā)現(xiàn)不需要雙向關(guān)聯(lián)了,此時用此法
  • Replace Magic Number with Symbolic Constant(以字面常量取代魔法數(shù))
    • 字面量需要用const常量來替代
    • 如科學(xué)計算中某些具有特殊意義的數(shù)值,需要統(tǒng)一const引用
  • Encapsulate Field(封裝字段)
    • 數(shù)據(jù)和行為被分開后,由于誰都可以引用public數(shù)據(jù),因此不容易管理及修改
    • 如果不暴露數(shù)據(jù),這樣就能做到只在當(dāng)前class中使用這些數(shù)據(jù)了
  • Encapsulate Collection(封裝集合)
    • 默認的List<T> Collection<T> ArrayList暴露了太多內(nèi)部邏輯,而且返回的對象能夠被客戶端修改,不利于隔離與封裝
    • 自己寫集合類,可以只暴露特定接口、返回對象新的拷貝,這樣能解決惡意、無意的修改
  • Replace Record with Data Class(以數(shù)據(jù)類取代記錄)
    • 將非對象化的平面數(shù)據(jù)類型(如:數(shù)組、傳遞過來的沒有良好命名的屬性等),重寫成class,只有private屬性的class
    • 目的只是為以后更進一步的重構(gòu)做準備
  • Replace Type Code with Class(以類取代類型碼)
    • Type Code:枚舉、多個string、int變量,如:string Male="男性" string Female="女性"),諸如此類的標識
    • 將這個Type Code(包含了多個字段,但是只是區(qū)分不同的Type)抽象為一個Type Code類
    • 引用的相關(guān)地方也要做出更改
  • Replace Type Code with Subclasses(以子類取代類型碼)
    • 用子類來標識,這樣可以使用重寫函數(shù)來解決一些行為上的變化
  • Replace Type Code with State/Strategy(以State/Strategy取代類型碼)
    • 用狀態(tài)、策略模式將變化部分抽取出來
  • Replace Subclass with Fields(以字段取代子類)
    • 如果子類中只是簡單的返回一些常量,則可以將這些子類廢除,壓縮繼承級別,將類型判斷的邏輯寫在父類的相應(yīng)方法中

簡化條件表達式

  • Decompose Conditional(分解條件表達式)
    • 往往邏輯比較復(fù)雜的地方,分支就較多
    • 一個分支中如果寫了很多小段代碼,也應(yīng)該重構(gòu)成更有語義的代碼
    • 需要將分支重構(gòu)為更加語義化,這樣會提高可讀性
  • Consolidate Conditional Expression(合并條件表達式)
    • 一般在函數(shù)入口出會檢查參數(shù)有效性,如果寫有多條if語句判斷為無效,都返回false,則可以將這些都return false的判斷抽取到一個單獨函數(shù)中
    • 主函數(shù)中語義更加清晰
  • Consolidate Duplicate Conditional Fragments(合并重復(fù)的條件片段)
    • 如果在if/else分支中,每個分支的開始或者結(jié)束區(qū)域都使用了同樣的代碼,則提取到if/else外進行統(tǒng)一調(diào)用
  • Remove Control Flag(移除控制標記)
    • 用在循環(huán)中,去掉控制標記,比如bool found=false之類的控制標記,當(dāng)找到時,直接return obj/return;
  • Replace Nested Conditional with Guard Clauses(以衛(wèi)語句取代嵌套條件表達式)
    • 把if/else以及嵌套的if/else改成平面寫法,如:
    • if(xxx)return result+1;
    • if(yyy)return result+2;
    • if(zzz)return result+3;
    • return result+4;
  • Replace Conditional with Polymorphism(以多態(tài)取代條件表達式)
    • 用在有多個子類的繼承體系中,父類有個方法用來計算:根據(jù)不同的子類來計算不同的value
    • 套用模板方法設(shè)計模式一樣
  • Introduce Null Object(引入Null對象)
    • 針對null對象的設(shè)計模式
    • 可以將null時,業(yè)務(wù)邏輯的例外算法在NullObject中實現(xiàn)一份,這樣在業(yè)務(wù)邏輯類中就不需要些一堆if null之類的判斷以及轉(zhuǎn)發(fā)了
  • Introduce Assertion(引入斷言)
    • 在函數(shù)的入口編寫Assert,用來確保被調(diào)用此函數(shù)時,相應(yīng)的前置條件是否正確,使用
    • 如果斷言失敗,則會在日志文件中出現(xiàn)調(diào)用堆棧信息以及自定義信息
    • System.Diagnostics.Trace.Assert:無論是否Release,都會記錄日志
    • System.Diagnostics.Trace.Debug:只在Debug模式下生成日志信息

簡化函數(shù)調(diào)用

  • Rename Method(函數(shù)改名)
    • 修改函數(shù)命名為更有語義,提高可讀性
    • 參數(shù)順序、參數(shù)命名也是考慮之一
  • Add Parameter(添加參數(shù))
    • 修改了一個函數(shù),但是這個函數(shù)目前又需要用到以前所沒有的信息
  • Remove Parameter(移除參數(shù))
    • 以前的參數(shù),現(xiàn)在不需要了
  • Separate Query from Modifier(將查詢函數(shù)和修改函數(shù)分離)
    • 如果一個函數(shù)在返回值的過程中,也去修改了一些值,則會對客戶端調(diào)用者產(chǎn)生某些困擾,需要將其拆分為2個函數(shù):Query、Modify
  • Parameterize Method(令函數(shù)攜帶參數(shù))
    • 在函數(shù)內(nèi)部提取公用子函數(shù),來實現(xiàn)代碼的扁平化及公用化
  • Replace Parameter with Explicit Methods(以明確函數(shù)取代參數(shù))
    • 當(dāng)函數(shù)行為完全取決于參數(shù)value時,需要將這個函數(shù)拆分到多個方法,避免函數(shù)內(nèi)部邏輯太雜
  • Reserve Whole Object(保持對象完整)
    • 當(dāng)被調(diào)用函數(shù)的參數(shù)正好是某對象的其中幾個屬性時,則直接傳入這個對象
    • 需要同時考慮被調(diào)用函數(shù)是否需要move到這個對象中
  • Replace Parameter with Methods(以函數(shù)取代參數(shù))
    • 如果主函數(shù)中包含有多個子函數(shù),并且這些子函數(shù)返回值只是首尾傳入傳出
    • 此時,考慮將除最后一個函數(shù)外,其他子函數(shù)不通過主函數(shù)來調(diào)用,而是通過最后一個字函數(shù)的內(nèi)部進行調(diào)用
  • Introduce Parameter Object(引入?yún)?shù)對象)
    • 當(dāng)某些參數(shù)總是成對、成堆出現(xiàn)時,考慮此模式
    • 如: DateTime from, DateTime end==> DateRange
    • int pageIndex, int pageSize==>PagingInfo
    • 以及PagingResult<T>{TotalCount, List<T>}
  • Remove Setting Method(移除設(shè)值函數(shù))
    • 如果某個類的屬性在構(gòu)造后就不需要被改變,則把相應(yīng)的set訪問器關(guān)閉
  • Hide Method(隱藏函數(shù))
    • 如果某函數(shù)沒有被其他類引用到,就改成private的
  • Replace Constructor with Factory Method(以工廠函數(shù)取代構(gòu)造函數(shù))
    • 當(dāng)類存在多個子類,并且希望通過類型碼來生成新對象時,可以將構(gòu)造函數(shù)改成工廠方法,這樣便于客戶端調(diào)用,無需知道到底是哪個子類
  • Encapsulate Downcast(封裝向下轉(zhuǎn)型)
    • 是說對于類型的強制轉(zhuǎn)換,需要放在具體的函數(shù)中實現(xiàn),不要放在客戶端代碼中
    • 現(xiàn)在.Net有了泛型,減少了很多這種麻煩
  • Replace Error Code with Exception(以異常取代錯誤碼)
    • 在代碼中如遇異常,則直接throw new XXXXException("xx"),而不是用return errorCode的方式
    • 如果是可控異常,則在catch(XXXException ex)處理掉
    • 如果是不可控異常,則無需處理
    • 不可控異常應(yīng)有框架來處理,如AOP或者Global中的Error事件
  • Replace Exception with Test(以測試取代異常)
    • 對于濫用了catch異常的邏輯進行邏輯上的修改
    • 用單元測試+Assert+邊界值測試來確保某些異常沒有被觸發(fā)

處理概括關(guān)系

  • Pull Up Field(字段上移)
    • 當(dāng)多個子類中存在相似的字段時,需要分析
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 一级做a爱片久久 | 91精品国产91 | 国产精品久久久久久久久久久久午夜 | 九色 在线 | 成人在线免费观看网址 | 亚洲欧美日韩中文在线 | 7777在线观看| 免费黄色在线观看网站 | 久久不射电影网 | 欧美视频国产 | 91久久综合 | 精品国产一区二区三区在线 | 国产午夜精品久久久久婷 | 久久人人av| 欧美日韩中文字幕在线 | 国产一国产一级毛片视频 | 久久亚洲网 | 久久免费视频3 | 日本精品黄色 | 日本网站一区 | 国产精品免费在线 | 国产亚洲精品久久久久久久久 | 亚洲va久久久噜噜噜久久男同 | 日本一级黄色毛片 | 91欧美视频| 毛片视频网站在线观看 | 久久国产精品小视频 | 欧美毛片免费观看 | 一级免费 | 中文字幕在线观看视频一区 | porno video hd 365hd| 黄色99视频 | 欧美18一12sex性处hd | 中文字幕亚洲一区二区三区 | 国产小视频在线观看 | 最新91在线视频 | 奶子吧naiziba.cc免费午夜片在线观看 | 久久成人午夜视频 | 狠狠操电影 | 国产一级一级 | 国产亚洲精品综合一区 |