第14條:在公有類中使用訪問方法而非公有域
阿新 • • 發佈:2018-12-28
有時候、可能會編寫一些退化類(degenerate classes),沒有什麼作用,只是用來集中例項域:
1 class Point { 2 public double x; 3 public double y; 4 }
由於這種類的資料是可以直接被訪問的,這些類沒有提供封裝(encapsulation)的功能。如果不改變API,就無法改變它的資料表示法,也無法強加任何約束條件,當域被訪問時,也無法採取任何輔助的行動。對於這種可變的類來說,應該用包含私有域和公有設值方法(setter)的類來代替:
1 class Point { 2 privatedouble x; 3 private double y; 4 public Point(double x, double y) { 5 super(); 6 this.x = x; 7 this.y = y; 8 } 9 public double getX() { 10 return x; 11 } 12 public void setX(double x) { 13 this.x = x; 14 } 15 publicdouble getY() { 16 return y; 17 } 18 public void setY(double y) { 19 this.y = y; 20 } 21 }
毫無疑問,說到公有類的時候,堅持面向物件程式設計思想的看法是正確的:如果類可以在它所在的包的外部進行訪問,就提供訪問方法,以保留將來改變該類的內部表示法的靈活性。如果類暴露了它的資料域,要想將來改變其內部表示法是不可能的,因為公有類的客戶端程式碼已經遍佈各處了。
然而,如果類是包級私有的,或者是私有的巢狀類,直接暴露它的資料域並沒有本質的錯誤。雖然客戶端程式碼與該類的內部表示法緊密相連,但是這些程式碼被限定在包含該類的包中,如果有必要,不改變包之外的任何程式碼而只改變內部資料表示法也是可以的。在私有巢狀類的情況下,改變的作用範圍被進一步限制在外圍類中。
讓公有類直接暴露域從來不是種好的辦法,但是如果域是不可改變的,這種做法的危害就比較小一些。如果不改變類的API,就無法改變這種類的表示法,當域被讀取的時候,你也無法採取任何輔助的行為,但是可以強加約束條件,例如,這種類確保了每個例項都表示一個有效的時間:
1 public final class Time { 2 private static final int HOUR_PER_DAY = 24; 3 private static final int MINUTES_PER_HOUR = 60; 4 5 public final int hour; 6 public final int minute; 7 8 public Time(int hour, int minute) { 9 if(hour < 0 || hour >= HOUR_PER_DAY) 10 throw new IllegalArgumentException("Hour:" + hour); 11 if(minute < 0 || minute >= MINUTES_PER_HOUR) 12 throw new IllegalArgumentException("Min:" + minute); 13 this.hour = hour; 14 this.minute = minute; 15 } 16 }
總之,公有類永遠都不應該暴露在可變的域。雖然還是有問題,但是讓公有類暴露不可變域其危害比較小。但是,有時候會需要包級私有的或者私有的巢狀類來暴露域,無論這個類是可變的還是不可變的。