我的问题涉及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从接口更改为抽象类,因为基本上矢量不应该是其他任何东西。
谢谢大家。
答案 0 :(得分:7)
作为Vector库的用户,我不希望有一个add
实现修改当前Object和另一个add
实现(同一个接口)返回一个新实现。 / p>
最好有一组明确的方法不修改当前对象,然后在可变向量中有其他方法来修改当前对象。
答案 1 :(得分:2)
我认为您的设计没有任何明显错误。我发现它完全有效。如果我是你,我会考虑很少的事情:
当然,大多数这些观点都是有争议的,这些只是我的观点。
答案 2 :(得分:0)
你的想法很好,但它并不完美。
你遗漏了泛型。
您假设为Vector所持有的类型定义了加法和减法等算术运算,这可能不正确。 (泛型可能会有所帮助。)
我不知道不可变向量在数学和物理学的背景下有多么有用。
完美的API会有一个类似的Matrix类,因为你需要为数学和物理做线性代数。
我将看看Apache的常用数学库以获取灵感。它是JAMA的继承人。我发现,通过我的优秀人员查看成功的设计和实施是一种很好的学习方式。
答案 3 :(得分:0)
我觉得这个设计不是很好。拥有可变的算术对象is not good,即使你明确标记为可变对象。另外,我不会将向量操作放在类向量中。因为现在你只有加法和乘法,明天你会想要别的东西,你的类会增长和增长,因为你会添加这个或什么向量操作。如果我是你,我会像这样创建一个不可变的向量
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;
}
但我真的不喜欢这里的可变性,因为它引入了不必要的并发症