繁体   English   中英

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

[英]Is it better to use getters or to access private members directly?

以下哪个更好? 它甚至基于意见还是存在任何相关差异? 在某些情况下可以偏爱其中一个吗?

public class MyClass {
    private Integer myField;

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

    public Integer getMyField() {
        return myField;
    }

}

我需要一种方法来检查是否允许某事。 请不要谈论这个代码示例的意义。 这只是一个最小的例子。

实施1

public boolean isAllowed() {
    MyEnum.ALLOWED.getInt().equals(getMyField());
}

实施2

public boolean isAllowed() {
    MyEnum.ALLOWED.getInt().equals(myField);
}

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

以下哪个更好? 它甚至基于意见还是存在任何相关差异? 在某些情况下可以偏爱其中一个吗?

我认为这是良好实践的问题。 区别在于代码的可读性。

作为一般规则,如果不需要,您应该避免间接。 MyClass的当前实例在这些字段之一中具有用于实现操作的信息。 它不需要向自己隐藏其内部状态。
所以在内部, MyClass没有有价值的理由支持使用getMyField()而不是直接使用myField字段。
getMyField()访问器更适合类的客户端使用。
所以我认为在您的示例代码中无论如何都更好:

public boolean isAllowed() {
    MyEnum.ALLOWED.getInt().equals(myField);
}

编辑
除了可读性之外,这里还有一个示例,说明为什么您没有兴趣将内部状态耦合到公共 getter。
在开发阶段,你从类中删除假设public getMyField()方法,因为不再需要或为类的客户没有任何不再需要,如果isAllowed()依赖于getMyField()在执行,它会被打破,你应该用myField替换它。

我的答案不会是最有信息量的,但是它将来自处理这种模式的直接经验。 在设计对象时,通常倾向于直接访问成员字段而不是依赖访问器。 这个愿望源于想要简化对象并避免从简单返回值的方法中增加混乱。 以您的示例进一步添加上下文和含义:

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;
    }

}

这里的年龄是一个简单的数字,似乎没有必要在它周围添加 getter/setter。 如果我们添加以下方法,您会很想直接访问该字段,因为行为没有变化:

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

然后,您的对象可能会使用依赖于您继续直接使用它的年龄字段的方法来增加新功能。 稍后,您可能会改变年龄的产生方式并从网络中提取价值。 您可能不想将网络逻辑合并到构造函数中,因为这是一项昂贵的操作,只能在需要时触发。 calculateScore()方法可以建立网络连接并发现年龄,但所有其他依赖于年龄的方法也是如此。 但是,如果calculateScore 如下所示呢?:

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

然后,您可以增强对象,改变其推导年龄的方式,而无需触及calculateScore()方法。 这意味着您的方法遵循开放封闭原则 (OCP)。 它对增强开放但对更改关闭,或者您不必更改方法源以更改它的年龄。

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

一般来说,您应该明白封装的需要几乎从来都不是立竿见影的。 随着对象的增长,它会随着时间的推移而出现,如果对象从一开始就没有设置封装,那么分阶段使用它的成本会更高。这就是使这种方法难以理解的原因。 需要经验(即进行典型的过度简化并在几年内多次遭受痛苦)才能感觉到为什么需要封装。 这不是你可以观察和发现的。

也就是说,当 IDE 的功能不那么完整时,这曾经是一个比今天更大的问题。 今天,您可以在某些 IDE(如 IntelliJ)中使用内置的encapsulate fields重构来根据需要引入模式。 即使使用现代 IDE,从一开始就练习封装仍然是有利的。

我建议使用 getter,因为在某些情况下,它可以有一些额外的逻辑(如格式化、检查空值等)。 因此,在使用字段本身时,您可能会丢失一些逻辑。

为了保持良好的封装性,您首先需要考虑的是您将在类之外公开哪些方法,例如,如果在这里,例如您将只使用 is allowed 方法,我将只公开该方法,并且在构造函数中定义字段,如果该字段适合更改,那么您将需要 getter/setter,但始终取决于您想从类中提供什么。 并尽可能多地封装。

public class MyClass {
    private Integer myField;

    MyClass(Integer myField){
        this.myField = myField;
    }

    //Only this method is offered nobody need to know you use myField to say true/false
    public boolean isAllowed() {
        MyEnum.ALLOWED.equals(myField);
    }

}

在我的软件工程课程中,我被告知要实现“秘密原则”。 因此,您应该始终使用 getter 和 setter 例程。 这样您就可以确保没有人意外访问成员变量。

奇怪的函数或对象可能永远不会看到甚至更改成员变量,除非您通过 setter 和 getter 明确告诉它们这样做。

由于您的属性是私有的,您只能使用 getter 或 setter 方法在其他类中安全地访问它。 所以我会说最好的实现是遵循封装原则的,即使用getter而不是直接访问的实现。 这也将防止数据泄漏。

https://www.tutorialspoint.com/java/java_encapsulation.htm

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM