築夢角落

致力於用最生活化的例子讓所有人都能懂程式,也喜歡分享動漫、小說心得,以及自己的所見所聞、所思所想。

Java一定要用getter/setter嗎?

最後更新時間 : 2023-11-01 | viewed : 161

相信許多寫過 Java 的人,心中或多或少都有一個疑問,Java 的 getter / setter 真的有這樣寫的必要嗎?

今天就讓我們來探討一下這個問題。

 

目次

 

前言


getter / setter 只是一種慣例嗎?甚至可以說是 Java 的陋習嗎?

把只需要簡單存取的成員變數宣告成 public 會破壞封裝嗎?那麼,為 private 成員提供 public getter / setter不會破壞封裝嗎

使用 getter / setter預留未來的可擴充性嗎?如果我確定完全不需要擴充,或是我有別的方法擴充呢?

慣例、避免破壞封裝、預留可擴充性,應該都是主張使用 getter / setter 的常見理由,但我認為 Java getter / setter 存在的意義應該從別的層面探討

本文內容純屬個人淺見,並非是公認的標準答案,僅供參考。

 

誰適合閱讀


不限,但有學習過或使用過物件導向程式語言(OOP)的人可能會比較感興趣。

 

getter / setter 是必要的嗎?


如果我有一個這樣的 Class:

public class Dog {

   private String name;
    
   public Dog(String name) {
      this.name = name;
   }
    
   public String getName() {
      return this.name;
   }
    
   public void setName(String name) {
      this.name = name;
   }
}

Dog 的成員 name 宣告成 private,但同時也提供兩個public 方法讓外部存取 name

從邏輯上來說,這無異於直接將 name 宣告成 public,讓外部能夠直接存取 name

既然如此,特地把 name 宣告成 private,再提供兩個 public 方法讓外部去存取它,有什麼意義嗎?

我認為還是有,下面我會說明我的理由。

 

getter / setter 的設計哲學


物件導向程式語言裡,一個 Class 的成員變數(instance variables)意味著這個 Class 的實例(Instance)能具備什麼狀態,比如名字、年齡、性別。

而 Class 的方法(method)則透露這個 Class 具備什麼能力,比如走、跑、吃。

當我們把 Class Dog 的成員變數 name 宣告成 private,代表我們想表達這個變數屬於 Dog 的內部狀態,如果外部想要存取它的值,應該使用 Dog 提供的方法來操作。

所以 getName()setName() 就是在讓外部知道,Dog 可以告訴你他的名字,也允許你修改他的名字。

但如果我們今天把 Class Dog 改寫成這樣:

public class Dog {

   public String name;

   public Dog(String name) {
      this.name = name;
   }
}

這個 Class 就不具備任何能力,雖然他確實存在一個成員變數,而且宣告成 public,允許外部存取他的值,可是卻無法表達 name 的用法

設計這個 Class 的人,真的是希望我們去存取 name 嗎?還是有其他目的?

如果沒有 method,這種猜疑就可能發生,尤其是對於那些只要稍微動一行程式碼就可能釀成悲劇的大型專案,想必會變得更加寸步難行。

這時候如果加上 getter / setter,就能明確表達意圖。

這個行為看起來像多此一舉,是因為我們從邏輯的層面去看待這個問題。

如果我們換個維度,從設計哲學的角度去思考,就可能得出 getter / setter 有一定的合理性這樣不同的答案。

 

方便追蹤程式碼


即使不從哲學的角度來看 getter / setter,單從追蹤程式碼的角度來說,getter / setter 也存在一定的方便性。

如果我們有使用 getter / setter,我們就可以很輕易在一個大型專案裡,找到程式在什麼地方進行了賦值取值,這可以提高程式碼的可維護性。

如果只是單純把 Class 的成員變數宣告成 public,那就無法簡單做到這件事。

一個變數可能在 100 個地方被引用,其中只有 1 個地方是賦值,其他全部是取值。

如果我們現在只關心它的值是在哪裡被修改的,要想在 100 個搜尋結果中,找到唯一一個賦值的地方,勢必比較麻煩。

如果有 setter,馬上就可以找到了

在修改一個大型專案時,確認有哪些地方會賦值、哪些地方會取值,是確保程式運作能符合預期、甚至是保證多執行緒安全(thread-safe)常常需要做的工作之一

小提示:搜尋一個方法或一個變數在哪裡被引用,已經是大部份IDE的必備功能,比如 VSCode 可以對變數按F12來搜尋,在 Eclipse 可以用 Ctrl + Shift + G。

 

結語


一項設計不會無緣無故就是合理的,每個技術的出現都是為了回應某個問題,如果我們不嘗試擴大思考格局、不去想像某種設計是在試圖解決什麼問題,就可能讓一項有用的設計看起來一點都不合邏輯。

然而,合理並不表示是最好的,如果我們能認同一個設計理念,卻不滿意具體作法,就表示重新設計的時候到了,重複造輪子並不會沒有意義。

「認知」會改變一個人的行為,包括如何設計程式。

我們所寫下的每一行程式碼、規劃的程式模塊或程式架構,都透露著我們如何認知程式設計、如何認知我們所面對的問題的本質

擁有不同認知的人,在面對同一個問題時,會採取完全不同的決策。

也許這些決策在邏輯上都是對的,但在哲學上卻可能有合理性的高低之分,只有當時間拉長了,或是問題的範疇某天忽然改變了,才能發現原本的設計有什麼優缺點。

程式設計不是完全的邏輯問題,如果我們在訓練邏輯思維的同時,也能留意哲學思維的重要性,長遠來看一定會帶給我們正面的影響。

 

希望有帶給大家一些不一樣的想法~

 

 
我要留言!
 

X
暱稱(選填)
email(選填,僅站長可見。)
留言 To:#