是否存在可变公共字段的用例?

时间:2015-04-29 08:00:46

标签: java oop encapsulation

我想知道是否存在公共字段合理的用例(对于可变值)或者应该不惜一切代价避免使用,并且应该始终使用getter / setter。

假设有一个DTO(数据传输对象)/ Java Bean,它没有任何逻辑,只是保存数据(添加更多这可能是协议的一部分,不会改变,只会增强)。 实际上没有要封装的实现细节,因为没有实现,只有数据。 还假设Bean Validation框架用于通过注释验证对象,因此没有用于验证逻辑的setter的目的。

为什么安装者和吸气者会困扰我?

没有setter / getters,它更容易阅读;字段,特别是对于内部类(您不必滚动到设置者和getter到达其他字段)。同样,在Groovy中使用user.password而不是user.getPassword()也更清楚。

我知道项目龙目岛;但我已经从Bean验证中获得了很多注释。

所以要明确这个问题:上述场景是否可以用于公共领域,或者是否更适合拥有setter / getter? 为什么?

4 个答案:

答案 0 :(得分:1)

一个常见的情况是,在实施或执行过程中,您会发现您的记录以空用户名结束。

现在,您要么:

  • 搜索所有位置,分配属性以查找为其分配空值的位置。它可能是20个地方,最有可能的是,它不是user.userName = null而是String userName = null; ..... user.userName = userName

  • setUserName()方法中添加一项检查,以便在尝试设置空值时启动异常。您将获得完整的堆栈跟踪,并且向您的持久层引入错误数据之前停止该程序的操作。

因此,直接访问属性的用例是当您确定代码中存在(并且将来)绝对没有错误时。

我会坚持使用二传手。

此外,一些(许多)框架依赖于使用setter和getter来访问属性。

答案 1 :(得分:1)

目前的观点是,你应该拥有可变的公共字段。

它来自两个不同方向的Principle of least astonishment

首先,通常的做法是将所有字段设为私有,只能通过getter和setter访问。有很多代码可以解决这个问题令人惊讶当你遇到没有的代码时。

其次,它为您提供了可预测性。您可以保证,除了您的setter之外,没有人会更改其他任何地方的字段。因此,您可以在设置器上设置一个断点,并知道,该字段的每次更改都将达到断点。

然而,有一些纯粹的数据情况,比如你描述了将字段公之于众的诱惑,甚至更为罕见,而不是最终的。

我的工具箱中有一个类Pair,它有公共字段:

public class Pair<P, Q> {

    // Exposing p & q directly for simplicity. They are final so this is safe.

    public final P p;
    public final Q q;

    public Pair(P p, Q q) {
        this.p = p;
        this.q = q;
    }

答案 2 :(得分:0)

  

上述情况可能适用于公共领域,还是有更好的设置者/吸气剂?

场景似乎是你在滚动浏览getter和setter时感到困扰。这不是我书中的一个很好的理由,但是如果你可以保留这些类包私有或私有嵌套类,那么Joshua Bloch就可以了,因为未来潜在的变化范围。

有效Java项14:在公共类中,使用访问器方法,而不是公共字段

  

如果公共类暴露了它   数据字段,因为客户端代码可以分发,所以改变其表示的所有希望都会丢失   广泛的。

     

但是,如果一个类是包私有的或者是一个私有的嵌套类,那么就有了   暴露其数据字段本质上没有错误 - 假设他们在描述类提供的抽象方面做了足够的工作。

就个人而言,我更严格一点,我遵循的经验法则是编译单元之外的字段不可见。 e.g:

public class Outer {
    private int fieldMustBePrivate;

    public class PublicNested {
        private int fieldMustBePrivate;
    }

    private class PrivateNested {
        int fieldOkToBePackagePrivate;
    }

    public void exampleUsage() {
        PrivateNested privateNested = new PrivateNested();
        privateNested.fieldOkToBePackagePrivate = 123;
    }
}

答案 3 :(得分:0)

是的,有:

  • 性能
  • 代码简洁

调用函数(访问器/增变器是函数)可能看起来不是什么大问题,但是当转换为汇编代码时,这意味着您必须将函数参数推送到堆栈,将返回地址推送到堆栈,执行CALL函数,执行函数体,然后将结果推送到堆栈,弹出原始参数,弹出返回地址并转到原始地址......这根据调用约定略有不同,我可能已经枚举了以下步骤错误的顺序,但通常在调用函数时会有开销。

因此对于像getter / setter这样的愚蠢函数可能只包含一个或两个汇编指令,开销与有效负载的数量级相同,可能更大。如果您正在开发具有高性能要求的游戏或任何其他软件,那么摆脱函数调用是优化技术之一。

关于第二个用例,代码简洁,getter和setter什么都不做(它们没有验证,没有同步,没有复制,没有任何东西)实际上是多余的,我们只是编写代码,因为它是Java中的约定。但他们没有任何目的。其他人(例如:谷歌)不会盲目地遵循这个惯例。

较新的语言通常提供一些机制来自动创建这些方法,甚至完全跳过它们,依靠成员访问修饰符来提供封装。例如,C#'s properties提供自动获取/设置器,但允许您的代码将它们称为公共成员。