我已获得以下代码:
public class Triangle {
private Point left;
private Point right;
private Point top;
public Triangle(Point left, Point right, Point top) {
this.left = left;
this.right = right;
this.top = top;
}
// more program logic...
}
我想知道构建这样的对象是否可行且安全,因为我担心Point类型的三个变量中的一些可以从外部修改(break encapsulation)。 例如:
public static void main(String[] args) {
Point left = new Point(0.0, 1.0);
Point right = new Point(2.4, 3.2);
Point top = new Point(5.8, 2.0);
Triangle t = new Triangle(left, right, top);
top.setX(10.2);
top.setY(23.4);
}
毫无疑问,这将操纵同样的"顶部"在Triangle变量中引用的对象。 因此修复在Triangle构造函数中执行以下操作:
public Triangle(Point left, Point right, Point top) {
this.left = new Point(left);
this.right = new Point(right);
this.top = new Point(top);
}
(请记住我在Point类中有一个复制构造函数,所以上面的三个语句都是有效的)
答案 0 :(得分:4)
您可以克隆构造函数中的原始点,并使克隆与外界保持隐藏。如果Point
已经实现了Cloneable
,或者您可以自己实现它,请以这种方式使用它:
public Triangle(Point left, Point right, Point top) {
this.left = left.clone();
this.right = right.clone();
this.top = top.clone();
}
如果Point
没有实现Cloneable
并且您无法访问其源代码,则只需手动克隆点:
public Triangle(Point left, Point right, Point top) {
this.left = new Point(left.getX(), left.getY());
this.right = new Point(right.getX(), right.getY());
this.top = new Point(top.getX(), top.getY());
}
答案 1 :(得分:3)
这是一个非常好的问题。
具有可变状态并不总是坏事。例如,您可以将此Triangle对象实例发送到显示程序,如果您更改了点坐标,则可以在屏幕上为三角形设置动画。
它取决于用例以及它应该解决的问题。
如果你下定决心,对于你的用例,整个对象图(这个对象,我的孩子和孩子的孩子等)一旦创建就应该是不可变的,有办法确保。
为了使不可变对象真正不可变,这里有一些很好的指导: http://www.javapractices.com/topic/TopicAction.do?Id=29
如果对象具有集合作为属性,则可以根据需要使用Immutable或Unmodifiable集合: Java Immutable Collections
最后,如果您确实允许可变状态,并且想要跟踪,则可以将子对象注册为观察者。 When should we use Observer and Observable
同样,原则是保持尽可能少的可变状态。换句话说,始终尽可能保持对象不变。不可变的集合很容易获得并且易于使用。问题出在我们的自定义类上。
如果您使自定义对象/模型对象完全不可变,则可能会出现复制整个对象图以更改单个属性的情况。
所以,保守一点。特别是收藏品,很容易做到。
使用面向对象的编程范例很难避免可变性。功能编程应该与不变性更加一致。你可能想看看Haskell或Scala只是为了尝试。
答案 2 :(得分:1)
如果你有一个复制构造函数,那么使用它是安全的。否则,如果点实现cloneable,则可以使用clone。