[英]Design of immutable and mutable objects in Java
我的问题涉及API设计。
假设我正在设计一个矢量(数学/物理意义)。 我想要一个不可变的实现和一个可变的实现。
然后我的矢量看起来像这样:
public interface Vector {
public float getX(); public float getY();
public X add(Vector v);
public X subtract(Vector v);
public X multiply(Vector v);
public float length();
}
我想知道如何确保同时具有可变和不可变的实现。 我真的不喜欢java.util.List的方法(默认允许可变性)和Guava的不可变实现所具有的UnsupportedOperationException()。
如何使用这两种实现设计“完美”接口或抽象类Vector?
我想过这样的事情:
public interface Vector {
...
public Vector add(Vector v);
...
}
public final class ImmutableVector implements Vector {
...
public ImmutableVector add(Vector v) {
return new ImmutableVector(this.x+v.getX(), this.y+v.getY());
}
...
}
public class MutableVector implements Vector {
...
public MutableVector add(Vector v) {
this.x += v.getX();
this.y += v.getY();
return this;
}
...
}
总而言之,我想检查这种方法是否存在明显的设计缺陷,这些缺陷是什么,我应该怎么做才能解决这些问题?
注意:“向量”的东西是一个更通用的用例的例子。 为了我的问题,我可以选择重写List接口或其他任何东西。 请关注更一般的用例。
最终选择,在下面的答案之后,基于Joda-time,如有人解释但现在编辑:
/** Basic class, allowing read-only access. */
public abstract class ReadableVector {
public abstract float getX(); public abstract float getY();
public final float length() {
return Vectors.length(this);
}
// equals(Object), toString(), hashCode(), toImmutableVectors(), mutableCopy()
}
/** ImmutableVector, not modifiable implementation */
public final class ImmutableVector extends ReadableVector implements Serializable {
// getters
// guava-like builder methods (copyOf, of, etc.)
}
/** Mutable implementation */
public class Vector extends ReadableVector implements Serializable {
// fields, getters and setters
public void add (ReadableVector v) {/* delegate to Vectors */}
public void subtract(ReadableVector v) {/* delegate to Vectors */}
public void multiply(ReadableVector v) {/* delegate to Vectors */}
}
/** Tool class containing all the logic */
public final class Vectors {
public static ImmutableVector add(ReadableVector v1, ReadableVector v2) {...}
public static void addTo(Vector v1, ReadableVector v2) {...}
...
}
我将Vector从接口更改为抽象类,因为基本上vector不应该是其他任何东西。
谢谢大家。
作为Vector库的用户,我不希望有一个add
实现来修改当前的Object和另一个add
实现(相同的接口),它返回一个新的实现。
最好有一组明确的方法不修改当前对象,然后在可变向量中有其他方法来修改当前对象。
我不认为你的设计有任何明显的错误。 我发现它完全有效。 如果我是你,我会考虑很少的事情:
当然,大多数这些观点都是有争议的,这些只是我的观点。
你的想法很好,但它并不完美。
你遗漏了泛型。
您假设为Vector所持有的类型定义了加法和减法等算术运算,这可能不正确。 (泛型可能有助于此。)
我不知道不可变向量在数学和物理学的背景下有多么有用。
一个完美的API会有一个类似的Matrix类,因为你需要为数学和物理做线性代数。
我将看看Apache的常见数学库以获得灵感。 它是JAMA的继承人。 我发现,通过我的优秀人员查看成功的设计和实施是一种很好的学习方法。
我觉得这个设计不是很好。 拥有可变的算术对象并不好 ,即使你明确标记为可变。 另外,我不会将向量操作放在类向量中。 因为现在你只有加法和乘法,明天你会想要别的东西,你的类会增长和增长,因为你会添加这个或什么向量操作。 如果我是你,我会创建一个像这样的不可变向量
public class Vector {
private Double X;
private Double Y;
public Vector(Double x, Double y) {
X = x;
Y = y;
}
public Double getX() {
return X;
}
public Double getY() {
return Y;
}
}
然后我会创建一个用于执行基本向量操作的类:
public class BaseVectorAlgebra {
public static Vector add(Vector arg1, Vector arg2) {
return new Vector(arg1.getX() + arg2.getX(), arg1.getY() + arg2.getY());
}
}
通过这种方式,您可以轻松扩展系统,而无需触及现有类,也不会引入可变性,这只会使事情复杂化。
更新:
如果你仍然想要使用可变向量,那么我会将SetX和SetY setter添加到Vector类中,但是将可变性决策放入BaseVectorAlgebra中,如下所示:
public static Vector addInto(Vector arg1, Vector arg2) {
arg1.setX(arg1.getX() + arg2.getX());
arg1.setY(arg1.getY() + arg2.getY());
return arg1;
}
但实际上我不喜欢这里的可变性,因为它引入了不必要的复杂性
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.