對象和數據結構在使用場景上有不同的側重點,要做到代碼整潔,需要明確區分兩者,並靈活運用
6.1 數據抽象
- 我們習慣性熟知的數據封裝方式:變量都是私有,通過 Getter / Setter 對變量進行取值賦值
- 但其實這麼做之後,變量依舊是暴露的,隱藏實現並非只是在變量之前放上一個函數層這麼簡單
- 隱藏的關鍵點是 抽象 ,類並不是簡單的通過 Getter / Setter 將變量推向外層
- 而是曝露抽象接口,以方便用戶無需瞭解數據的實現就能操作數據本體
6.2 數據、對象的反對稱性
- 對象把數據隱藏在抽象之後,曝露操作數據的函數
- 數據結構曝露自己的數據,沒有提供有意義的函數
- 在如今面向對象盛行的時期,其實我們在決定具體時候什麼方式實現代碼時,並不是只有單一的選擇
- 有時候根據場景需要,使用數據結構做一些過程式的操作也是可行的
6.2.1 過程式代碼
- 就是使用數據結構的代碼,可以再不改動既有數據結構的前提下添加新函數
- 難以添加新數據結構,因爲每次添加都需要所有所有調用的函數結構
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 面向對象代碼
- 可以在不改動既有函數的前提下添加新類
- 難以添加新函數,因爲每次添加都需要修改所有實現的類
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 )
- 核心觀念:模塊不應該瞭解它所操作對象的內部情形
6.3.1 火車失事
ctxt.getOptions().getScratchDir().getAbsolutePath()
這種鏈式語句就像是一列火車- 這句代碼顯然是違反了得墨忒耳率,但事實告訴我們,由於使用場景的限制,沒有絕對的規則
- 例如這串代碼優化成以下格式,依舊違反了得墨忒耳率,但在語義理解上要直觀的多
Options options = ctxt.getOptions();
File scratchDir = options.getScratchDir();
final String outputDir = scratchDir.getAbsolutePath();
6.3.2 混雜
- 在一個類中,一半是對象,一半是數據結構,導致這個類即擁有 執行操作的函數 ,也擁有 公共變量或 Getter / Setter 函數
6.3.3 隱藏結構
- 對上述那串代碼追根溯源找到最終使用的位置,並將整個邏輯進行封裝,直接返回最後結果
- 例如
BufferedOutputStream bos = ctxt.createScratchFileStream(classFileName)
,就比較好的隱藏了內部實現
6.4 數據傳送對象
- 最爲精煉的數據結構,是一個只有公共變量、沒有函數的類
- 這種類通常被稱爲 數據傳送對象 DTO( Data Transfer Objects )
- 但現在 DTO 的使用場景更像是一個 Bean ,擁有私有變量的同時,也擁有共有的 Getter / Setter 函數
6.5 小結
- 對象曝露行爲,隱藏數據,便於添加新對象類型而不需要修改既有行爲
- 但對象難以在既有對象中添加新行爲
- 數據結構曝露數據,沒有明顯的行爲,便於向既有數據結構添加新行爲
- 但數據結構難以向既有函數添加新數據結構