整潔代碼之道 6 對象和數據結構

對象和數據結構在使用場景上有不同的側重點,要做到代碼整潔,需要明確區分兩者,並靈活運用

6.1 數據抽象

  1. 我們習慣性熟知的數據封裝方式:變量都是私有,通過 Getter / Setter 對變量進行取值賦值
  2. 但其實這麼做之後,變量依舊是暴露的,隱藏實現並非只是在變量之前放上一個函數層這麼簡單
  3. 隱藏的關鍵點是 抽象 ,類並不是簡單的通過 Getter / Setter 將變量推向外層
  4. 而是曝露抽象接口,以方便用戶無需瞭解數據的實現就能操作數據本體

6.2 數據、對象的反對稱性

  1. 對象把數據隱藏在抽象之後,曝露操作數據的函數
  2. 數據結構曝露自己的數據,沒有提供有意義的函數
  3. 在如今面向對象盛行的時期,其實我們在決定具體時候什麼方式實現代碼時,並不是只有單一的選擇
  4. 有時候根據場景需要,使用數據結構做一些過程式的操作也是可行的

6.2.1 過程式代碼

  1. 就是使用數據結構的代碼,可以再不改動既有數據結構的前提下添加新函數
  2. 難以添加新數據結構,因爲每次添加都需要所有所有調用的函數結構
public class Square {
  public Point topLeft;
  public double side;
}

public class Rectangle {
  public Point topLeft;
  public double height;
  public double width;
}

public class Circle {
  public Point center;
  public double radius;
}

public class Geometry {
  public final double PI = 3.1415926;

  public double area(Object shape) throws NoSuchShapeException {
    if (shape instanceOf Square) {
      Square square = (Square) shape;
      return square.side * square.side;
    } else if (shape instanceof Rectangle) {
      Rectangle rectangle = (Rectangle) shape;
      return rectangle.height * rectangle.width;
    } else if (shape instanceof Circle) {
      Circle circle = (Circle) shape;
      return PI * cicle.radius * circle.radius;
    }

    throw new NoSuchShapeException();
  }
}

6.2.2 面向對象代碼

  1. 可以在不改動既有函數的前提下添加新類
  2. 難以添加新函數,因爲每次添加都需要修改所有實現的類
public class Square implements Shape {
  private Point topLeft;
  private double side;

  public double area() {
    return side * side;
  }
}

public class Rectangle implements Shape {
  private Point topLeft;
  private double height;
  private double width;

  public double area() {
    return height * width;
  }
}

public class Circle implements Shape {
  private Point center;
  private double radius;
  private final double PI = 3.1415926;

  public double area() {
    return PI * raduis * raduis;
  }
}

6.3 得墨忒耳率( The Law of Demeter )

  1. 核心觀念:模塊不應該瞭解它所操作對象的內部情形

6.3.1 火車失事

  1. ctxt.getOptions().getScratchDir().getAbsolutePath() 這種鏈式語句就像是一列火車
  2. 這句代碼顯然是違反了得墨忒耳率,但事實告訴我們,由於使用場景的限制,沒有絕對的規則
  3. 例如這串代碼優化成以下格式,依舊違反了得墨忒耳率,但在語義理解上要直觀的多
Options options = ctxt.getOptions();
File scratchDir = options.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();

6.3.2 混雜

  1. 在一個類中,一半是對象,一半是數據結構,導致這個類即擁有 執行操作的函數 ,也擁有 公共變量或 Getter / Setter 函數

6.3.3 隱藏結構

  1. 對上述那串代碼追根溯源找到最終使用的位置,並將整個邏輯進行封裝,直接返回最後結果
  2. 例如 BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName) ,就比較好的隱藏了內部實現

6.4 數據傳送對象

  1. 最爲精煉的數據結構,是一個只有公共變量、沒有函數的類
  2. 這種類通常被稱爲 數據傳送對象 DTO( Data Transfer Objects )
  3. 但現在 DTO 的使用場景更像是一個 Bean ,擁有私有變量的同時,也擁有共有的 Getter / Setter 函數

6.5 小結

  1. 對象曝露行爲,隱藏數據,便於添加新對象類型而不需要修改既有行爲
  2. 但對象難以在既有對象中添加新行爲
  3. 數據結構曝露數據,沒有明顯的行爲,便於向既有數據結構添加新行爲
  4. 但數據結構難以向既有函數添加新數據結構
發佈了419 篇原創文章 · 獲贊 50 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章