使用getter或直接访问私有成员是否更好?

Chr*_*311 4 java oop coding-style

以下哪项更好?它是基于意见还是有任何相关的差异?在某些情况下,是否可以选择其中一种?

public class MyClass {
    private Integer myField;

    public void setMyField(Integer myField) {
        this.myField = myField;
    }

    public Integer getMyField() {
        return myField;
    }

}
Run Code Online (Sandbox Code Playgroud)

我需要一种方法来检查是否允许某些东西.请不要谈论这个代码示例的意义.这只是一个很小的例子.

实施1

public boolean isAllowed() {
    MyEnum.ALLOWED.getInt().equals(getMyField());
}
Run Code Online (Sandbox Code Playgroud)

实施2

public boolean isAllowed() {
    MyEnum.ALLOWED.getInt().equals(myField);
}
Run Code Online (Sandbox Code Playgroud)

编辑: 此帖子在链接问题中没有答案(请参阅初始帖子的评论)

dav*_*xxx 6

以下哪项更好?它是基于意见还是有任何相关的差异?在某些情况下,是否可以选择其中一种?

我认为这是良好做法的问题.不同之处在于代码的可读性.

作为一般规则,如果不需要,您应该避免间接.当前实例MyClass具有其中一个字段中的信息来实现操作.它不需要隐藏其内部状态.
所以在内部,MyClass没有任何有价值的理由支持getMyField()使用该myField领域的直接使用.
getMyField()存取器更适合于由类的客户端使用.
所以我认为在你的示例代码中它总是更好:

public boolean isAllowed() {
    MyEnum.ALLOWED.getInt().equals(myField);
}
Run Code Online (Sandbox Code Playgroud)

编辑:
除了可读性之外,这里有一个例子,为什么你没有兴趣将内部状态耦合到公共getter.
假设在开发阶段你从类中删除了public getMyField()方法,因为对于类的客户端不再需要或不需要,如果isAllowed()依赖getMyField()于它的实现,它将被破坏,你应该替换它myField.

  • 我同意你的看法。在某些情况下,必须使用 OCP 以及更一般的抽象级别,因为我们需要扩展初始行为。但是,在无法预见且几乎不可能发生变化的情况下,我认为这简直是矫枉过正。这与在任何类之前创建接口是一样的,而类具有零值以拥有接口。有意义的代码是揭示其意图的代码。所以,我们应该只在需要时才创建抽象。 (2认同)

Cli*_*iff 5

我的回答将不是最有用的,但是它将来自处理这种模式的直接经验。设计对象时,通常很想直接访问成员字段而不是依赖访问器。需求源于希望简化对象并避免简单返回值的方法增加混乱。让您的示例更进一步以添加上下文和含义:

public class MyClassmate {
    private Integer age;

    public MyClassmate(Integer age) {
        this.age = age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getAge() {
        return age;
    }

}
Run Code Online (Sandbox Code Playgroud)

这里的年龄是一个简单的数字,似乎没有必要在其周围添加吸气剂/设置剂。如果我们添加以下方法,由于行为没有变化,您将很容易直接访问该字段:

public Integer calculateScore() {
    if(age > 21) return 100 - getNumberOfIncorrectAnswers();
    //Add 10% before for younger students
    else return (100 - getNumberOfIncorrectAnswers()) + 10;
}
Run Code Online (Sandbox Code Playgroud)

然后,您的对象可能会使用依赖于您继续直接使用它的age字段的方法来扩展新功能。稍后,您可能会更改年龄的生成方式,并从整个网络中提取该值。您可能不希望将网络逻辑包含在构造函数中,因为这是一项昂贵的操作,应仅在需要时才触发。该calculateScore()方法可以建立网络连接并发现使用期限,但是其他所有依赖于使用期限的方法也都可以。但是,如果calculateScore如下所示,该怎么办?:

public Integer calculateScore() {
    if(getAge() > 21) return 100 - getNumberOfIncorrectAnswers();
    //Add 10% before for younger students
    else return (100 - getNumberOfIncorrectAnswers()) + 10;
}
Run Code Online (Sandbox Code Playgroud)

然后,您可以增强对象,从而改变其推算年龄的方式,而无需触碰该calculateScore()方法。这意味着您的方法遵循开放式封闭原则(OCP)。它可以进行增强,但不能更改,或者您不必更改方法的来源即可更改使用期限。

根据您的应用程序和对象模型的复杂性,有时仍然会出现封装访问过高的情况,但是即使在这些情况下,也应该了解直接字段访问的权衡,而这些更为简单的情况很少见。

通常,您应该了解几乎不需要封装。随着对象的增长,它会随着时间的流逝而出现,如果从一开始就没有使用封装来设置对象,则逐步引入它的成本会更高。这就是这种方法难以理解的原因。需要经验(即进行典型的过度简化并在数年内遭受多次折磨)才能感觉到为什么需要封装。这不是您可以察觉和发现的东西。

就是说,这比现在的IDE还不具备全部功能时要大得多。今天,您可以encapsulate fields在某些IDE(如IntelliJ)中使用内置的重构,以根据需要引入模式。即使使用现代IDE,从一开始就实践封装仍然是有利的。