控制圈復雜度的9種重構技術

一個軟件總是為解決某種特定的需求而產生,時代在發展,客戶的業務也在發生變化。有的需求相對穩定一些,有的需求變化的比較劇烈,還有的需求已經消失了,或者轉化成了別的需求。在這種情況下,軟件必須相應的改變。

考慮到成本和時間等因素,當然不是所有的需求變化都要在軟件系統中實現。但是總的說來,軟件要適應需求的變化,以保持自己的生命力。
這就產生了一種糟糕的現象:軟件產品最初制造出來,是經過精心的設計,具有良好架構的。但是隨著時間的發展、需求的變化,必須不斷的修改原有的功能、追加新的功能,還免不了有一些缺陷需要修改。為了實現變更,不可避免的要違反最初的設計構架。經過一段時間以后,軟件的架構就千瘡百孔了。bug越來越多,越來越難維護,新的需求越來越難實現,軟件的架構對新的需求漸漸的失去支持能力,而是成為一種制約。最后新需求的開發成本會超過開發一個新的軟件的成本,這就是這個軟件系統的生命走到盡頭的時候。

重構就能夠最大限度的避免這樣一種現象。系統發展到一定階段后,使用重構的方式,不改變系統的外部功能,只對內部的結構進行重新的整理。通過重構,不斷的調整系統的結構,使系統對于需求的變更始終具有較強的適應能力。

重構可以降低項目的耦合度,使項目更加模塊化,有利于項目的開發效率和后期的維護。讓項目主框架突出鮮明,給人一種思路清晰,一目了然的感覺,其實重構是對框架的一種維護。

那么說到重構,可能我們普遍認為就是改改框架、使用一些最新流行的框架或者技術等等,我們到底要從哪些方面來對項目進行重構呢?下面我們進入主題:控制圈復雜度的9種重構技術

  • 提煉函數

例子:如果某段代碼可以被組織在一起并獨立出來

void printOwing(double previousAmount)
 {
    Enumeration e = _orders.elements();
    double outstanding = previousAmount * 1.2;
    
      // 打印大標題
    System.out.println ("**************************");
    System.out.println ("***** Customer Owes ******");
    System.out.println ("**************************");

    // 計算未完成的訂單數量
    while (e.hasMoreElements()) 
    {
        Order each = (Order) e.nextElement();
        outstanding += each.getAmount();
    }

    //打印明細
    System.out.println ("name:" + _name);
    System.out.println ("amount" + outstanding);
}

將這段代碼放進一個獨立函數中,并讓函數名稱解釋該函數的用途

void printOwing(double previousAmount) 
{
    printBanner();
    double outstanding = getOutstanding(previousAmount * 1.2);
    printDetails(outstanding);
}

void printBanner() //打印大標題
{
    System.out.println ("**************************");
    System.out.println ("***** Customer Owes ******");
    System.out.println ("**************************");
}

// 計算未完成的訂單數量
double getOutstanding(double initialValue) 
{
    double result = initialValue;
    Enumeration e = _orders.elements();
    while (e.hasMoreElements()) 
    {
        Order each = (Order) e.nextElement();
        result += each.getAmount();
    }
    return result;
}

void printDetails (double outstanding) //打印明細
 {
    System.out.println ("name:" + _name);
    System.out.println ("amount" + outstanding);
}
  • Substitute Algorithm(替換你的算法)

例子:
把當前算法重構成更清晰的算法

String foundPerson(String[] people)
{
    for (int i = 0; i < people.length; i++) 
    {
        if (people[i].equals ("Don"))
            return "Don";
        if (people[i].equals ("John"))
            return "John";
        if (people[i].equals ("Kent"))
            return "Kent";
    }
    return "";
}

重構成更清晰的算法

String foundPerson(String[] people)
{
    List candidates = Arrays.asList(new     
            String[]{"Don", "John","Kent"});
    for (int i=0; i<people.length; i++)
        if (candidates.contains(people[i]))
            return people[i];
    return "";
}
  • Decompose Conditional(分解條件式)

例子:你有一個復雜的條件語句

if (date.before (SUMMER_START) || date.after(SUMMER_END))
    charge = quantity * _winterRate + _winterServiceCharge;
else 
    charge = quantity * _summerRate;

從if、then、else三個段落中分別提煉出獨立函數

if (notSummer(date))
    charge = winterCharge(quantity);
else 
    charge = summerCharge (quantity);
  • Consolidate Conditional Expression(合并條件式)

例子:你有一系列條件判斷,都得到相同結果

double disabilityAmount() 
{
    if (_seniority < 2) return 0;
    if (_monthsDisabled > 12) return 0;
    if (_isPartTime) return 0;
    // compute the disability amount

將這些判斷合并為一個條件式,并將這個條件式提煉成為一個獨立函數,函數名自注釋

double disabilityAmount() 
{
    if (isNotEligableForDisability()) return 0;
    // compute the disability amount
  • Consolidate Duplicate Conditional Fragments(合并重復的條件片斷)

例子:在條件式的每個分支上有著相同的一段代碼。

if (isSpecialDeal()) 
    {
        total = price * 0.95;
        send();
    }
else 
    {
        total = price * 0.98;
        send();
    }

將這段重復代碼搬移到條件式之外,避免用拷貝粘貼的方式寫重復代碼

if (isSpecialDeal())
    total = price * 0.95;
else
    total = price * 0.98;
send();
  • Remove Control Flag(移除控制標記)

例子:當前代碼使用標記變量,可讀性差,容易出錯

void checkSecurity(String[] people) {
    boolean found = false;
    for (int i = 0; i < people.length; i++) {
        if (! found) {
            if (people[i].equals ("Don")){
                sendAlert();
                found = true;
            }
            if (people[i].equals ("John")){
                   sendAlert();
                   found = true;
            }
        }
    }
}

以break和return取代標記變量

void checkSecurity(String[] people) {
    for (int i = 0; i < people.length; i++) {     
        if (people[i].equals ("Don")){
            sendAlert();
            break;
        }
        if (people[i].equals ("John")){
            sendAlert();
            break;
        }
    }
}
  • Separate Query from Modifier(將查詢函數和修改函數分離)

例子:某個函數既返回對象狀態值,又修改對象狀態,建立兩個不同的函數,其中一個負責查詢,另一個負責修改




  • Parameterize Method(令函數攜帶參數)

例子:若干函數做了類似的工作,但在函數本體中卻
包含了不同的值

Dollars baseCharge()
 {
    double result = Math.min(lastUsage(),100) * 0.03;
    if (lastUsage() > 100) {
        result += (Math.min (lastUsage(),200) - 100) * 0.05;
    };
    if (lastUsage() > 200) {
        result += (lastUsage() - 200) * 0.07;
    };
    return new Dollars (result);
}

建立單一函數,以參數表達那些不同的值

Dollars baseCharge() 
{
    double result = usageInRange(0, 100) * 0.03;
    result += usageInRange (100,200) * 0.05;
    result += usageInRange (200, Integer.MAX_VALUE) * 0.07;
    return new Dollars (result);
}

int usageInRange(int start, int end) 
{
    if (lastUsage() > start) 
        return Math.min(lastUsage(),end) -start;
    else 
        return 0;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,048評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,414評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,169評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,722評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,465評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,823評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,813評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,000評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,554評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,295評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,513評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,722評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,125評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,430評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,237評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,482評論 2 379

推薦閱讀更多精彩內容