将可变对象封装到只读对象中

时间:2011-08-11 10:51:14

标签: java immutability defensive-copy

我目前正在实施迭代求解器,它通过连续改进对特定问题的解决方案的估计来工作。由于解决方案是一组相当大的数据,因此可以进行细化。

我已经实现了一个简单的Observer / Observable模式,以便能够在迭代发生时观察算法。特别是,求解器提供了一种方法

Foo getCurrentSolution()

返回解决方案的当前估计值。然后,观察者可以根据当前估计自由地进行一些计算(例如:确定解决方案是否足够好并且可以停止迭代)。 Foo是可变的,但当然,如果观察者修改了解的当前估计值,这可能会破坏求解器的迭代。

因此,getCurrentSolution()应该真正返回防御性副本。但这需要大量问题的时间和记忆,所以我提出了另一个想法,即让getCurrentSolution()返回一个新的ReadOnlyFoo(bar),其中foo是(可变的)当前估计值解决方案,对求解器是私有的。我们的想法是ReadOnlyFoo具有与Foo几乎相同的接口,只有可能修改数据的方法被“停用”(它们抛出异常)。下面给出了一些虚拟类的所有细节。

我的问题是:这种方法是好的做法吗?有更好的模式吗?

谢谢! 的Sebastien

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");
    }
}

4 个答案:

答案 0 :(得分:3)

我将定义一个仅包含只读方法的接口Solution和一个包含所有方法的可变类MutableSolution,并使getCurrentSolution()方法返回{{1}实例。这样,您无需创建防御性副本或将解决方案包装到只读包装器中。

当然,观察者仍然可以将解决方案转换为Solution,但这不是偶然的。如果你想保护自己免受强制转换,那么写一个实现MutableSolution的{​​{1}}包装器类并委托给包裹的ReadOnlySolution。这类似于你的命题,除了方法的签名清楚表明对象不可变。

答案 1 :(得分:1)

这实际上是Collections类对unmodifiableList(...)等做的方法。它返回一个包含原始列表的包装器,但在修改集合的方法中抛出异常。

答案 2 :(得分:1)

我不会这样做。如果使用AbstractFoo甚至一个公共接口(可能存在于您的实际实现中),那么他事先并不知道当前实例是否可变。因此,用户将冒一些未经检查的异常的风险。

而且,对于一个不可改变的对象,它是不可修改的,它根本不是特例。换句话说:我不会使用execption来发信号,那个人试图修改FooReadOnly的实例。

至少我将抽象方法boolean isModifiable()添加到抽象类AbstractFoo,以便我们可以测试,如果我们可以修改对象。在这种情况下,我们不需要抛出异常 - 修改方法的实现可能什么也不做。

答案 3 :(得分:0)

为什么这种过度设计的解决方案?为什么没有一个类和readOnly布尔属性?然后为每个setter执行checkWriteable()。