简体   繁体   English

将可变 object 封装成只读 object

[英]Encapsulating a mutable object into a read-only object

I'm currently implementing iterative solvers, which work by successively improving the estimate the solution to a specific problem.我目前正在实施迭代求解器,它通过不断改进对特定问题的解决方案的估计来工作。 As the solution is a rather large set of data, refinement is carried out in place.由于解决方案是一组相当大的数据,因此进行了适当的细化。

I have implemented a simple Observer/Observable pattern in order to be able to watch the algorithm while the iterations take place.我已经实现了一个简单的 Observer/Observable 模式,以便能够在迭代发生时观察算法。 In particular, the solver provides a method特别是,求解器提供了一种方法

Foo getCurrentSolution()

which returns the current estimate of the solution.它返回解决方案的当前估计。 The observer is then free to do some computations, based on the current estimate (for example: to decide whether or not the solution is good enough and the iterations could be stopped).然后观察者可以根据当前估计自由地进行一些计算(例如:决定解决方案是否足够好以及是否可以停止迭代)。 Foo is mutable, but of course, if the observer modifies the current estimate of the solution, this can ruin the iterations of the solver. Foo是可变的,但是当然,如果观察者修改了解决方案的当前估计,这可能会破坏求解器的迭代。

Therefore, getCurrentSolution() should really return a defensive copy.因此, getCurrentSolution()应该真正返回一个防御性副本。 But this requires time and memory on large problems, so I came up with another idea, which is to have getCurrentSolution() return a new ReadOnlyFoo(bar) , where foo is the (mutable) current estimate of the solution, private to the solver.但这需要时间和 memory 解决大问题,所以我想出了另一个想法,即让getCurrentSolution()返回一个新的ReadOnlyFoo(bar) ,其中foo是解决方案的(可变)当前估计,对求解器私有. The idea is that ReadOnlyFoo has almost the same interface as Foo , only the methods which might modify the data are "deactivated" (they throw an exception).这个想法是ReadOnlyFoo具有与Foo几乎相同的接口,只有可能修改数据的方法被“停用”(它们抛出异常)。 All details of some dummy classes are given below.下面给出了一些虚拟类的所有细节。

My question is: is this approach good practice?我的问题是:这种方法是好的做法吗? Is there a better pattern?有更好的模式吗?

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

I would define an interface Solution containing only the read-only methods, and a mutable class MutableSolution containing all the methods, and make the getCurrentSolution() method return a Solution instance.我将定义一个仅包含只读方法的接口Solution ,以及一个包含所有方法的可变 class MutableSolution ,并使getCurrentSolution()方法返回一个Solution实例。 This way, you don't need to create a defensive copy or to wrap your solution into a read-only wrapper.这样,您无需创建防御性副本或将解决方案包装到只读包装器中。

Of course, the observer could still cast the solution to a MutableSolution , but this wouldn't be an accident.当然,观察者仍然可以将解决方案转换为MutableSolution ,但这不是偶然的。 If you want to protect yourself against casts, then write a ReadOnlySolution wrapper class implementing Solution and delegating to the wrapped MutableSolution .如果您想保护自己免受强制转换,请编写一个ReadOnlySolution包装器 class 实现Solution并委托给包装的MutableSolution This is similar to your proposition, except that the signature of the method makes it clear that the object is not mutable.这与您的提议相似,只是该方法的签名清楚地表明 object 是不可变的。

That's actually the approach that the Collections class does with unmodifiableList(...) etc. It returns a wrapper that contains the original list but throws exceptions in the methods that modify the collection.这实际上是Collections class 对unmodifiableList(...)等执行的方法。它返回一个包含原始列表的包装器,但在修改集合的方法中抛出异常。

I wouldn't do that.我不会那样做的。 If one uses AbstractFoo of even a common interface (which might exist in your real implementation), then he doesn't know in advance, if the current instance is mutable or not.如果一个人甚至使用一个通用接口的AbstractFoo (可能存在于您的实际实现中),那么他不会事先知道当前实例是否是可变的。 So the user will risk some unchecked exceptions to be thrown.因此,用户将冒着抛出一些未经检查的异常的风险。

And, for an immutable object, beeing unmodifiable it's not exceptional at all.而且,对于不可变的 object 而言,不可修改的情况一点也不例外。 In other words: I wouldn't use execption to signal, that one attempted to modify an instance of FooReadOnly .换句话说:我不会使用 execption 来表示有人试图修改FooReadOnly的实例。

At least I'd add an abstract method boolean isModifiable() to the abstract class AbstractFoo so that we can test, if we can modify the object.至少我会在抽象 class AbstractFoo中添加一个抽象方法boolean isModifiable()以便我们可以测试,如果我们可以修改 object。 And in that case, we wouldn't need to throw exceptions - the implementation of the modifying methods could simply do nothing.在这种情况下,我们不需要抛出异常——修改方法的实现可以简单地什么都不做。

Why do such an overengineered solution?为什么要做这样一个过度设计的解决方案? Why not have one class and readOnly boolean attribute?为什么没有一个 class 和只读 boolean 属性? Then do checkWriteable() for each setter.然后为每个 setter 执行 checkWriteable() 。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM