iT邦幫忙

0

[讀書會]設計重構-層次結構

  • 分享至 

  • xImage
  •  
  • 本章內容
    • 型別層次(is-a)進行重構
    • 原則
      • 分類:
        • 找出共通性、差異性,越接近根部,越通用,越接近底部,越具體。
      • 合併:
        • 分類後組成超型別(共通性)、子型別(差異性)。
      • 可替換性:
        • 里氏替換:超型別可以替換成子型別物件。
      • 路徑:
        • 超型別=>子型別之間,不要產生沒有意義的中間層。
      • 順序 :
        • 依賴順序原則=>子類別依賴超型別(O)、超型別依賴子型別(X)
  • 1.分類:
    • 使用IF-ELSE 而不使用物件導向的多型(6-1全)
      • 解決方式:使用多型
    • 行為一樣的分層:只有當子類別有比基底類別更特殊的行為時,分類才有意義(6-2全)
      • 解決方式: 考慮使用Enum或組合
    • 6-2案例研究:JAVA基本型別不能支援泛型參數
      以下是我的一個泛型類別:
public class Box<T> {
    private T value;
 
    public Box(T value) {
        this.value = value;
    }
 
    public T getValue() {
        return value;
    }
 
    public void setValue(T value) {
        this.value = value;
    }
}
  // 其他的方法...

在Java當中,無法使用Box,需要使用Box,這將導致,當類別需要使用到int特性的方法時,需要用以下方式寫:

public class IntBox {
    private int value;
 
    public IntBox(int value) {
        this.value = value;
    }
 
    public int getValue() {
        return value;
    }
 
    public void setValue(int value) {
        this.value = value;
    }
 
    // 其他的方法...(int或Integer都可以處理的方法... + 你可能希望特定於int的方法...)
}

這樣寫的壞處在於會產生重複的程式碼(int或Integer都可以處理的方法...),並且如果我要換成實作Long的基本型別類別,又要再寫重複的程式碼。此時可以用組合的方式解決:

//用組合解決
public class IntBox {
    
    // int或Integer都可以處理的方法
    private Box<Integer> innerBox;
 
    public IntBox(int value) {
        this.innerBox = new Box<>(value);
    }
 
    public int getValue() {
        return innerBox.getValue();
    }
 
    public void setValue(int value) {
        innerBox.setValue(value);
    }
 
  // 你可能希望特定 於int的方法...

如此一來,我只需要實作特定於int才能使用的方法就可以了,剩餘的部分透過操作Box innerBox去處理。

  • 2.合併:
    • 重複(6-3全):
      • 子類別有重複的地方,並沒有移到超型別
      • Java泛型問題
      • 重複的原因僅是因為定義上的差異
        • 解決方式:向上移動 刪除子型別的方法
        • 注意:其他子型別是否通用 否則會造成叛逆
    • 過寬(6-4全):
      • 子型別過多,中間層不足
        • 解決方式:新增中間層,提取子型別重複向上
          -憑空想像
        • 解決方式:刪除過度設計的中間層
  • 3.過深:
    • 子類別層數過高 原因是因為過度想重用
    • 解決方式:刪除中間層
    • 應該也可以用策略模式/組合處理掉
    • 補充: marker interface
      Marker Interface 是不包含任何方法的接口。它的主要目的是對某個類別提供一種元數據(metadata)的標記或標識。它告訴編譯器或JVM該類具有某種特性或行為,但該接口本身不定義任何行為。

Serializable:
- 任何想要被序列化的類都應該實現Serializable接口。這個接口本身並不定義任何方法,但當一個類實現了它,JVM就知道它可以被序列化。
- 已經逐漸式微,目前透過註解+實作方法就可以了。

import java.io.Serializable;
 
public class Person implements Serializable {
    private String name;
    private int age;
    
    // getters, setters, constructors, etc.
}


Cloneable: 如果一個類想要使用Object的clone方法複製其對象,那麼該類必須實現Cloneable接口。這是一個提示性的接口,它告訴JVM可以安全地執行某些複製操作(但還是需要自己實作複製方法,並不是實作了介面空方法就沒事了)。

public class Book implements Cloneable {
    private String title;
    private String author;
    
    // Assume there are necessary methods and constructors
 
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

4.叛逆

  • 拒絕超型別提供的方法 違反里氏替換
  • 看到一個類別有想用的方法,以及不想使用的方法,選擇繼承並把不想使用的方法拒絕(複寫使其無用處)
  • 解決方式:定期重構 由超型別向下移
  • 移除方法
  • 組合/策略模式

5.支離破碎

  • 因為某些原因 亂用繼承
  • 白玫瑰是一種玫瑰(O),玫瑰是一種白玫瑰(X)
  • 解決方式:組合 策略模式 繼承關係顛倒
  • 轉接器模式
// 既有的印表機
public class OldPrinter
{
    public void PrintPlainText(string text)
    {
        Console.WriteLine(text);
    }
}

// 目標接口
public interface IRichTextPrinter
{
    void PrintRichText(string richText);
}

// 轉接器
public class PrinterAdapter : IRichTextPrinter
{
    private OldPrinter _oldPrinter;
    public PrinterAdapter(OldPrinter oldPrinter)
    {
        _oldPrinter = oldPrinter;
    }

    public void PrintRichText(string richText)
    {
        // 這邊假設轉換富文本為純文字的處理,實際上可能會更複雜。
        string plainText = ConvertRichTextToPlainText(richText);
        _oldPrinter.PrintPlainText(plainText);
    }

    private string ConvertRichTextToPlainText(string richText)
    {
        // 轉換邏輯...
        return richText; // 假設返回值為純文字格式
    }
}

// 使用
var oldPrinter = new OldPrinter();
var adapter = new PrinterAdapter(oldPrinter);
adapter.PrintRichText("<b>轉接器</b>模式");

6.路徑

  • 未注意到既有的繼承路徑,導致子型別間接繼承到中間層與超型別
  • 解決方式:刪除繼承超型別 或中間層

7.循環

  • 超類別包含子型別物件
  • A擁有 B、B 擁有 C 、所以A擁有C、C是繼承A的方法
  • 解決方式:狀態模式 策略模式 職責區分
    • 小算盤:
    • 假設一開始不是用狀態模式想的:
    • 我有一個小算盤
    • 其他不同情境下的小算盤繼承我這個小算盤
    • 我的小算盤必須包含所有不同情境 去做操控

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言