oop - 為什麼使用getters 和setters?

  显示原文与译文双语对照的内容

于那些variables,相關使用getter和 setter - 那隻獲取和設置的優點是什麼- 而不是簡單地使用公共欄位?

如果getter和setter的作用不僅僅是簡單的獲取/設置,我可以很快地計算出這一點,但我不是 100%清楚:

 
public String foo;

 

比以下情況更差:


private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

前者需要較少的樣板代碼。


在視點的javaweb dev, 編譯列表中上移對我在這裡似乎地頂部的獲獎者, :

  1. 當你意識到你需要做的不僅僅是設置和獲取值,你也不必改變代碼庫中的每個文件。
  2. 你可以在這裡執行驗證。
  3. 你可以更改正在設置的值。
  4. 你可以隱藏內部表示。 getAddress() 實際上可以為你獲取幾個欄位。
  5. 你已經將公共介面與工作表中的更改隔離。
  6. 一些庫需要這個。反射,序列化,mock 對象。
  7. 繼承此類,你可以重寫默認功能。
  8. 你可以為getter和setter具有不同的訪問級別。
  9. 延遲載入。
  10. 人們很容易告訴你沒有使用 python 。
时间:

公共欄位不比只返回欄位並分配給它的getter/setter對更糟糕。 首先,很明顯( 在大多數語言中) 沒有功能差異。 任何差異都必須在其他因素中,比如可維護性或者可讀性。

getter/setter對的oft-mentioned優勢,不是。 有一個聲明,你可以更改實現,你的客戶端不必重新編譯。 假定,setter允許你稍後添加類似驗證的功能,你的客戶端甚至不需要知道它。 但是,添加相應的驗證來一個設置器是一個更改到它的前提條件,上面的合同,這就是的衝突,簡單來講,"你可以在這裡放入任何東西,以後你可以從getter獲取同樣的東西"。

現在,你打破了契約,改變代碼庫中的每個文件是你應該做的,而不是避免。 如果你避免了它,你就會假設所有代碼假定這些方法的契約是不同的。

如果不應該是該協議,那麼介面允許客戶端將對象置於無效狀態。 真是的相反的封裝如果該欄位可以不是真的被設置為開始,而不是驗證怎麼會在那裡從一開始有線索?

同樣的參數也適用於這些 pass-through getter/setter對的其他假設: 如果以後決定更改設置的值,則會破壞合同。 如果你在派生類中重寫默認功能,而不是幾個無害的修改,那麼你將破壞基類的契約。 這違背了Liskov可以替代性原則,它被認為是面向對象的原則之一。

如果一個類包含了每個欄位的這些啞的getter和 setter,那麼它是一個沒有任何變數的類,沒有契約 。 那真的是object-oriented設計? 如果所有的類都是那些getter和 setter,它只是一個愚蠢的數據持有者,而啞數據持有者應該像啞數據持有者:


class Foo {
public:
 int DaysLeft;
 int ContestantNumber;
};

將 pass-through getter/setter對添加到此類中不會添加任何值。 其他類應該提供有意義的操作,而不僅僅是欄位已經提供的操作。 這就是你如何定義和維護有用的不變數。

的客戶:"我該如何處理此類的對象"?
設計器:"你可以讀寫多個變數。"
客戶:"哦。。酷,我猜"?

不存在有原因要使用getter和 setter,但是如果這些原因,使得獲取方法/設置方法偶的名義假封裝神,不是件好事。 使getter或者setter包含的有效原因包括經常提到的可能的更改,比如驗證或者不同的內部表示。 或者,這個值應該是客戶端可讀的,但不是可以寫的( 例如讀取字典的大小),所以一個簡單的getter是一個不錯的選擇。 但是當你做出選擇時,這些理由應該存在,而不僅僅是你以後可能想要的東西。 這是 YAGNI ( ,你不需要 )的一個實例。

很多人談論了getter和setter的優點,但我想扮演魔鬼的角色。 現在我正在調試一個非常大的程序,程序員決定讓所有的東西都成為getter和 setter 。 這看起來不錯,但它是一個reverse-engineering的噩夢。

假設你正在查看數百行代碼,你遇到了以下問題:

 
person.name ="Joe";

 

這是一個非常簡單的代碼,直到你意識到它是一個 setter 。 現在,你跟蹤一個給定器和發現它還設置人。firstname 。人。姓氏。人。ishuman,人。hasreallycommonfirstname,並調用 person.update(), 中間,這樣查詢出來到資料庫以及 等等 哦,就是在那裡你的內存泄漏出現可能。

首先理解局部代碼是很重要的一個重要屬性,getter和setter往往會破壞。 這就是為什麼我盡量避免它們,當我使用它們時最小化它們。

有很多原因。我最喜歡的是當你需要改變行為或者調整你可以設置的變數時。 例如假設你有一個 setSpeed(int speed) 方法。 但你希望只能設置 100的最大速度。 你將執行以下操作:


public void setSpeed(int speed) {
 if ( speed> 100 ) {
 this.speed = 100;
 } else {
 this.speed = speed;
 }
}

如果你的代碼中的任何地方都是公共欄位,然後你意識到你需要上面的需求? 查找公共欄位的每個用法,而不是僅僅修改你的setter 。

我的2美分:)

訪問器和轉換器的一個優點是你可以執行驗證。

例如如果 foo 是公共的,我可以輕鬆地將它設置為 null,然後其他人可以嘗試調用對象上的方法。 但它已經不存在了 ! 使用 setFoo 方法,我可以確保 foo 從未被設置為 null

accessor方法和mutator方法也允許使用封裝的值一旦它的設置- 如果你不應看到( 也許它在構造函數中被設置,然後被方法使用,但是不應該被改變) 上,它將永遠不會被看到了出來。 但是如果允許其他類查看或者更改它,你可以提供適當的訪問器和/或者賦值器。

取決於你的語言。你已經標記了這個"object-oriented"而不是"爪哇",所以我想指出chssply76的答案是 language-dependent 。 例如在 python 中,沒有理由使用getter和 setter 。 如果你需要更改行為,你可以使用一個屬性,它在基本屬性訪問周圍包裝一個getter和 setter 。 像這樣:


class Simple(object):
 def _get_value(self):
 return self._value -1

 def _set_value(self, new_value):
 self._value = new_value + 1

 def _del_value(self):
 self.old_values.append(self._value)
 del self._value

 value = property(_get_value, _set_value, _del_value)

...