1. 程式人生 > >第14條:在公有類中使用訪問方法而非公有域

第14條:在公有類中使用訪問方法而非公有域

有時候、可能會編寫一些退化類(degenerate classes),沒有什麼作用,只是用來集中例項域:

1 class Point {
2     public double x;
3     public double y;
4 }

由於這種類的資料是可以直接被訪問的,這些類沒有提供封裝(encapsulation)的功能。如果不改變API,就無法改變它的資料表示法,也無法強加任何約束條件,當域被訪問時,也無法採取任何輔助的行動。對於這種可變的類來說,應該用包含私有域和公有設值方法(setter)的類來代替:

 1 class Point {
 2     private
double 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 public
double 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 }

總之,公有類永遠都不應該暴露在可變的域。雖然還是有問題,但是讓公有類暴露不可變域其危害比較小。但是,有時候會需要包級私有的或者私有的巢狀類來暴露域,無論這個類是可變的還是不可變的。