繁体   English   中英

Java中不可变和可变对象的设计

[英]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编写代码,认为他们的实现总是可变的。
  • 不可变性通常意味着更多的对象和性能损失,因为需要在堆中放置越来越多的对象并迫使垃圾收集做更多的工作。 如果您的应用程序需要执行许多“添加”操作,您可能需要付出代价。 但是,嘿,这就是拥有可变版本的全部目的,对吗?
  • 此外,如果您正在为多线程环境编写代码,那么如果您希望确保可以切换实现而不会产生任何后果,则仍然需要同步访问以共享Vector类型的变量。 这再次证明,编写代码无法忽略实现细节。
  • 虽然我在其他帖子中与@Paulo Eberman有点争论,但我相信他是完全正确的。 我认为最好有两个独立的接口,一个用于不可变对象,一个用于mutable(可以扩展后者)。

当然,大多数这些观点都是有争议的,这些只是我的观点。

你的想法很好,但它并不完美。

你遗漏了泛型。

您假设为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.

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