1. 程式人生 > >Effective Java 3rd 條目16 在公開類使用訪問方法而不是公開域

Effective Java 3rd 條目16 在公開類使用訪問方法而不是公開域

偶然地你可能忍不住編寫一個退化的類,除了把例項域分組以外不作為任何目的:

// 像這樣的退化的類不應該是公開的! 
class Point {
    public double x;
    public double y; 
}

因為這樣的類的資料域可以直接訪問,所以這些類沒有提供封裝的優點(條目15)。沒有改變API你就不能改變這個表示,你不能實現不變性,而且當訪問一個域時你不能採取輔助行動。不妥協的面向物件程式設計師感覺這種類是詛咒,應該總是用這樣的類替代:它有私有域和公開訪問子方法(accessor method)(getter),而且有對於可變類的設定方法(mutator)

(setter):

// 通過訪問子方法和設定方法的資料封裝
class Point {
    private double x; 
    private double y;

    public Point(double x, double y) { 
        this.x = x; this.y = y; 
    }

    public double getX() { return x; } 
    public double getY() { return y; }

    public void setX(double x) { this.x = x; } 
    public
void setY(double y) { this.y = y; } }

當然,當涉及到公開類,這些不妥協者是正確的:如果一個類在它的包外是可訪問的,那麼提供訪問子方法來維持改變類內部表示的靈活性。如果一個公開類暴露了它的資料域,那麼失去了改變它的表示的所有希望,因為客戶端程式碼可以到處分佈。

然而,如果一個類是包私有的或者是私有巢狀類,那麼暴露它的資料域本質上沒有什麼錯誤–假設它們能夠充分描述由類提供的抽象。相對於訪問子方法,這個方法在類宣告中和在使用它的客戶端程式碼中有更少的視覺混亂。雖然客戶端程式碼捆綁於類的內部表示,但是這個程式碼是限制在包含這個類的包中。如果表示中的改變變得迫切,那麼你可以執行這個改變,而沒有觸碰到包外的任何程式碼。對於私有內嵌類這個情形,這個改變的範圍進一步地限制在包含類中。

Java平臺庫中的許多類違反了公開類不應該直接暴露域這個建議。顯著的例子包括java.awt包中的Point和Dimension類。這些類應該看作警世故事,而不是模仿它們。就像在條目67中描述的一樣,暴露Dimension類內部的決定導致了今天仍然困擾我們的嚴重效能問題。

雖然直接暴露公開類的域從來都不是一個好主意,但是當域是不可變時它有較低危害。你不能改變這種類的表示而沒有改變它的API,而且當域是可讀的時候,你不可以採取輔助行動,但是你可以實施不可變性。比如,下面的類保證每個例項代表一個有效的時間:

// 暴露不可變域的公開方法 - 有問題的 
public final class Time {
    private static final int HOURS_PER_DAY = 24; 
    private static final int MINUTES_PER_HOUR = 60;

    public final int hour; 
    public final int minute;

    public Time(int hour, int minute) { 
        if (hour < 0 || hour >= HOURS_PER_DAY) 
            throw new IllegalArgumentException("Hour: " + hour); 
        if (minute < 0 || minute >= MINUTES_PER_HOUR) 
            throw new IllegalArgumentException("Min: " + minute); 
        this.hour = hour; 
        this.minute = minute; 
    } 
    ... // Remainder omitted
}

概括起來,公開類從不應該暴露可變域。儘管仍然有問題,但是公開類暴露不可變域有較低危害。然而,包私有的或者私有的內嵌類暴露不管可變與不可變的域,有時是合適的。