系統重構(一系列等量變換)_技法1.抽取方法(Extract Method)_技法2.抽取類別(Extract Class)_技法3.抽取介面(Extract Interface)



最近開始接獲到一項需求
但是負責維護這部分相關程式的前人們
都不在

那映入眼簾的就是
一大堆function

而且每個 function 都是非常飽滿、好的大概50~60行多則900行
一個函數就這麼多行   = =|||
其餘大致上就是繼承關係看不出脈絡
或是主要的流程看不出端倪
有些類有這函數和變數   有些類沒有
有的路徑指定各有各自的路徑字串等等

真的是非常難閱讀且不易後續維護



也因此開始接觸了  人生中第一次所遇到的
系統程式重構的重責大任
注意重構等同於高空走鋼索 一旦改錯功能就會造成系統諸多異常
所以必須嚴謹再嚴謹(尤其是每次commit code 之前最好都再三留意)



何謂重構???

重構  主要是 要求對太過於零亂的系統程式碼去進行
一個等量變化的修復過程好利於後欲維護與提高程式邏輯可閱讀性

在重構過後 甚至過程進行中的程式碼
皆不可影響外在功能 、 不可新增多於功能
也因此有人說它是一種程式碼的等量變換


必須以不改變程式外在行為作為前提
去改變程式內部結構以提升設計品質
(可閱讀性、未來維護性、Bug 修復、甚至效能耗時改善)


可以想成  寫數學計算式子
你可能會節省很多行計算過程(用橡皮擦  清除掉過去計算部分)
只保留最精簡的式子



為何要重構???

現行系統運行就好好的  幹嘛還要搞個重構這麼麻煩費時

當你發現接獲需求要你從現有系統去擴充功能時
這時發現無法很方便增加新功能、修改邏輯

那就是該重構的時機了

重構的主要目標

A change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior.

改成較容易理解與降低修改成本


以下我們來舉例

技法1.抽取方法(Extract Method)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package helloworld;

import java.util.Calendar;
import java.util.Date;

/**
 *
 * @author chous
 */
public class HelloWorld {
    public String sayHello(Date now,String user){
        Calendar c;
        int h;
        String s = null;
        c = Calendar.getInstance();
        c.setTime(now);
        h = c.get(Calendar.HOUR_OF_DAY);
        if(h>=6 && h<12){
            s = "Good Morning!";
        }else if(h>=12 && h<19){
            s = "Good Afternoon!";
        }else{
            s = "Good Night!";
        }
        s = "Hi, " +user+". "+s;
        return s;
    }
}


在看完此段程式後雖然邏輯可能簡單不須註解你可以看得懂
這只是一個簡易示例

第一. 循序程式設計(上至下),無層次
第二. 少註解、變數名稱取不好(沒有意義)
第三. 聚合度太低

所以進行第一階段簡單重構



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package helloworld;

import java.util.Calendar;
import java.util.Date;

/**
 *
 * @author chous
 */
public class HelloWorld {
    /**
     * Say Hello to user
     * @param now
     * @param user
     * @return words what to say
     */
    public String sayHello(Date now, String user) {
        //Get current hour of day
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        
        //Get the right words to say
        String words = null;
        if (hour >= 6 && hour < 12) {
            words = "Good Morning!";
        } else if (hour >= 12 && hour < 19) {
            words = "Good Afternoon!";
        } else {
            words = "Good Night!";
        }
        words = "Hi, " + user + ". " + words;
        return words;
    }
}


接著  我們將上面兩段註解的Code
拆分提取出來形成  getHour()  與  getSecondGreeting()
兩函數業務邏輯



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package helloworld;

import java.util.Calendar;
import java.util.Date;

/**
 *
 * @author chous
 */
public class HelloWorld {
    /**
     * Say Hello to user
     * @param now
     * @param user
     * @return words what to say
     */
    public String sayHello(Date now, String user) {
        int hour = getHour(now);
        return "Hi, " + user + ". " + getSecondGreeting(hour);
    }
    /**
     * Get current hour of day
     * @param now
     * @return current hour of day
     */
    private int getHour(Date now){
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        return calendar.get(Calendar.HOUR_OF_DAY);
    }
    
    /**
     * Get the second greeting
     * @param hour
     * @return the second greeting
     */
    private String getSecondGreeting(int hour) {
        if (hour >= 6 && hour < 12) {
            return "Good Morning!";
        } else if (hour >= 12 && hour < 19) {
            return "Good Afternoon!";
        } else {
            return "Good Night!";
        }
    }
}

藉此練習
我們可以發覺 原先沒有先後次序的語句
被修改地十分有次序

將語句中某段相對較獨立的語句功能提煉出來形成一個函數

讓原語句去呼叫調用

許多函數名稱修改更具有意義,變數名稱修改等等細節上
就是一種等量變化
並不會改變程式原有功能


程式組織架構變更加容易閱讀、更容易維護


重構過程方法層次

主要可分為

(1)「方法的重構」
(2)「物件的重構」
(3)「物件間的重構」
(4)「繼承體系間的重構」
(5)「組織資料的重構」
(6)「體系架構的重構」

娜上個例子主要是練習方法的重構
你會發現主要都很常發生在一個物件類別中
主要是去針對該物件類別內部去改良

主要在剛剛練習過程
我們可以先排版註解完並且重新調整上至下循序邏輯
之後在獨立抽出方法去實作使程式區塊職責分明
這個就是常用的  Extract Method 抽取方法  的技法


技法2.抽取類別(Extract Class)

那你會發現好像還不夠
程式的內聚性不佳(太低) ---->講白話一點就是還是一個 牽一髮動全身的物件
可能需要
用一個  DateUtility  去管理類似像getHour()的方法
用一個  Greeting      去管理各種問候語和規則

藉此將內聚性提高
達到某個class或function負責處理的事情 ,已經無法再切分成別的class或function。
使單一Class就只做單一功能職責。






藉此改善後的 原先HelloWorld Class中程式碼變得十分乾淨


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package helloworld;

import java.util.Calendar;
import java.util.Date;

/**
 *
 * @author chous
 */
public class HelloWorld {

    /**
     * Say Hello to user
     *
     * @param now
     * @param user
     * @return words what to say
     */
    public String sayHello(Date now, String user) {
        DateUtility dateUtility = new DateUtility();
        int hour = dateUtility.getHour(now);
        
        Greeting greeting = new Greeting();
        return greeting.getFirstGreeting(user)+greeting.getSecondGreeting(hour);
    }
}

DateUtility Class -->只做幫我們取得當前時間(Hour整點)

HOUR_OF_DAY 是採用 24-hour clock時間制
E.g.,   at 10:04:15.250 PM the HOUR_OF_DAY is 22

HOUR 則是採用 12-hour clock時間制
(0 - 11). Noon and midnight are represented by 0, not by 12.
 E.g., at 10:04:15.250 PM the HOUR is 10.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package helloworld;

import java.util.Calendar;
import java.util.Date;

/**
 * A utility about time.
 * @author chous
 */
public class DateUtility {

    /**
     * Get current hour of day
     *
     * @param now
     * @return current hour of day
     */
    public int getHour(Date now) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(now);
        return calendar.get(Calendar.HOUR_OF_DAY);
    }
}


Greeting Class -->只負責握取得相關打招呼回應結果及相關規則方法


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package helloworld;

/**
 * All kinds of greeting.
 * @author chous
 */
public class Greeting {

    /**
     * Get the first greeting
     *
     * @param user
     * @return Hi, {user}.
     */
    public String getFirstGreeting(String user) {
        return "Hi, " + user + ". ";
    }

    /**
     * Get the second greeting
     *
     * @param hour
     * @return the second greeting, if in the morning, say "Good morning!", if
     * in the afternoon, say "Good afternoon!", else say "Good night!".
     */
    public String getSecondGreeting(int hour) {
        if (hour >= 6 && hour < 12) {
            return "Good Morning!";
        } else if (hour >= 12 && hour < 19) {
            return "Good Afternoon!";
        } else {
            return "Good Night!";
        }
    }
}

















留言

這個網誌中的熱門文章

何謂淨重(Net Weight)、皮重(Tare Weight)與毛重(Gross Weight)

Architecture(架構) 和 Framework(框架) 有何不同?_軟體設計前的事前規劃的藍圖概念

(2021年度)駕訓學科筆試準備題庫歸納分析_法規是非題