2011-08-11 64 views
3

我目前正在實現迭代求解器,該求解器通過不斷地改進對特定問題的解決方案的估計。由於解決方案是一個相當大的一組數據,因此要進行改進。將可變對象封裝到只讀對象

我已經實現了一個簡單的Observer/Observable模式,以便能夠在迭代發生時觀察算法。具體來說,求解器提供了一種方法

Foo getCurrentSolution() 

它返回解決方案的當前估計值。然後,觀察者可以根據當前的估計自由地進行一些計算(例如:決定解決方案是否足夠好並且可以停止迭代)。 Foo是可變的,但當然,如果觀察者修改當前解的估計值,這可能會破壞求解器的迭代。

因此,getCurrentSolution()應該真的返回一個防禦副本。但是這對於大問題需要時間和內存,所以我想出了另一個想法,即getCurrentSolution()返回一個新的ReadOnlyFoo(bar),其中foo是解決方案專用的(可變的)當前估計的解決方案。這個想法是,ReadOnlyFoo幾乎與Foo具有相同的接口,只有可能修改數據的方法被「停用」(它們會拋出異常)。下面給出了一些虛擬類的所有細節。

我的問題是:這種方法是不是很好的做法?有更好的模式嗎?

謝謝! 塞巴斯蒂安

public abstract class AbstractFoo{ 
    public abstract double getValue(); 

    public abstract void setValue(final double x); 

    public abstract AbstractFoo add(AbstractFoo bar); 

    public void addToSelf(AbstractFoo bar){ 
     setValue(getValue + bar.getValue()); 
    } 
} 

public class Foo extends AbstractFoo{ 
    private double value; 

    public Foo(final double x){ 
     value = x; 
    } 

    public double getValue(){ 
     return value; 
    } 

    public void setValue(final double x){ 
     value = x; 
    } 

    public AbstractFoo add(AbstractFoo bar){ 
     return new Foo(value + bar.getValue()); 
    } 
} 

public final class FooReadOnly extends AbstractFoo{ 
    private final Foo foo; 

    public FooReadOnly(AbstractFoo foo){ 
     this.foo = foo; 
    } 

    public double getValue(){ 
     return foo.getValue(); 
    } 

    public void setValue(final double x){ 
     throw new NotImplementedException("read only object"); 
    } 

    public AbstractFoo add(AbstractFoo bar){ 
     return foo.add(bar); 
    } 

    public void addToSelf(AbstractFoo bar){ 
     throw new NotImplementedException("read only object"); 
    } 
} 

回答

3

我將定義只包含只讀方法的接口Solution,幷包含所有方法的可變類MutableSolution,使getCurrentSolution()方法返回一個Solution實例。這樣,您就不需要創建防禦副本或將您的解決方案包裝到只讀包裝中。

當然,觀察員仍然可以將解決方案投射到MutableSolution,但這不會是一個意外。如果你想保護自己免受強制轉換,然後寫一個ReadOnlySolution包裝類實現Solution並委託給包裝MutableSolution。這與您的提議類似,只是方法的簽名明確指出該對象不可變。

+0

這肯定會更乾淨,但假設我不能更改'解決方案'的實現?我不能讓它從'MutableSolution'繼承...... – Sebastien

+0

然後用getCurrentSolution返回一個只讀方法定義一個接口,並定義一個實現這個接口的類並委託給你的可變對象。 –

+0

我對其他選擇不滿意,所以我想我會選擇這個。非常感謝。 – Sebastien

1

這實際上是該方法的Collections類不與unmodifiableList(...)等它返回一個包含原始列表中,但罰球在修改集合的方法異常的包裝。

+0

我沒有意識到這一點。我會研究它。有人提出這個問題,但如果你添加一個新的mutator到包裝的對象,這種方法已不再安全。 – Sebastien

+0

沒錯。我的解決方案中不存在此問題。 –

1

我不會那樣做。如果使用甚至是一個通用接口(可能存在於您的實際實現中)的AbstractFoo,那麼他不會預先知道當前實例是否可變。所以用戶會冒一些未經檢查的異常被拋出。

而且,對於一個不可改變的對象來說,它是不可修改的,它根本就不是特例。換句話說:我不會使用execption來發信號,那個人試圖修改一個FooReadOnly的實例。

至少我一個抽象方法boolean isModifiable()添加到抽象類AbstractFoo這樣我們就可以測試一下,如果我們可以修改對象。在這種情況下,我們不需要拋出異常 - 修改方法的實現可能無所作爲。

+0

+1表示關於例外的建議。 – Sebastien

+0

我仍然會讓這些方法拋出異常,以便清楚地表明開發人員不尊重合同:如果isModifiable爲false,則不要調用setter方法。不過,我會使用標準的java.lang.UnsupportedOperationException。 –

+0

我從來不喜歡他們在'Iterator'中做的'UnsupportedOperationException'技巧。假設你從某個方法接收到一個'Iterator'的實例,並且想用它來刪除一個元素:爲了瞭解* this * iterator是否支持刪除元素,你必須'try'和'catch'一個未經檢查的異常。並非JRE中的每一行代碼都是傑作,值得複製;) –

0

爲什麼這樣的過度工程解決方案?爲什麼不有一個類和readOnly布爾屬性?然後爲每個設置者執行checkWriteable()。

+0

應該有一個標誌,不僅適用於'readOnly',也適用於'不可變'。他們不*意味着同樣的事情。希望知道並立即使用解決方案的當前狀態的代碼可以滿足對未來將改變的對象的只讀包裝,但是需要對當前狀態進行快照的代碼需要製作防禦副本它被給予的對象*除非已知它是不可變的*。這很糟糕,很多圖書館很少支持對象報告它們是否是不可變的。 – supercat