2009-10-20 36 views
2

在新的Java項目中,我嘗試儘可能多地應用最佳實踐。我遇到的問題是不變性。雖然我理解了這個概念,並且已經建立了一些不可變類,但現在我來到了一個類,我認爲將它作爲一個可變類是更合適的。如何限制對可變或不可變方法的訪問?

主要問題是我想在某些情況下隱藏類的可變部分,以便在我的情況下,MVC的視圖層不能直接修改對象,但必須通過其控制器。

我認爲有2種方法做到這一點:

  1. 創建界面「東西」,它有它的所有不可變的方法(只讀),並創建一個接口「MutableThing」它有setter方法。

  2. 將所有方法放在一個接口中,並通過包裝對象,如Collections.unmodifiableList(obj)方法來限制對變異方法的訪問,以便在訪問變異方法時拋出異常。

我更喜歡第一個,因爲我認爲它更乾淨,設計更好,但我有一個問題。我在「Thing」接口中添加了addListener(l)和removeListener(l),以便視圖層可以將自己註冊爲某些模型對象的偵聽器。但在「Thing」界面中使用這兩種方法時,它自己就不再有意義了。爲什麼如果沒有辦法實際改變數據,一個接口就能夠註冊監聽器來發出數據變化的信號?我可以將這些方法放在「MutableThing」接口中,但視圖層只能訪問「Thing」接口,並且不能將自己註冊爲偵聽器。

這不工作的原因僅僅是因爲聽衆,視圖層實際上負責將自己註冊爲模型上的監聽器嗎?如果控制器可以以某種方式做到這一點(可以訪問「MutableThing」),那麼就不會有問題,但我不知道如何實現它。

你會建議什麼?

謝謝!

回答

0

在我看來,你提到的兩種可能性都是解決問題的有效方法。關於第一種方法:我假設你的意思是接口MutableThing擴展接口Thing以添加增變器方法。

關於聽衆的東西:有幾種方法可以做到這一點。你可以將它與ThingMutableThing接口分開,例如通過使用Java的java.util.Observable類和java.util.Observer接口(或者你自己編寫的類似的東西 - 這些類不是類型安全的,因爲它們來自泛型被添加到Java之前)。

+0

通過將接口與接口分隔開來是什麼意思?這看起來如何? – letmaik 2009-10-20 15:20:05

+0

我的意思是說,你不要在'Thing'或'MutableThing'接口中放置'addListener'和'removeListener'方法,但是你需要爲監聽器功能使用一個單獨的接口(例如'Observer' /'Observable') 。 – Jesper 2009-10-20 19:42:47

3

爲什麼接口能夠註冊偵聽器,哪些信號數據發生變化,如果沒有方法實際更改數據?

所有接口狀態都是您可以檢索下列值。它並不意味着對象是不可變的。當對象是可變的時,IMO是一個好主意,確保每個人都知道它,因爲不可變對象具有對象可能沒有的一些屬性(線程安全,可以安全地緩存結果等)。你的對象是可變的。你不應該假裝它是不可變的。因此,與1一起,並添加/刪除Thing接口。

+0

我正要提到這一點,但你已經這樣做了。這個問題本身不是答案,否則+1 – ptomli 2009-10-20 11:23:05

+0

+1 - 很顯然,OP將「不可變」與「可變但不使用此API」混爲一談。瞭解這種混亂將有助於OP找出一致的API設計。 – 2009-10-20 12:10:25

+0

感謝您的回答和評論!好吧,我明白你的意思是混淆兩個術語,那麼將接口命名爲mutable *或immutable *通常是個不錯的主意,因爲它應該屬於它的實現?我想是這樣。然而,如何處理不應該在視圖層中暴露的setter?留在MutableThing中? – letmaik 2009-10-20 15:11:32

1

我會去修改第一次嘗試。

你可以通過一個包含所有getter的接口來定義你的不可變。一個可變類實現了Immutable接口,幷包含所有必需的set方法,而不需要接口指定這些方法。在你的代碼中,你只能通過Immutable接口訪問實例。 您的代碼中實際需要創建或修改類的地方,您(專門)通過簡單地轉換或通過將這些修改實現與可變類放在同一個包中使其可見來引用這些可變類。像osgi/eclipse這樣的捆綁概念在這裏可能會有所幫助,因爲您在隱藏實現等方面找到了支持。

另外,您可以在創建(並修改它們)後使用seal()方法來密封實例。無論何時調用set-method,都會檢查該實例是否未被密封。這可能有助於防止修改(或在大型團隊中工作時)。

+0

其實我也想過這個,但是不太確定這是不是很棒的OO設計?好吧,如果所有這一切都發生在一個捆綁包內,那麼如果其他人(另一個捆綁包或其他)想要設置一些價值。它不能投射,因爲它不知道實施。我在做什麼:不應該setter也在接口?如果不是,在哪些情況下不? – letmaik 2009-10-20 15:14:58

+0

嘿嘿,我以爲你說了一些關於不可變的東西?嚴重:如果你打算讓這些類變爲可變的,那麼面向* set *的接口在這裏是有意義的。但是,再次:你可能會放棄整個「獲得」 - /「設置」 - 接口遊行,並尋求密封模式,以避免以後的變化。 – pimpf0r 2009-10-21 06:00:08

+0

我認爲在我的情況下,setter我強烈地屬於業務邏輯,必須通過接口由其他人訪問。我不能使用印章,因爲它實際上應該總是可以修改的。在我第一次嘗試之後,我仍然會這樣做,它似乎工作得很好。如果我遇到嚴重的麻煩,我會告訴你:P – letmaik 2009-10-21 10:48:57

1

正確的方法通常有三個類和/或接口:ReadableFoo,ImmutableFoo和MutableFoo。後兩者獨立於第一個派生,這意味着可以在需要ReadableFoo時使用,但在需要ImmutableFoo時只能使用ImmutableFoo,而在MutableFoo中也是如此。 ReadableFoo包含用於CloneAsMutable,AsMutable和AsImmutable的方法可能會有所幫助。 CloneAsMutable將始終創建一個新的可變對象,其中包含從原始複製的屬性。在ReadableFoo上調用AsMutable和AsImmutable將返回原始對象(如果它是所需的類型)或從原始數據複製數據的新對象(如果它不是)。