重構 -改變既有代碼的設計 ---- 筆記

目錄java

這是一篇《重構 》的總結 ,我在學習的同時並使用它做爲參考。這不是一本書的替代品,因此你要想真的想學習裏面的內容,買一本書使用這個文章做爲參考和指南。git

另外: 建議 評論 還 PR 都是十分歡迎的github

1. TABLE OF CONTENT

3. BAD SMELLS IN CODE(代碼的壞味道)

1. Duplicated code (重複的代碼)

多個地方使用相同的代碼算法

2. Long Method(很長的方法)

一個很長的程序是很難被理解的數據庫

3. Large Classes(超級大的類)

當一個類變的愈來愈大的時候,是難以閱讀的express

4. Long Parameter List(長參數列表)

長參數是很難去理解,不符合也較難使用編程

5. Divergent Change(發散的改變)

當一個類常常由於不一樣緣由發生在不一樣的方向發生變化數組

6. Shotgun Surgery(散彈槍修改)

每次你作一小部分修改時,都不的不須要作大量的修改在不少不一樣的類中session

7. Feature Envy(依戀情結)

某個方法彷佛在另外的類的興趣高於本身所處的類架構

8. Data Clumps(數據泥團)

一堆數據雜糅在一塊兒(字段, 參數)

9. Primitive Obsession(基本類型偏執)

使用基本類型替代小對象

10. Switch Statements(Switch 驚悚現身)

當程序中出現不少 switch 語句在不少地方,使用多態來進行替換

11. Parallel Inheritance Hierarchies(平行繼承類)

每當你爲一個類增長一個子類,你不得不爲另外一個類增長相應的一個子類

12. Lazy Class(冗餘類)

當一個類不足與爲其自身買單它就應該被刪除

13. Speculative Generality(誇誇其談將來性)

全部的鉤子和特殊狀況處理那些不須要的

14. Temporary Field(使人迷惑的臨時變量)

一個臨時變量僅僅爲某種特殊狀況作而定,這樣的代碼讓人難以理解

15. Message Chain(過長的消息鏈)

當一個類請求調用一個對象,可是這個類又在調用其餘的方法

16. Middle Man(中間人)

當一個對象委託大部分功能,開發中可能會過分的使用委託模式,致使某個類中的方法大都委託給其餘方法處理

17. Inappropriate Intimacy(不恰當的親密關係)

當兩個類過分的親密,須要將其拆散

18. Alternative Classes with Different Interfaces(殊途同歸的類)

類的方法過分類似

19. Incomplete Library Class(不完美的庫)

當咱們使用外部依賴庫時

20. Data Class(數據類)

不要操做數據類,咱們經過封裝它的不變性

21. Refused Bequest(被拒絕的遺贈)

子類不想使用父類的方法

22. Comments(過多沒用的註釋)

當一個方法使用過分的註釋解釋其中的邏輯時,說明這個方法應該被重構了。

6. COMPOSING METHODS(從新組織函數))

1. Extract Method(提煉函數)

你能夠將一些代碼組合起來,而後放到一個方法中

void printOwing(double amount) {
        printBanner();
        //print details
        System.out.println ("name:" + _name);
        System.out.println ("amount" + amount);
    }

to

void printOwing(double amount) {
        printBanner();
        printDetails(amount);
    }

    void printDetails (double amount) {
        System.out.println ("name:" + _name);
    System.out.println ("amount" + amount);
    }

動機

  • 增長代碼被複用的機會
    * 閱讀方法就像閱讀一系列聲明同樣簡單
void printOwing(double previousAmount) {
        Enumeration e = _orders.elements();
        double outstanding = previousAmount * 1.2;
        printBanner();

        // calculate outstanding
        while (e.hasMoreElements()) {
            Order each = (Order) e.nextElement();
            outstanding += each.getAmount();
        }
        printDetails(outstanding);
    }

to

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

    double getOutstanding(double initialValue) {
        double result = initialValue;
        Enumeration e = _orders.elements();

        while (e.hasMoreElements()) {
            Order each = (Order) e.nextElement();
            result += each.getAmount();
        }
        return result;
    }

2. Inline Method (內聯函數)

一個函數本體和函數名稱同樣容易理解

int getRating() {
        return (moreThanFiveLateDeliveries()) ? 2 : 1;
    }

    boolean moreThanFiveLateDeliveries() {
        return _numberOfLateDeliveries > 5;
    }

to

int getRating() {
        return (_numberOfLateDeliveries > 5) ? 2 : 1;
    }

動機

  • 當間接不是必要的時候
  • 當一組方法被嚴格的分解,會使這個方法變得清晰

3. Inline Temp (內聯臨時變量)

你申明瞭一個臨時的變量在一段表達裏面,而後臨時的變量將會阻擋你重構

double basePrice = anOrder.basePrice();
    return (basePrice > 1000)

to

return (anOrder.basePrice() > 1000)

Motivation

4. Replace Temp with Query (以查詢取代臨時變量)

你正在使用臨時變量來保存表達式的結果

double basePrice = _quantity * _itemPrice;
    if (basePrice > 1000){
        return basePrice * 0.95;
    }
    else{
        return basePrice * 0.98;
    }

to

if (basePrice() > 1000){
        return basePrice() * 0.95;
    }
    else{
        return basePrice() * 0.98;
    }
    ...
    double basePrice() {
        return _quantity * _itemPrice;
    }

動機

  • 使用方法代替臨時變量,類中的任何方法均可以獲取信息
  • 這是一個十分重要的步驟在其以前1. Extract Method

5. Introduce Explaining Variable (引入解釋性變量)

有一個複雜的表達式

if ( (platform.toUpperCase().indexOf("MAC") > -1) &&
        (browser.toUpperCase().indexOf("IE") > -1) &&
        wasInitialized() && resize > 0 )
    {
        // do something
    }

to

final boolean isMacOs = platform.toUpperCase().indexOf("MAC") >-1;
    final boolean isIEBrowser = browser.toUpperCase().indexOf("IE") >-1;
    final boolean wasResized = resize > 0;
    if (isMacOs && isIEBrowser && wasInitialized() && wasResized) {
        // do something
    }

動機

  • 當一個表達式難以理解時

6. Split Temporary Variable (分解臨時變量)

你能有一個臨時變量聲明不止一次,可是它不是循環體中的變量或者要被存儲的變量

double temp = 2 * (_height + _width);
    System.out.println (temp);
    temp = _height * _width;
    System.out.println (temp);

to

final double perimeter = 2 * (_height + _width);
    System.out.println (perimeter);
    final double area = _height * _width;
    System.out.println (area);

動機

  • 變量不該該有屢次的聲明
  • 使用臨時變量在兩次不一樣的地方,是閱讀者十分迷惑的

7. Remove Assignments to Parameters(移除對參數的賦值)

下的代碼將對參數進行了賦值

int discount (int inputVal, int quantity, int yearToDate) {
        if (inputVal > 50) {
            inputVal -= 2;
        }
    }

to

int discount (int inputVal, int quantity, int yearToDate) {
        int result = inputVal;
        if (inputVal > 50) {
            result -= 2;
        }
    }

動機

  • 改變內部的對象時能夠的,可是不能將這個對象指向別的對象
  • 參數的做用僅僅是表達傳遞對象

8. Replace Method with Method Object (以函數對象取代函數)

將這個函數放進一個單獨的對象,如此一來局部變量就變成對象內部的字段,而後你能夠在同一個對象中將這個大型函數分解爲多個小型函數

class Order...
        double price() {
            double primaryBasePrice;
            double secondaryBasePrice;
            double tertiaryBasePrice;
            // long computation;
            ...
        }

to

class Order...
        double price(){
            return new PriceCalculator(this).compute()
        }
    }

    class PriceCalculato...
    compute(){
        double primaryBasePrice;
        double secondaryBasePrice;
        double tertiaryBasePrice;
        // long computation;
        return ...
    }

動機

  • 當一個方法有不少的本地變量時進行分解時不容易的

它的樣本本不該該這樣的重構,可是爲顯示這樣作的方法

Class Account
        int gamma (int inputVal, int quantity, int yearToDate) {
            int importantValue1 = (inputVal * quantity) + delta();
            int importantValue2 = (inputVal * yearToDate) + 100;
            if ((yearToDate - importantValue1) > 100)
            importantValue2 -= 20;
            int importantValue3 = importantValue2 * 7;
            // and so on.
            return importantValue3 - 2 * importantValue1;
        }
    }

to

class Gamma...
        private final Account _account;
        private int inputVal;
        private int quantity;
        private int yearToDate;
        private int importantValue1;
        private int importantValue2;
        private int importantValue3;

        Gamma (Account source, int inputValArg, int quantityArg, int yearToDateArg) {
            _account = source;
            inputVal = inputValArg;
            quantity = quantityArg;
            yearToDate = yearToDateArg;
        }

        int compute () {
            importantValue1 = (inputVal * quantity) + _account.delta();
            importantValue2 = (inputVal * yearToDate) + 100;
            if ((yearToDate - importantValue1) > 100)
            importantValue2 -= 20;
            int importantValue3 = importantValue2 * 7;
            // and so on.
            return importantValue3 - 2 * importantValue1;
        }

        int gamma (int inputVal, int quantity, int yearToDate) {
            return new Gamma(this, inputVal, quantity,yearToDate).compute();
        }

9. 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 "";
    }

to

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 "";
    }

動機

  • 打破一些複雜的概念
  • 使算法更容易修改
  • 替換一個大的複雜的算法是十分困難的,讓算法變的簡單更容易對算法進行替換

7. Moving features between elements(移動對象)

10. Move method (移動方法)

在進行方法的初始定義的時候要想下之後會不會有其餘的類也將會用到它

一個類它一般會建立一個新的簡單的方法體, 同時它會將9⃣舊的方法作一個簡單的委託或者移除它

class Class1 {
        aMethod()
    }

    class Class2 {  }

to

class Class1 {  }

    class Class2 {
        aMethod()
    }

動機

當一個類作了不少工做,或者這個類過分的耦合

11. Move field (移動字段)

當一個字段被定義的時候,可能不只被不止一個類使用。
建立一個字段在一個目標類中,而後改變全部的擁有者

class Class1 {
        aField
    }

    class Class2 {  }

to

class Class1 {  }

    class Class2 {
        aField
    }

動機

若是一個字段被超過多個類引用

12. Extract Class (提取類)

你有一個類,可是這個類作了它份外的事情

建立一個新的類,而後將相關字段移入到新的類中

class Person {
        name,
        officeAreaCode,
        officeNumber,
        getTelephoneNumber()
    }

to

class Person {
        name,
        getTelephoneNumber()
    }

    class TelephoneNumber {
        areaCode,
        number,
        getTelephoneNumber()
    }

動機

類隨着業務的增加在變化

在合適的時候進行分解它

  • 類似的方法組合在一塊兒
  • 數據子集一般一塊兒變化或者相互依賴

13. Inline Class (一致的類)

一個其實沒作多少事情的類

將這個類整合到另一個類中,而後刪除這個類

class Person {
        name,
        getTelephoneNumber()
    }

    class TelephoneNumber {
        areaCode,
        number,
        getTelephoneNumber()
    }

to

class Person {
        name,
        officeAreaCode,
        officeNumber,
        getTelephoneNumber()
    }

動機

在重構的時候將這個類的基本信息移入到另一個類中,而後在移除這個類

14. Hide Delegate (隱藏委託)

客戶端其實調用的是對象的委託類
在服務端建立一個方法,而後隱藏這個委託類

class ClientClass {
        //Dependencies
        Person person = new Person()
        Department department = new Department()
        person.doSomething()
        department.doSomething()
    }

to

class ClientClass {
        Person person = new Person()
        person.doSomething()
    }

    class Person{
        Department department = new Department()
        department.doSomething()
    }

解決方法

class ClientClass{
        Server server = new Server()
        server.doSomething()
    }

    class Server{
        Delegate delegate = new Delegate()
        void doSomething(){
            delegate.doSomething()
        }
    }
    //委託類其實隱藏在客戶類裏面
    // 改變不會傳播到客戶端那邊,由於它以後影響到服務端這邊
    class Delegate{
        void doSomething(){...}
    }

動機

關鍵在於封裝
類應該儘可能的使用其餘的類
> manager = john.getDepartment().getManager();

class Person {
        Department _department;
        public Department getDepartment() {
            return _department;
        }
        public void setDepartment(Department arg) {
            _department = arg;
            }
        }

    class Department {
        private String _chargeCode;
        private Person _manager;
        public Department (Person manager) {
            _manager = manager;
        }
        public Person getManager() {
            return _manager;
        }
        ...

to

> manager = john.getManager();

class Person {
        ...
        public Person getManager() {
            return _department.getManager();
        }
    }

15. Remove Middle Man (移除中間人)

一個類經過代理幹了太多的事情
讓客戶直接調用委託

class ClientClass {
        Person person = new Person()
        person.doSomething()
    }

    class Person{
        Department department = new Department()
        department.doSomething()
    }

to

class ClientClass {
        //Dependencies
        Person person = new Person()
        Department department = new Department()
        person.doSomething()
        department.doSomething()
    }

動機

當客戶類使用過多的中間人調用委託的方法

16. Introduce Foreign Method (引入外加的函數)

一個類是引用的外部開源包,可是不能修改其內部的邏輯
建立一個新的方法在這個類中,並以第一個參數的形式傳入一個服務類實例

Date newStart = new Date(previousEnd.getYear(),previousEnd.getMonth(),previousEnd.getDate()+1);

to

Date newStart = nextDay(previousEnd);

    private static Date nextDay(Date date){
        return new Date(date.getYear(),date.getMonth(),date.getDate()+1);
    }

動機

當你使用一個類,這個類你又不能對其進行修改的時候能夠採用這樣方式

17. Introduce Local Extension (引入本地擴展)

你須要爲一個服務類提供一些額外的方法,可是你沒法修改這個子類
建立一個新的類,使它包含這些額外的方法。這個擴展的類成爲源類的子類或者包裝類

class ClientClass(){

        Date date = new Date()
        nextDate = nextDay(date);

        private static Date nextDay(Date date){
            return new Date(date.getYear(),date.getMonth(),date.getDate()+1);
        }
    }

to

class ClientClass() {
        MfDate date = new MfDate()
        nextDate = nextDate(date)
    }
    class MfDate() extends Date {
        ...
        private static Date nextDay(Date date){
            return new Date(date.getYear(),date.getMonth(),date.getDate()+1);
        }
    }

動機

當咱們使用 16. Introduce Foreign Method 咱們須要在這個類中添加額外的方法

8. ORGANIZING DATA (組織數據)

18. Self Encapsulate Field (對字段獲取進行封裝)

你能夠直接獲取對象,可是這樣的話會變得愈來愈複雜
經過建立setting getting 方法來獲取這些字段

private int _low, _high;
    boolean includes (int arg) {
        return arg >= _low && arg <= _high;
    }

to

private int _low, _high;
    boolean includes (int arg) {
        return arg >= getLow() && arg <= getHigh();
    }
    int getLow() {return _low;}
    int getHigh() {return _high;}

動機

容許子類能夠覆蓋如何get方法,而且這樣的話它更加的支持靈活的管理,例如延遲加載

19. Replace Data Value with Object (用對象替換數據值)

當你有個數據項須要進行添加數據或行爲
將數據項轉換爲對象

class Order...{
        private String _customer;
        public Order (String customer) {
            _customer = customer;
        }
    }

to

class Order...{
        public Order (String customer) {
            _customer = new Customer(customer);
        }
    }

    class Customer {
        public Customer (String name) {
            _name = name;
        }
    }

動機
簡單的數據對象並不簡單

20. Change Value to Reference (將值改成引用)

你有個類擁有不少單個對象,這些對象須要用一個單獨的對象替代
將這個對象轉換爲引用對象

class Order...{
        public Order (String customer) {
            _customer = new Customer(customer);
        }
    }

    class Customer {
        public Customer (String name) {
            _name = name;
        }
    }

to

//Use Factory Method
    //使用工廠方法
    class Customer...
        static void loadCustomers() {
            new Customer ("Lemon Car Hire").store();
            new Customer ("Associated Coffee Machines").store();
            new Customer ("Bilston Gasworks").store();
        }
        private void store() {
            _instances.put(this.getName(), this);
        }
        public static Customer create (String name) {
            return (Customer) _instances.get(name);
        }

動機
引用對象是相似於消費者或者帳單這樣的對象,每一個對象表明這一類對象在一個真實的世界,並使用對象表示來測試它們是否相同

21. Change Reference to Value (將引用改成值)

你有一個有一個引用對象是很小,不變,難以管理的
將其轉換爲值對象

new Currency("USD").equals(new Currency("USD")) // returns false

to

new Currency("USD").equals(new Currency("USD")) // now returns true

動機
使用引用對象是變得越來月複雜,而且引用對象是不變和單一的。尤爲在分佈式和併發系統中

22. Replace Array with Object (用對象代替數組)

你擁有一個數組,其中這些元素是不一樣的
使用一個對象來替換這個數組,將數組的元素賦值在對象的屬性上

String[] row = new String[3];
    row [0] = "Liverpool";
    row [1] = "15";

to

Performance row = new Performance();
    row.setName("Liverpool");
    row.setWins("15");

動機

數組應該被用在一些類似的集合對象序列中

23. Duplicate Observed Data (監控數據對象)

可能你有一些domain數據是經過GUI控制的與此同時這些domain數據是須要訪問的
往一些實體對象複製一些數據,經過設置觀察者來同步兩部分數據

動機
爲了將代碼從用戶界面分解到業務處理層

24. Change Unidirectional Association to Bidirectional(將單向聯繫改成雙向聯繫)

你有兩個對象,這兩個對象須要使用對方的特徵屬性,可是目前只有一種鏈接方式
添加返回指針,而後更改修飾符已更改兩個對象

class Order...
        Customer getCustomer() {
            return _customer;
        }
        void setCustomer (Customer arg) {
            _customer = arg;
        }
        Customer _customer;
    }

to

class Order...
        Customer getCustomer() {
            return _customer;
        }
        void setCustomer (Customer arg) {
            if (_customer != null) _customer.friendOrders().remove(this);
            _customer = arg;
            if (_customer != null) _customer.friendOrders().add(this);
        }
        private Customer _customer;

        class Customer...
            void addOrder(Order arg) {
                arg.setCustomer(this);
            }
            private Set _orders = new HashSet();

            Set friendOrders() {
                /** should only be used by Order */
                return _orders;
            }
        }
    }

    // Many to Many
    class Order... //controlling methods
        void addCustomer (Customer arg) {
            arg.friendOrders().add(this);
            _customers.add(arg);
        }
        void removeCustomer (Customer arg) {
            arg.friendOrders().remove(this);
            _customers.remove(arg);
        }
    class Customer...
        void addOrder(Order arg) {
            arg.addCustomer(this);
        }
        void removeOrder(Order arg) {
            arg.removeCustomer(this);
        }
    }

動機
當對象引用須要互相引用的時候,你應該採用這種方法

25. Change Bidirectional Association to Unidirectional (將單向改成雙向的聯繫)

當你有個雙向聯繫的類,可是在後期一個類不在須要另外一個類中的屬性了
扔掉不須要的聯繫

動機
當雙向聯繫不在須要,減小複雜度,移除殭屍對象,消除相互依賴

26. Replace Magic Number with Symbolic Constant (使用符號來替代魔法字符串)

你有一個特定含義的字符串

建立一個常量,名稱根據它的意思命名而後替換那個數字

double potentialEnergy(double mass, double height) {
        return mass * 9.81 * height;
    }

to

double potentialEnergy(double mass, double height) {
        return mass * GRAVITATIONAL_CONSTANT * height;
    }
    static final double GRAVITATIONAL_CONSTANT = 9.81;

動機
避免使用魔法數字

27. Encapsulate Field (封裝字段)

這裏有一個公共的字段
將它改成私有的並提供訪問函數

public String _name

to

private String _name;
    public String getName() {
        return _name;
    }
    public void setName(String arg) {
        _name = arg;
    }

動機
你應該將你數據公開

28. Encapsulate Collection (封裝集合)

一個返回幾個的方法
確保返回一個只讀的影像對象,而後提供添加和移除方法

class Person {
        Person (String name){
            HashSet set new HashSet()
        }
        Set getCourses(){}
        void setCourses(:Set){}
    }

to

class Person {
        Person (String name){
            HashSet set new HashSet()
        }
        Unmodifiable Set getCourses(){}
        void addCourses(:Course){}
        void removeCourses(:Course){}
    }

動機

  • 封裝減小了擁有類和和其客戶端的耦合
  • getter 方法不該該返回集合的自己
  • getter方法應返回對集合進行操做的內容並隱藏其中沒必要要的細節
  • 這個幾個不該該有setter方法,只能添加和移除操做

29. Remove Record with data class (在數據類中移除 記錄值)

你必須面對一個記錄值在傳統的編程環境中
使用一個殭屍數據對象代替記錄值

動機

  • 複製一個傳奇代碼
  • 使用傳統的API編程和數據庫來代替一個記錄值

30. Replace Type Code with Class (使用類來替代類別代碼)

一個類擁有數字類別碼,不能影響其行爲
使用類來代替數字

class Person{
        O:Int;
        A:Int;
        B:Int;
        AB:Int;
        bloodGroup:Int;
    }

to

class Person{
        bloodGroup:BloodGroup;
    }

    class BloodGroup{
        O:BloodGroup;
        A:BloodGroup;
        B:BloodGroup; 
        AB:BloodGroup;
    }

動機
靜態類別檢查

31. Replace Type Code with Subclasses (使用子類來替代類別代碼)

在一個類中擁有一個不變的類型碼影響整個類的行爲
使用子類來替代這個不變的類型碼

class Employee...
        private int _type;

        static final int ENGINEER = 0;
        static final int SALESMAN = 1;
        static final int MANAGER = 2;

        Employee (int type) {
            _type = type;
        }
    }

to

abstract int getType();
    static Employee create(int type) {
        switch (type) {
            case ENGINEER:
                return new Engineer();
            case SALESMAN:
                return new Salesman();
            case MANAGER:
                return new Manager();
            default:
                throw new IllegalArgumentException("Incorrect type code value");
        }
    }

動機

  • 執行不一樣的代碼邏輯取決於這個type的值
  • 當每一個type對象有着惟一的特徵
  • 應用於架構體

32. Replace Type Code with State/Strategy(經過狀態模式或者策略模式來代替類型碼)

在類中有個類型碼,並經過這個類型碼來影響行爲,可是你不能使用子類
經過狀態對象來代替這個類型碼

class Employee {
        private int _type;

        static final int ENGINEER = 0;
        static final int SALESMAN = 1;
        static final int MANAGER = 2;

        Employee (int type) {
            _type = type;
        }
        int payAmount() {
            switch (_type) {
                case ENGINEER:
                    return _monthlySalary;
                case SALESMAN:
                    return _monthlySalary + _commission;
                case MANAGER:
                    return _monthlySalary + _bonus;
                default:
                    throw new RuntimeException("Incorrect Employee");
                }
            }
        }
    }

to

class Employee...
        static final int ENGINEER = 0;
        static final int SALESMAN = 1;
        static final int MANAGER = 2;

        void setType(int arg) {
            _type = EmployeeType.newType(arg);
        }
        class EmployeeType...
            static EmployeeType newType(int code) {
                switch (code) {
                    case ENGINEER:
                        return new Engineer();
                    case SALESMAN:
                        return new Salesman();
                    case MANAGER:
                        return new Manager();
                    default:
                        throw new IllegalArgumentException("Incorrect Employee Code");
                }
            }
        }
        int payAmount() {
            switch (getType()) {
                case EmployeeType.ENGINEER:
                    return _monthlySalary;
                case EmployeeType.SALESMAN:
                    return _monthlySalary + _commission;
                case EmployeeType.MANAGER:
                    return _monthlySalary + _bonus;
                default:
                    throw new RuntimeException("Incorrect Employee");
            }
        }
    }

動機

  • 31. Replace Type Code with Subclasses 是類似的,可是它可使用在類型碼發生了改變在對象的生命週期發生了變化或者另外一個緣由阻止了子類的變化,則可使用它
  • 它一般是和狀態模式或者策略模式配合使用

32. Replace Subclass with Fields(用字段代替子類)

你的子類僅在返回常數變量數據變量的方法中有所不一樣
將這個方法提高到父類中,並移除這個子類

abstract class Person {
        abstract boolean isMale();
        abstract char getCode();
        ...
    }
    class Male extends Person {
            boolean isMale() {
            return true;
        }
        char getCode() {
            return 'M';
        }
    }
    class Female extends Person {
        boolean isMale() {
            return false;
        }
        char getCode() {
            return 'F';
        }
    }

to

class Person{
        protected Person (boolean isMale, char code) {
            _isMale = isMale;
            _code = code;
        }
        boolean isMale() {
            return _isMale;
        }
        static Person createMale(){
            return new Person(true, 'M');
        }
        static Person createFemale(){
            return new Person(false, 'F');
        }
    }

動機

  • 當子類的某個方法不足與繼續存在
  • 將這個子類完全刪除,並將這個字段上移到父類中
  • 刪除額外的子類

9. SIMPLIFYING CONDITIONAL EXPRESSIONS(簡化條件表達式)

33. Decompose Conditional (分解條件)

你有一個複雜的條件(大量的if else then )
使用額外的方法代替這個表達式,將then 放在一部分,else 放在一部分

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

to

if (notSummer(date))
        charge = winterCharge(quantity);
    else 
        charge = summerCharge (quantity);

動機

  • 將條件表達式高亮,這樣的話你能清楚將其分開
  • 對分叉以後的結果進行高亮

34. Consolidate Conditional Expression

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

to

double disabilityAmount() {
    if (isNotEligableForDisability()) return 0;
    // compute the disability amount

35. Consolidate Duplicate Conditional Fragments (合併重複的條件片斷)

在條件表達式的每一個分支上有着相同的一片代碼
將這段重複代搬移到條件表達式以外

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

to

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

動機
使得變量清晰並保持相同

36. Remove Control Flag (移除控制標記)

在一系列的布爾表達式中,某個變量帶有「控制標記」的做用
已break或者return語句取代控制標記

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;
                    }
                }
        }
    }

to

void checkSecurity(String[] people) {
        for (int i = 0; i < people.length; i++) {
            if (people[i].equals ("Don")){
                sendAlert();
                break; // or return
            }
            if (people[i].equals ("John")){
                sendAlert();
                break; // or return
            }
        }
    }

動機

  • 控制標記的做用是在於決定是否繼續下面流程,可是現代語言注重於使用breakcontinue
  • 確保真實的條件表達式是清晰的

37. Replace Nested Conditional with Guard Clauses (以衛語句取代嵌套的條件表達式)

函數的條件邏輯令人難以看清正常的執行路徑
使用衛語句表現全部的特殊狀況

double getPayAmount() {
        double result;
        if (_isDead) result = deadAmount();
        else {
            if (_isSeparated) result = separatedAmount();
            else {
                if (_isRetired) result = retiredAmount();
                else result = normalPayAmount();
            };
        }
        return result;
    };

to

double getPayAmount() {
        if (_isDead) return deadAmount();
        if (_isSeparated) return separatedAmount();
        if (_isRetired) return retiredAmount();
        return normalPayAmount();
    };

動機

  • 若是這個條件是非同尋常的條件,檢查這條件是否符合而後返回true
  • 這樣大度的檢查被稱爲「衛語句」
  • 對某一條分支已特別的重,若是使用if-then-else 結構,你對if分支和else分支的重要性是同等的
  • 各個分支具備同同樣的重要性
  • 取代以前的觀念 「每一個函數只能有一個入口和一個出口」

38. Replace Conditional with Polymorphism (以多態取代條件表達式)

你手上有一個條件表達式,它根據對象的類型的不一樣選擇不一樣的行爲
將條件表達式的全部分支放進一個子類內的覆蓋函數中,而後將原始函數聲明爲抽象函數

class Employee {
        private int _type;

        static final int ENGINEER = 0;
        static final int SALESMAN = 1;
        static final int MANAGER = 2;

        Employee (int type) {
            _type = type;
        }
        int payAmount() {
            switch (_type) {
                case ENGINEER:
                    return _monthlySalary;
                case SALESMAN:
                    return _monthlySalary + _commission;
                case MANAGER:
                    return _monthlySalary + _bonus;
                default:
                    throw new RuntimeException("Incorrect Employee");
                }
            }
        }
    }

to

class Employee...
        static final int ENGINEER = 0;
        static final int SALESMAN = 1;
        static final int MANAGER = 2;

        void setType(int arg) {
            _type = EmployeeType.newType(arg);
        }
        int payAmount() {
            return _type.payAmount(this);
        }
    }

    class Engineer...
        int payAmount(Employee emp) {
            return emp.getMonthlySalary();
        }
    }

動機

  • 若是對象的行爲因其類型而異,請避免編寫顯示的條件
  • Switch 聲明在面嚮對象語言中應該儘可能少的被使用

39. Introduce Null Object (引入Null 對象)

你不得不檢查對象是否爲Null對象
將null值替換爲null對象

if (customer == null){
        plan = BillingPlan.basic();
    } 
    else{
        plan = customer.getPlan();
    }

to

class Customer {

    }

    class NullCusomer extends Customer {

    }

動機

  • 對象根據其類型作正確的事情,Null對象也應該遵照這個規則

40. Introduce Assertion (引入斷言)

某段代碼須要對程序狀態作出某種假設
已斷言明確表現這種假設

double getExpenseLimit() {
        // should have either expense limit or a primary project
        return (_expenseLimit != NULL_EXPENSE) ?
            _expenseLimit:
            _primaryProject.getMemberExpenseLimit();
    }

to

double getExpenseLimit() {
        Assert.isTrue (_expenseLimit != NULL_EXPENSE || _primaryProject != null);
        return (_expenseLimit != NULL_EXPENSE) ?
            _expenseLimit:
            _primaryProject.getMemberExpenseLimit();
    }

動機

  • 斷言是一種明確表達會爲true的行爲
  • 當斷言失敗時每每是一個非受控的異常
  • 斷言每每在生產代碼中移除
  • 交流層面 : 斷言能使程序的閱讀者理解代碼所作的假設
  • 在調試的角度 : 斷言能使編程者儘量近的接觸bug

10. MAKING METHOD CALLS SIMPLER (簡化函數的調用)

41. Rename method (函數更名)

函數的名稱不能表達函數的用途
修改函數名稱

getinvcdtlmt()

to

getInvoiceableCreditLimit

動機
函數的名稱最好能表達函數的意圖

42. Add Parameter (添加參數)

某個函數須要從調用端獲得更多的信息
爲此函數添加一個對象函數,讓改對象帶進函數所須要信息

getContact()

to

getContact(:Date)

動機
在改變方法以後,你得到更多的信息

43. Remove Parameter(移除參數)

一個參數不在函數中使用了
移除它

getContact(:Date)

to

getContact()

動機
一個參數再也不使用還留着它幹嗎?

44. Separate Query from Modifier (將查詢函數和修改函數分離)

某個函數即返回函數的狀態值,又修改對象的狀態
建立兩個不一樣的函數,其中一個負責查詢,另外一個負責修改

getTotalOutStandingAndSetReadyForSummaries()

to

getTotalOutStanding()
    SetReadyForSummaries()

動機
將有反作用的方法和沒有反作用的方法分開

45. Parameterize Method (令函數攜帶參數)

若干函數作了相似的工做,但在函數本體中卻包含了不一樣的值
建立單一函數,已參數表達那些不一樣的值

fivePercentRaise()
    tenPercentRaise()

to

raise(percentage)

動機
移除重複的代碼提升靈活度

46. Replace Parameter with Explicit Methods (已明確函數取代參數)

🈶一個函數,其中徹底取決於參數值而採起不一樣的行爲
針對該函數的每個可能值,創建一個獨立函數

void setValue (String name, int value) {
        if (name.equals("height")){}
            _height = value;
            return;
        }
        if (name.equals("width")){
            _width = value;
            return;
        }
        Assert.shouldNeverReachHere();
    }

to

void setHeight(int arg) {
        _height = arg;
    }
    void setWidth (int arg) {
        _width = arg;
    }

動機

  • 避免條件行爲
  • 在編譯器進行檢查
  • 清晰的接口

47. Preserve Whole Object (保持對象的完整)

你從某個對象支行取出若干值,將他們做爲某一次函數調用時的參數
改成傳遞一整個對象

int low = daysTempRange().getLow();
    int high = daysTempRange().getHigh();
    withinPlan = plan.withinRange(low, high);

to

withinPlan = plan.withinRange(daysTempRange());

動機

  • 使參數列表在變化的時候具備魯棒性
  • 使代碼更與易讀
  • 在代碼中移除類似的重複的代碼
  • 消極的 : 增長了函數參數在調用時的依賴

48. Replace Parameter with Method (已函數取代參數)

對象調用某個函數,並將其所得的結果做爲參數,傳遞給另外一個函數。而接受該參數的函數自己也可以調用錢一個函數
將函數接受者去除該項參數,並直接調用前一個函數

int basePrice = _quantity * _itemPrice;
    discountLevel = getDiscountLevel();
    double finalPrice = discountedPrice (basePrice, discountLevel);

to

int basePrice = _quantity * _itemPrice;
    double finalPrice = discountedPrice (basePrice);

動機

  • 若是一個函數能夠經過其餘的途徑得到參數值,那麼它就不該該經過參數取得該值
  • 若是你能有其餘的方法或者相同的計算值
  • 若是要在對調用對象的引用的其餘對象上調用方法

49. Introduce Parameter Object (引入參數對象)

某些參數老是很天然的同時出現
以一個對象取代這些參數

class Customer{
        amountInvoicedIn (start : Date, end : Date)
        amountReceivedIn (start : Date, end : Date)
        amountOverdueIn (start : Date, end : Date)
    }

to

class Customer{
        amountInvoicedIn (: DateRange)
        amountReceivedIn (: DateRange)
        amountOverdueIn (: DateRange)
    }

動機

  • 減小參數列表的長度
  • 使代碼看的更加簡潔

50. Remove Setting Method (移除設值函數)

類中的某個字段應該在對象建立時被設值,而後就再也不改變
去掉該字段的全部設值函數

class Employee{
        setImmutableValue()
    }

to

class Employee{
        ¯\_(ツ)_/¯
    }

動機
確保你的清晰的目的 : 若是你想你的字段在建立以後就不要被改變了,你就不該該提供一個setting方法用於確保你的字段是否不被改變的

51. Hide Method (隱藏函數)

有一個函數,歷來沒有被其餘任何類用到
將這個函數修改成 private

class Employee{
        public method()
    }

to

class Employee{
        private method()
    }

動機
若是一個方法不須要被外部調用,那麼就應該講這個方法隱藏

52. Replace Constructor with Factory Method (已工廠函數取代構造函數)

在建立對象時不只僅是作簡單的健夠動做
將構造函數替換爲工廠函數

Employee (int type) {
        _type = type;
    }

to

static Employee create(int type) {
        return new Employee(type);
    }

建立一個對象依賴於其子類,構造函數只能返回單一類型的對象,所以你須要將構造函數替換爲一個工廠函數

53. Encapsulate Downcast (封裝向下轉型)

某個函數返回的對象,須要由調用者執行向下轉型
將向下轉型動做轉移到函數中

Object lastReading() {
        return readings.lastElement();
    }

to

Reading lastReading() {
        return (Reading) readings.lastElement();
    }

動機
將一個方法最有效的返回值進行返回給函數的調用者
若是類型是準確的,檢查使用這個對象的方法並提供一個更爲有效的方法

54. Replace Error Code with Exception (以異常取代錯誤碼)

某個函數返回一個特定的代碼,用以表示某種錯誤狀況
改用異常將其拋出去

int withdraw(int amount) {
        if (amount > _balance)
            return -1;
        else {
            _balance -= amount;
            return 0;
        }
    }

to

void withdraw(int amount) throws BalanceException {
        if (amount > _balance)
            throw new BalanceException();
        _balance -= amount;
    }

動機
當一個程序在發生了一個不可處理的錯誤時,你須要使這個函數的調用者知道。向上拋出異常,讓上層調用者知道

55. Replace Exception with Test (已測試取代異常)

面對一個調用者能夠預先檢查的條件,你拋出了一個異常
修改調用者,使它在調用函數以前先作檢查

double getValueForPeriod (int periodNumber) {
        try {
            return _values[periodNumber];
        } catch (ArrayIndexOutOfBoundsException e) {
            return 0;
        }
    }

to

double getValueForPeriod (int periodNumber) {
        if (periodNumber >= _values.length)
            return 0;
        return _values[periodNumber];
}

動機

  • 不要過分使用異常,它們應該被用在檢查異常上
  • 不要用它來代替條件測試
  • 檢查異常錯誤條件在調用方法以前

11. DEALING WITH GENERALIZATION (處理歸納關係)

56. Pull up field (字段上移)

兩個子類擁有相同的字段
將該字段移到超類中

class Salesman extends Employee{
        String name;
    }
    class Engineer extends Employee{
        String name;
    }

to

class Employee{
        String name;
    }
    class Salesman extends Employee{}
    class Engineer extends Employee{}

動機

  • 刪除重複的代碼
  • 容許將子類的行爲轉移到父類中

57. Pull Up Method (構造函數本體上移)

你在各個子類中擁有一些構造函數,他們的本體幾乎徹底一致
在超類中新建一個構造函數,並在子類構造函數中調用它

class Salesman extends Employee{
        String getName();
    }
    class Engineer extends Employee{
        String getName();
    }

to

class Employee{
        String getName();
    }
    class Salesman extends Employee{}
    class Engineer extends Employee{}

動機

  • 消除重複的行爲

58. Pull Up Constructor Body (構造函數本體上移)

在子類中的構造函數與父類中的構造函數是相同的
在超類中建立一個構造函數,並在子類構造函數中調用它

class Manager extends Employee...
        public Manager (String name, String id, int grade) {
        _name = name;
        _id = id;
        _grade = grade;
    }

to

public Manager (String name, String id, int grade) {
        super (name, id);
        _grade = grade;
    }

動機

  • 構造函數與方法不一樣
  • 消除重複的代碼

59. Push Down Method (方法下移)

父類的某個方法至於某個子類相關
將其移到子類中

class Employee{
        int getQuota();
    }
    class Salesman extends Employee{}
    class Engineer extends Employee{}

to

class Salesman extends Employee{
        int getQuota();
    }
    class Engineer extends Employee{}

動機
當方法只在子類中顯現

60. Push Down Field (字段下移)

超類的字段只在某個子類中用到
將這個字段移到須要它的那些子類中去

class Employee{
        int quota;
    }
    class Salesman extends Employee{}
    class Engineer extends Employee{}

to

class Salesman extends Employee{
        int quota;
    }
    class Engineer extends Employee{}

動機
當一個字段只在子類中使用時

61. Extract Subclass (提煉子類)

類中的某些特性只被某些實例用到
新建一個子類,將上面所說的那一部分特性移到子類中去

class JobItem   {
        getTotalPrices()
        getUnitPrice()
        getEmployee()
    }

to

JobItem {
        getTotalPrices()
        getUnitPrice()
    }
    class class LabotItem extends JobItem   {
        getUnitPrice()
        getEmployee()
    }

動機
當一個類的行爲只用在某些實例中而不用在其餘類中

62. Extract Superclass (提煉超類)

兩個類具備類似特性
建立一個父類,而後將這兩個類中相同的部分移到父類中,而後在繼承這個父類

class Department{
        getTotalAnnualCost()
        getName()
        getHeadCount
    }
    class Employee{
        getAnnualCost()
        getName()
        getId
    }

to

class Party{
        getAnnualCost()
        getName()
    }
    class Department {
        getAnnualCost()
        getHeadCount
    }
    class Employee {
        getAnnualCost()
        getId
    }

動機
當兩個類有過多類似的地方的時候,就須要考慮下是否須要將這個類進行下抽象了

63. Extract Interface (提煉接口)

若干客戶使用類接口中的同一個子類,或者兩個類的接口有相同的部分
將相同的子集提煉到一個獨立接口中

class Employee {
        getRate()
        hasSpecialSkill()
        getName()
        getDepartment()
    }

to

interface Billable  {
        getRate()
        hasSpecialSkill()
    }
    class Employee implements Billable  {
        getRate
        hasSpecialSkill()
        getName()
        getDepartment()
    }

動機

  • 若一個類的子集明確被一系列的客戶使用
  • 若是一個類須要和多個類處理並能處理肯定的請求

64. Collapse Hierarchy (摺疊繼承體系)

超類和子類無太大區別
將它們合爲一個

class Employee{ }
    class Salesman extends Employee{    }

to

class Employee{ }

動機
該子類沒有帶來任何價值

65. Form Template Method (塑造模板函數)

有些子類,其中對應的某些函數以相同順序執行相似的操做,但各個操做的細節上有所不一樣
將這些操做分別放進獨立函數中,並保持它們都有相同的簽名,因而原函數也就變得相同了。而後將原函數上移到超類

class Site{}
    class ResidentialSite extends Site{
        getBillableAmount()
    }
    class LifelineSite extends Site{
        getBillableAmount()
    }

to

class Site{     
        getBillableAmount()
        getBaseAmount()
        getTaxAmount()
    }
    class ResidentialSite extends Site{
        getBaseAmount()
        getTaxAmount()
    }
    class LifelineSite extends Site{
        getBaseAmount()
        getTaxAmount()
    }

動機

66. Replace Inheritance with Delegation (以委託取代繼承)

某個子類只使用了超類接口中的一部分,或是根本不須要繼承而來的數據
_在子類中建立一個字段用以保存超類,調整子類函數,令它改2而委託超類:而後去除二者之間的繼承關係

class Vector{
        isEmpty()
    }

    class Stack extends Vector {}

to

class Vector {
        isEmpty()
    }

    class Stack {
        Vector vector
        isEmpty(){
            return vector.isEmpty()
        }
    }

動機

  • 當你使用委託類的時候會對你使用的數據字段更加的清晰
  • 在你控制以外的方法都會被忽略,你只須要關注於自身

67. Replace Delegation with Inheritance (以繼承取代委託)

你在兩個類之間使用簡單的委託關係,並常常爲整個接口編寫許多不少簡單的委託函數
讓委託類繼承委託類

class Person {
        getName()
    }

    class Employee {
        Person person
        getName(){
            return person.getName()
        }
    }

to

class Person{
        getName()
    }
    class Employee extends Person{}

動機

  • 若是你使用全部的方法在委託類中
  • 若是你不能使用委託類的全部函數,那麼久不該該使用它

12. BIG REFACTORINGS (大型重構)

68. Tease Apart Inheritance (梳理並分解繼承體系)

某個繼承體系同時承擔兩項責任
創建兩個繼承體系,並經過委託關係讓其中一個能夠調用另一個

class Deal{}
    class ActiveDeal extends Deal{}
    class PassiveDeal extends Deal{}
    class TabularActiveDeal extends ActiveDeal{}
    class TabularPassiveDeal extends PassiveDeal{}

to

class Deal{
        PresentationStyle presettationStyle;
    }
    class ActiveDeal extends Deal{}
    class PassiveDeal extends Deal{}

    class PresentationStyle{}
    class TabularPresentationStyle extends PresentationStyle{}
    class SinglePresentationStyle extends PresentationStyle{}

動機

  • 過分的繼承會致使代碼重複
  • 若是繼承體系中的某一特定層級上的全部類,其子類名稱都已相同的形容詞開始,那麼這個體系可能肩負着兩項不一樣的責任。
    | |

69. Convert Procedural Design to Objects (過程化設計轉爲對象設計)

你手上有些傳統過程化風格的代碼
將數據記錄變成對象,將大塊的行爲分紅小塊,並將行爲移入相關對象中

class OrderCalculator{
        determinePrice(Order)
        determineTaxes(Order)
    }
    class Order{}
    class OrderLine{}

to

class Order{
        getPrice()
        getTaxes()
    }
    class OrderLine{
        getPrice()
        getTaxes()
    }

動機
使用面向對象思想進行變成

70. Separate Domain from Presentation (將領域和表述/顯示分離)

某些GUI類中包含了領域邏輯
將領域邏輯分離出來嗎,爲他們建立獨立的領域類

class OrderWindow{}

to

class OrderWindow{
        Order order;
}

動機

  • 分裂兩個過於複雜的代碼,使他們更易於修改
  • 容許多層風格編寫的程序
  • 這是值得被使用的

71. Extract Hierarchy (提煉繼承體系)

有某個類作了太多的工做其中一部分工做是以大量的條件表達式完成的
建立一個繼承體系,已一個子類來表達某一種特殊的狀況

class BillingScheme{}

to

class BillingScheme{}
    class BusinessBillingScheme extends BillingScheme{}
    class ResidentialBillingScheme extends BillingScheme{}
    class DisabilityBillingScheme extends BillingScheme{}

動機

  • 一個類實現一個概念演變成實現多個概念
  • 保持單一責任

GitHub地址歡迎Star Fork Follower

相關文章
相關標籤/搜索