2015-09-16 101 views
28

封裝隱藏數據。我想在這裏聽到一些非常有趣的答案。封裝 - 當setter已經公開時,爲什麼我們需要它?

當我們已經爲變量聲明瞭public setter方法時,將變量保留爲private的意義何在。

我理解封裝的使用,但是當我們在爲公衆作出制定者背後是什麼保持變量作爲private點,我們可以直接使用public訪問修飾符

是因爲我們不希望別人知道我們在後端存儲數據或管理數據的確切方式嗎?

+0

而[這裏](http://stackoverflow.com/questions/4739597/getter-with-side-effect)是什麼可以被視爲相反的問題。 –

+0

我擁有數十萬甚至上百萬行代碼的大型系統,無需一個設置器。我喜歡這樣。 –

+1

@ McAdam331是的,正確的是......提問者提到了java,但是它是應用於所有使用getter和setter的語言。這是一個相關的方面..謝謝:) –

回答

30

難道是因爲我們不想讓別人知道我們的確切方式 在後端存儲數據還是管理數據?

是的,這就是問題所在。它與抽象概念信息隱藏也有關係。

您提供一個公共的setter,當由類客戶端調用將有你有記載的效果。客戶的業務如何實際達到這種效果並非客戶的業務。你在修改某個類的屬性嗎?好的,讓客戶知道,但不是你實際上修改變量的事實。將來,您可能希望修改您的課程,以便不使用簡單的備份變量,而是使用完全不同的內容(屬性字典?外部服務?無論如何!),並且客戶端不會中斷。

因此,您的setter是您提供給客戶端以「修改此類屬性」的抽象。同時你隱藏事實上你正在使用一個內部變量,因爲客戶端不需要知道這個事實。

(注:我在這裏用的是「屬性」作爲一個通用的概念,沒有涉及到任何具體的編程語言)

+0

抽象是關於隱藏功能..這個是一些如何隱藏後端機制,我們設置數據的方式..好解釋... :) –

+6

抽象是關於隱藏_implementation_不是功能。 – codemonkey

+0

功能依賴於實現..所以是它回到了相同的東西.. –

2

更多或更少的簡單和現實的例子,我在實踐中遇到的是一個Options類,其中有許多制定者和獲得者。在某些時候,您可能想要添加取決於他人或具有副作用的新選項。甚至用Enum替換一組選項。在這種情況下,setA函數不會修改a字段,但會隱藏一些額外的配置邏輯。同樣,getA不會只返回a的值,但類似於config == cStuffSupportingA

5

雖然Konamimananswer被發現,我想補充的是,在公衆制定者對你的要求直接暴露公共領域的特定情況下,還有另外一個非常重要的區別,從牢記開信息隱藏和解耦從公共表面實施,或API,一類; 驗證

在公共領域的情況下,沒有辦法時,它的修改,以驗證該字段的值。如果是公共二傳手(可能是Foo {get; set;}財產或SetFoo(Foo value))方法,您可以添加驗證碼並啓動所需的副作用,並以此方式確保您的班級始終處於有效或可預測的狀態。

10

因爲通過封裝我們提供單點訪問。假設你定義一個變量及其設置方法如下

String username; 

public void setUsername(String username){ 
this.username = username; 
} 

稍後,您希望在設置用戶名屬性前添加一些驗證。如果您通過直接訪問該屬性將用戶名設置爲10個地方,那麼您沒有單一訪問點,並且需要在10個地方進行此更改。但是如果你有一個setter方法,那麼通過在一個地方做出改變,你可以很容易地獲得結果。

23

我完全Konamiman's answer同意,但我想補充一點:

有情況下,你真的不希望抽象。這很好。

一個簡單的例子,我想在這裏使用的是3維向量浮點類:

class Vector3f { 
public: 
    float x; 
    float y; 
    float z; 
}; 

你能不能讓這些領域的私人和提供setter方法呢?當然可以。但是在這裏你可能會爭辯說,這個類實際上只是提供了一個浮動元組,並且你不需要任何額外的功能。因此,添加setter只會讓班級變得複雜,您寧願將這些字段公開。

現在,您可以輕鬆構建可能會在稍後出現的情況。舉例來說,你也許有一天拿到Vector3f而且不允許存儲NaN S和如果有人試圖這樣做應該拋出一個異常的要求。但是這樣一個假設的未來問題不應該足以證明引入額外的抽象。

這是你的作爲一個程序員來調用,以決定哪些抽象對手頭的問題有意義,哪些只會讓你完成工作。不必要的抽象是over-engineering,並且會影響您的生產力,就像不夠抽象一樣。

底線:不要因爲有人聲稱這是好的做法而盲目地使用setter。相反,考慮手頭的問題並考慮權衡。

+2

好的,這就是我說的,取決於你實際需要什麼。 –

+0

平衡(在抽象和抽象之間)是很困難的。在任何一個方向上錯誤都會損害生產力。 – TripeHound

+0

更相關的情況恕我直言,如果一個Vector提供了一個'SetLocation'函數,那麼矢量的導數可能會在它的位置發生變化時觸發一些動作。例如,一個'MoveReportingVector'可以在其構造函數中接受一個'MotionReporter' [接口]對象,並且每當調用'MoveReportingVector'上的'SetLocation'時調用它的'ReportMotion'方法。我希望Java有一種方法來聲明非final字段是公開可讀的,但是可以在本地修改,以促進這種類型,因爲這樣的語義通常是合適的。 – supercat

3

如果您想在轉讓前進行範圍檢查,該怎麼辦?這是我使用setter和getters的情況之一

2

Wikipedia對[mutator方法(https://en.wikipedia.org/wiki/Mutator_method)有很好的概述,這是setter方法的作用,以及它們如何在不同的語言中工作。

短的版本:如果你想介紹驗證或獲取對象上執行修改其他邏輯它是好的,有一個二傳手把在邏輯還可能要隱藏你如何存儲的東西。所以,那些是獲得者/設定者的原因。類似地,對於獲取者,你可能有一些邏輯提供默認值或取決於對於像區域設置,字符編碼等的配置有很多正當的理由想擁有比獲取或設置實例變量其他邏輯。

顯然,如果你有getter和setteres,你不希望人們通過直接操縱對象狀態來繞過它們,這就是爲什麼你應該保持實例變量爲私有的原因。是否要修改對象狀態線程安全性,例如,是否確實希望您的對象完全可變(如果不是,則使字段最終)。鎖定,同步等。

2

將字段設置爲私有文檔是一個強大的事實:這些專用字段僅在當前類中直接使用。這有助於維護人員不必追蹤現場使用情況。他們可以通過查看類並更好地瞭解代碼,並確定類和環境中這些字段的影響通過公共和受保護的方法調用進行。它限制了課堂上的暴露表面。

反過來,爲私人領域定義一個「二傳手」並不是要再次宣傳它。它是關於宣佈另一個強大的事實:屬於這個類別的對象具有屬性,它可以從外部修改。 (術語對象財產的這個部分,而不是在OOP意義上的全和可觀察到的事實有界部分的意義上使用)

爲什麼然後宣佈「二傳手「在公開領域時就足夠了?因爲聲明一個字段不僅綁定一個名稱到該類的對象屬性,但也承諾使用此屬性的內存存儲。

因此,如果你申報了「私人領域與二傳手」,聲明三件事情:

  • 你宣佈你給現場/二傳手集羣名稱表示對象的財產當物體被視爲黑匣子時,這是令人感興趣的。
  • 您聲明此屬性的值可由對象的環境修改。
  • 您聲明在此特定具體類中,該對象的屬性是通過向其提交一些內存存儲來實現的。

我主張你從未讓你的私人領域與getter和setter亂射。字段用於描述存儲。方法是用於與環境的交互。 (「getters」和「setter」的具體情況是用於描述感興趣的屬性

4

想一想:我想通過一個類來表示一個真實的生活對象,一個獅子。我會做這樣的事情。

class Lion { 
    public int legs; 
} 

現在我的課是其他開發人員需要創建一個對象並設置其腿部字段。他會做這樣的事情

Lion jungleKing = new Lion(); 
jungleKing.legs=15; 

現在的問題是,Java不會限制他設置任何數量超過4作爲該對象的腿數。這不是一個錯誤,它會運行得很好。但這是一個邏輯錯誤,編譯器不會幫助你。這樣獅子可以有任何數量的腿。 但是,如果我們這樣

class Lion { 
    private int legs; 
    public void setLegs(int legs){ 
     if(legs>4) 
      this.legs=4; 
     else this.legs=legs; 
    } 
} 

現在,你不會有任何獅子有超過四條腿,因爲更新類領域的政策已經由類本身定義編寫的代碼,而且也沒有辦法任何不知道策略的人都會更新腿部字段,因爲更新腿部字段的唯一方法是通過setLegs()方法,並且該方法知道類的策略。

+0

很好的答案,但最後一段時間可以使用一些逗號。 –

相關問題