与Java的公共领域有什么关系?

时间:2011-10-31 20:07:13

标签: java oop

我一直在javaworld.com上阅读两篇文章(1) (2),关于所有类字段应该是私有的,getter / setter方法也同样糟糕。对象应该对其拥有的数据起作用,而不是允许访问它。

我目前正在为Connect Four开设大学作业。在设计程序时,玩游戏的代理商需要访问董事会的状态(因此他们可以决定要移动的内容)。他们还需要将此移动传递给游戏,以便将其验证为合法移动。在决定移动内容时,将各个部分分组为具有起点和终点的威胁。

Board,Threat和Point对象实际上并没有做任何事情。它们只是用于存储可以人类可读方式访问的相关数据。

在设计之初,我将板上的点表示为两个元素int数组,但是在创建点或引用它们的组件时会很烦人。

所以,班级:

public class Point {
    public int x;
    public int y;
    public Point(int x, int y){
        this.x = x;
        this.y = y;
    }
}

我能想到的各方面都很完美。除了它打破了我学到的每一条规则。我犯了罪吗?

9 个答案:

答案 0 :(得分:18)

公共字段将对象的表示暴露给其调用者,即如果表示必须更改,则调用者也是如此。

通过封装表示,您可以强制调用者如何与其进行交互,并且可以更改该表示,而无需在公共API未更改的情况下修改调用者。在任何非平凡的程序中,封装都是实现合理可维护性所必需的。但是,虽然您需要胶囊,但它们的适当粒度可能比单个类大。例如,从Iterator操作的Collection的内部表示中封装public class Point { public int x; public int y; public Point(int x, int y){ this.x = x; this.y = y; } } 毫无意义。

有了这个,让我们看一下你的例子:

Point

该类的内部表示极不可能改变,因此通过将字段设为私有来隐藏表示的结构没有任何好处。但是,我会阻止调用者在构造后修改public class Point { public final int x; public final int y; public Point(int x, int y){ this.x = x; this.y = y; } }

Point

以便实际希望封装其状态的类可以返回其Game而不使用leaking其内部表示,并在其表示中使用给定的Point而不使用capturing。这也非常适合一个点的数学概念,它没有身份或改变状态。

  

在设计程序时,玩游戏的代理需要访问董事会的状态(以便他们决定要移动的内容)。他们还需要将此移动传递给游戏,以便将其验证为合法移动。在决定移动内容时,将各个部分分组为具有起点和终点的威胁。

     

Board,Threat和Point对象实际上并没有做任何事情。它们只是用于存储可以人类可读方式访问的相关数据。

现在这听起来像是一个浪费的封装机会:代理人真的不应该被允许任意修改董事会,但仅限于法律行动。为什么在Board课程中更新的州何时才能决定合法行动是什么?Board如果public class Board { // private fields with state // public methods to query state public void perform(Move move) throws IllegalMoveException; } 要自行验证移动,则没有来电者,特别是没有代理人,可能会违反游戏规则:

{{1}}

答案 1 :(得分:12)

将一个字段公开并不总是一件坏事,但是你严格限制自己,因为你将实现与使用它的类耦合起来。稍后你想要添加一个监听器,只要设置了X中的值,就会通知你。没有重构一切,你无法做到这一点。如果你实现了setX()并隐藏了字段本身,你只需要改变setX()的实现来通知你在没有改变使用这种方法的类的情况下进行了更改。

答案 2 :(得分:5)

如果您知道界面不会改变,那么将变量公之于众是完全合法的。问题是随着程序变得越来越复杂,您需要更改对字段的访问权限,但现代IDE使重构变得容易,所以我想说继续。

答案 3 :(得分:5)

这是c#优于Java的一个优点。你可以宣布一个有公共领域的类,然后改变你的想法使用带有getter和setter的隐藏字段;但调用者的语法是相同的

public class Point
{
     public int X;
}

变为

public class Point
{
     int m_x;
     public int X {get {return m_x;} set {m_x = value;}
}

但来电者总是这样做

Point p;
p.X = 12;

答案 4 :(得分:4)

是和否。

不,如果:你确定你的范围,并且不需要任何需要普通Java属性(getter / setter)的东西,那么它可能不是什么大问题。

是的,如果:某些行为可能会发生变化,就像您需要抽象影响x或y的计算而不影响调用代码,或者您正在使用期望获取者和/或设置者的东西。

通常,遵循普通的Java属性模式最容易 - 它消除了一种风险。这也是我希望Java拥有真正属性的原因之一。

答案 5 :(得分:3)

Java的“规则”并不是绝对的。在您的情况下,当您仅使用对象存储数据而不提供关键行为时,将字段公开是完全可以的。

另一方面,对于您不需要或想要向用户公开的字段,因为它们可能仅在内部上下文中相关,那么您应该将字段标记为私有,并且仅提供getter和/或setter如果你真的需要/想要。

对象越复杂,确保操作之间的状态就越重要,因此程序员越有可能保持对象状态的良好封装。

答案 6 :(得分:1)

我认为没问题。如果犯了罪,那至少不是凡人。向量是另一种方法,但从我收集的内容中,java中的向量需要太多开销。

答案 7 :(得分:1)

公共字段违反了封装规则,即保护数据。是的,他们确实持有数据但是通过让您的实例变量PUBLIC可以被工作区中的任何类访问。 实例变量既可以被保护也可以被私有。您可以使用您的getter和setter方法,这样您就可以修改类的实例变量所拥有的数据。 通常你的setter方法会有某种验证,这就是为什么我们必须保护腐败数据的实例变量(通过将它们标记为私有或受保护)

在上面的例子中,你有一个构造函数,它正在初始化你的inst。变量,但是作为此类的开发人员,您可能拥有权限并知道要插入哪些数据以保持类的完整性。在你的班级上工作的其他人可能不会意识到这一点,并且可能通过打破封装和整个程序来访问你的变量。

考虑x = -10;当x只能从0到100(例如)。 我建议坚持封装原则。 希望这有帮助!

答案 8 :(得分:0)

有时规则会预测您可能没有考虑过的使用情况。

如果你创建一个点或一个点的哈希映射怎么办?为了让你按照你希望它工作的方式工作,你应该实现equals()和hashcode()重载,但是最好的做法是使对象不可变 - 这样你就不能从下面改变值了设置/地图。