当前位置:  开发笔记 > 编程语言 > 正文

像Java中的对象一样结构化

如何解决《像Java中的对象一样结构化》经验,为你挑选了8个好方法。

是否完全违反Java方式来创建类似对象的结构?

class SomeData1 {
    public int x;
    public int y;
}

我可以看到一个带有访问器和mutator的类更像Java.

class SomeData2 {
    int getX();
    void setX(int x);

    int getY();
    void setY(int y);

    private int x;
    private int y;
}

第一个例子中的类是符号方便的.

// a function in a class
public int f(SomeData1 d) {
    return (3 * d.x) / d.y;
}

这不方便.

// a function in a class
public int f(SomeData2 d) {
    return (3 * d.getX()) / d.getY();
}

小智.. 287

似乎许多Java人员不熟悉Sun Java编码指南,该指南认为当类本质上是"Struct"时使用公共实例变量是非常合适的,如果Java支持"struct"(当没有行为时).

人们倾向于认为getter和setter是Java方式,就像它们是Java的核心一样.事实并非如此.如果您遵循Sun Java编码指南,在适当的情况下使用公共实例变量,您实际上编写的代码比使用不必要的getter和setter使其混乱更好.

1999年的Java代码约定仍未改变.

10.1提供对实例和类变量的访问

没有充分理由,不要公开任何实例或类变量.通常,实例变量不需要显式设置或获取 - 通常是方法调用的副作用.

适当的公共实例变量的一个示例是类本质上是数据结构而没有行为的情况.换句话说,如果您使用结构而不是类(如果Java支持的结构),那么将类的实例变量公之于众是恰当的.

http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177

http://en.wikipedia.org/wiki/Plain_old_data_structure

http://docs.oracle.com/javase/1.3/docs/guide/collections/designfaq.html#28



1> 小智..:

似乎许多Java人员不熟悉Sun Java编码指南,该指南认为当类本质上是"Struct"时使用公共实例变量是非常合适的,如果Java支持"struct"(当没有行为时).

人们倾向于认为getter和setter是Java方式,就像它们是Java的核心一样.事实并非如此.如果您遵循Sun Java编码指南,在适当的情况下使用公共实例变量,您实际上编写的代码比使用不必要的getter和setter使其混乱更好.

1999年的Java代码约定仍未改变.

10.1提供对实例和类变量的访问

没有充分理由,不要公开任何实例或类变量.通常,实例变量不需要显式设置或获取 - 通常是方法调用的副作用.

适当的公共实例变量的一个示例是类本质上是数据结构而没有行为的情况.换句话说,如果您使用结构而不是类(如果Java支持的结构),那么将类的实例变量公之于众是恰当的.

http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-137265.html#177

http://en.wikipedia.org/wiki/Plain_old_data_structure

http://docs.oracle.com/javase/1.3/docs/guide/collections/designfaq.html#28


+1实际拥有权威来源.其他每一个答案都是人们像他们的事实一样转动他们的意见.
@ user924272:Java Beans规范如何与这个答案相关,讨论何时适合使用"公共实例变量"?如果规范是自动将实例变量转换为属性的标准方法,那么它可能是相关的.但事实并非如此,对吗?它仅指定需要创建的样板getter和setter的命名,以进行这样的映射.

2> izb..:

真的使用常识.如果你有类似的东西:

public class ScreenCoord2D{
    public int x;
    public int y;
}

然后将它们包裹在吸气剂和制定者中是没有意义的.你永远不会以任何其他方式在整个像素中存储x,y坐标.吸气剂和制定者只会减慢你的速度.

另一方面,有:

public class BankAccount{
    public int balance;
}

您可能希望在将来的某个时间点更改计算余额的方式.这应该真的使用getter和setter.

最好知道你为什么要运用良好的练习,以便你知道什么时候可以改变规则.


我赞同这个答案,并进一步说你可以创建一个带有公共字段的类,只要字段是**粗体**彼此独立.即一个领域不依赖于另一领域.在许多情况下,这对于函数的多个返回值或极性共同作用非常有用.{angle,length},它们在一起,但不以任何内在的方式相互依赖.

3> Bartosz Bier..:

这是一个经常讨论的话题.在对象中创建公共字段的缺点是您无法控制设置它的值.在有许多程序员使用相同代码的小组项目中,避免副作用很重要.此外,有时最好返回字段对象的副本或以某种方式对其进行转换等.您可以在测试中模拟此类方法.如果您创建新类,则可能看不到所有可能的操作.这就像防御性编程 - 有一天getter和setter可能会有所帮助,创建/使用它们并不需要花费太多.所以它们有时很有用.

在实践中,大多数领域都有简单的getter和setter.可能的解决方案如下所示:

public property String foo;   
a->Foo = b->Foo;

更新:在Java 7中或者可能永远不会添加属性支持.其他JVM语言(如Groovy,Scala等)现在支持此功能. - 亚历克斯米勒


那太糟糕了,我喜欢C#风格的属性(这听起来像你在谈论的)
我更喜欢能够使用`=`,这在我看来使代码更清晰.
@T-Bull:只是因为你可以有两个不同的东西,这不是一个好主意.恕我直言,这是一个不好的建议,因为它可能导致人类读者的混淆.基本原则:不要让读者做双重考虑; 明确你说的是什么 - 为不同的实体使用不同的名称.即使差异仅仅是前缀下划线.不要依赖周围的标点来区分实体.
所以使用重载... private int _x; public void x(int value){_ x = value; public int x(){return _x; }
@ToolmakerSteve:当涉及到捍卫编码教条(而不是编码风格)时,"可能导致混淆"是最常见且仍然是最弱的论点.总有人可能会被最简单的事情弄糊涂.你总会发现有人抱怨他在保持清醒和编码半周或其他一些错误之后犯了一个错误然后归咎于误导性的编码风格.我不记得那个.该样式是有效的,显而易见的并且保持名称空间清洁.此外,这里没有不同的实体,有/ one/entity和一些样板代码.

4> Brian..:

为解决可变性问题,您可以将x和y声明为final.例如:

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

调用尝试写入这些字段的代码将得到编译时错误"字段x被声明为final;无法分配".

然后,客户端代码可以具有您在帖子中描述的"简写"便利

public class DataTest {
    public DataTest() {
        Data data1 = new Data(1, 5);
        Data data2 = new Data(2, 4);
        System.out.println(f(data1));
        System.out.println(f(data2));
    }

    public int f(Data d) {
        return (3 * d.x) / d.y;
    }

    public static void main(String[] args) {
        DataTest dataTest = new DataTest();
    }
}


谢谢 - 一个有用而简洁的答案.演示了如何在不需要可变性的情况下获得字段语法的好处.

5> sampathsris..:

不要使用public字段

public当您真的想要包装类的内部行为时,请不要使用字段.以java.io.BufferedReader为例.它有以下字段:

private boolean skipLF = false; // If the next character is a line feed, skip it

skipLF在所有读取方法中读取和写入.如果在一个单独的线程中运行的外部类恶意修改skipLF了读取中间的状态怎么办?BufferedReader肯定会变得混乱.

使用public字段

以这Point堂课为例:

class Point {
    private double x;
    private double y;

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

    public double getX() {
        return this.x;
    }

    public double getY() {
        return this.y;
    }

    public void setX(double x) {
        this.x = x;
    }

    public void setY(double y) {
        this.y = y;
    }
}

这将使计算两点之间的距离非常痛苦.

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.getX() - a.getX(), 2) + Math.pow(b.getY() - a.getY(), 2));

除了普通的getter和setter之外,该类没有任何行为.当类只表示一个数据结构时,使用公共字段是可以接受的,并且没有,并且永远不会有行为(瘦的getter和setter 在这里被认为是行为).它可以这样写得更好:

class Point {
    public double x;
    public double y;

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

Point a = new Point(5.0, 4.0);
Point b = new Point(4.0, 9.0);
double distance = Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));

清洁!

但要记住:不仅你的班级必须缺乏行为,而且也应该没有理由在将来也有行为.


(这正是这个答案所描述的.引用"Java编程语言的代码约定:10.编程实践":

适当的公共实例变量的一个示例是类本质上是数据结构而没有行为的情况.换句话说,如果你会使用struct,而不是一个类(如果支持Java struct),那么它的合适使类的实例变量公开.

所以官方文档也接受这种做法.)


此外,如果您更确定上述Point类的成员应该是不可变的,那么您可以添加final关键字来强制执行它:

public final double x;
public final double y;



6> Spoike..:

顺便提一下,您作为示例提供的结构已经存在于Java基类库中java.awt.Point.它有x和y作为公共字段,请自行检查.

如果您知道自己在做什么,并且团队中的其他人知道这一点,那么公共领域是可以的.但是,你不应该依赖于它,因为它们能引起头痛作为使用对象涉及到开发商的错误,好像他们是堆栈分配结构(Java对象总是发送到方法作为参考而不是复印件).



7> Mark Renouf..:

回复:aku,izb,John Topley ......

注意可变性问题......

省略getter/setter似乎是明智的.在某些情况下,它实际上可能没问题.这里显示的提议模式的真正问题是可变性.

问题是,一旦您传递包含非最终公共字段的对象引用.该引用的任何其他内容都可以自由修改这些字段.您无法再控制该对象的状态.(想想如果字符串是可变的会发生什么.)

当该对象是另一个对象的内部状态的重要部分时,它会变坏,您刚刚暴露了内部实现.为防止这种情况,必须返回对象的副本.这可行,但可能会造成大量的一次性副本创建的GC压力.

如果您有公共字段,请考虑将该类设置为只读.将字段作为参数添加到构造函数,并将字段标记为final.否则,请确保您没有暴露内部状态,如果需要为返回值构造新实例,请确保不会过度调用它.

请参阅:Joshua Bloch撰写的" 有效Java " - 第13项:偏好不变性.

PS:还要记住,如果可能的话,现在所有的JVM都会优化掉getMethod,导致只有一个字段读取指令.


getter/setter如何解决此问题.您仍然有一个参考,您没有与操作同步.吸气剂/安装者本身不提供保护.

8> Steve B...:

我已经在一些项目中尝试了这个,理论上getter和setter在语义上毫无意义地混淆了代码,并且其他语言似乎可以很好地使用基于约定的数据隐藏或职责划分(例如python).

正如其他人在上面提到的那样,你遇到了两个问题,而且它们并不是真正可以修复的:

几乎所有Java世界中的自动化工具都依赖于getter/setter约定.正如其他人所指出的那样,jsp标签,弹簧配置,eclipse工具等也是如此...反对你的工具期望看到的东西是长期会话的一个秘诀,通过谷歌试图找到非标准的启动方式春豆.真的不值得麻烦.

一旦你有一个包含数百个公共变量的优雅编码应用程序,你可能会发现至少有一种情况,它们是不够的 - 你绝对需要不变性,或者你需要在变量设置时触发一些事件,或者你想抛出变量更改的异常,因为它将对象状态设置为令人不快的事物.然后,你会遇到令人尴尬的选择,在任何变量被直接引用的地方,用一些特殊的方法来混乱代码,在应用程序的1000个变量中有3个具有特殊的访问形式.

这是完全在一个独立的私人项目中工作的最佳情况.一旦将整个事物导出到可公开访问的库中,这些问题就会变得更大.

Java非常冗长,这是一件很诱人的事情.不要这样做.

推荐阅读
mobiledu2402851173
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有