5

我们以这个类为例:

public class Student{
    private String name;
    private String id;

    public Student(String name, String id){
        this.name = name;
        this.id = id;
    }

    ... getters and setters for both fields

并将其与此进行比较:

public class Student{
    public final String name;
    public final String id;

    public Student(String name, String id){
        this.name = name;
        this.id = id;
    }
 }

在我看来,不需要访问器。
这会被认为是糟糕的 OO 设计吗?

4

8 回答 8

7

这是一个加载的问题。

不幸的是,好的(或坏的)设计 100% 取决于它的使用方式。

根据经验,保持成员变量私有是一个好主意,这样对象模型才能控制它们的访问。这意味着第一种方法更好。

如果价值观永远不会改变,那有什么意义呢?如果永远不会使用 setter,为什么还要编写 setter?

那么,哪一个更好呢?正如我上面提到的,这取决于你这样做的目的。如果是课堂作业,我会选择第一个。你的老师会更喜欢它,因为它更像是“教科书”。

因此,如果这是一个个人项目或您可以利用未来版本的工作,我会选择两者之间的交叉:

public class Student{
    private final String name;
    private final String id;

    public Student(String name, String id){
        this.name = name;
        this.id = id;
    }

  ... getters ONLY for both fields

这种方法是安全的,因为成员字段是私有的,并且没有未使用方法的“代码气味”。这也是相当可扩展的,因为如果您的需求发生变化并且您需要修改字段,您可以很容易地添加设置器。

于 2012-08-08T00:14:59.773 回答
6

您应该始终致力于将可变性和范围限制在严格的最小值。在您的情况下:将字段设为私有的、最终的、带有 getter 的、没有 setter 的字段。

奖励:你的类然后变得不可变并且线程安全。

另见这篇文章

于 2012-08-08T00:19:05.330 回答
2

Getter 和 Setter 使 API 更加稳定。例如,考虑一个由其他类访问的类中的公共字段。现在稍后,您想在获取和设置变量时添加任何额外的逻辑。这将影响使用 API 的现有客户端。因此,对该公共字段的任何更改都需要更改引用它的每个类。相反,使用访问器方法,可以很容易地添加一些逻辑,比如缓存一些数据,稍后再懒惰地初始化它。此外,如果新值与以前的值不同,则可以触发属性更改事件。所有这些对于使用访问器方法获取值的类来说都是无缝的。

ThePragmaticProgrammer 也建议始终使用 getter 和 setter,因为它是更通用的接口。查看这篇文章http://c2.com/cgi/wiki?AccessorsAreEvil它非常好,并且对我们为什么应该使用它们提供了深刻的理解。

于 2012-08-08T02:30:21.200 回答
0

当然,如果您知道它们永远不会改变,那么您就不需要访问器。

但如果你告诉我你确定他们永远不会改变,我不会相信你。

于 2012-08-08T00:11:38.150 回答
0

如果字段是final,那么你真的不需要使用 getter/setter。二传手也毫无意义,因为他们永远不会改变。

于 2012-08-08T00:13:16.960 回答
0

一致性规则。如果您有一些类/属性可以公开访问变量,而其他类/属性具有“getter”,那么这将不是一个好地方。

假设您Student上课并添加一个java.util.Date字段。

public final Date dateOfBirth;

啊,现在你Student以不受控制的方式变化。我们需要。

private final Date dateOfBirth;
...
public Date dateOfBirth() {
     return new Date(dateOfBirth.getTime()); // Awful API.
}

通常,只需将每个(实例)字段设为私有(数据传输对象除外)。如果您有二传手,那么您可能已经放弃了对封装的所有希望。Getter 适用于不可变的值对象。(“get”前缀是 Java 的一个约定(主要是),但它主要是噪音,特别是在支持不可变值对象和告诉-不要-询问接口的代码中。如果删除“get”,客户端代码只有额外的“()”作为直接字段访问的冗长。)

于 2012-08-08T02:48:18.490 回答
0

那里有一些很好的答案,我同意将字段设为私有并添加适当的构造函数/getter/setter 以在需要时限制可变性。

我将指出使用基于字段的分配的另一个缺点。任何基于代理的 AOP 在向字段分配添加切入点时都会遇到麻烦,因此通过基于代理的 AOP 构造进行日志记录(和其他操作)会变得很困难,因为它们仅限于方法拦截。您很可能被迫实际进行编织,这变得更加复杂。来自 Spring 自己的文档:

Spring AOP 当前仅支持方法执行连接点(建议在 Spring bean 上执行方法)。没有实现字段拦截,尽管可以在不破坏核心 Spring AOP API 的情况下添加对字段拦截的支持。如果您需要建议字段访问和更新连接点,请考虑使用 AspectJ 等语言。

于 2012-08-08T04:29:32.357 回答
-1

我不认为这是一个坏主意。

如果它们不会更改,请确保使用适当的关键字标记字段,例如Final.

使不变的值以不同的方式显示也是一个好主意。例如,Java 中的常量通常全部大写。

于 2012-08-08T00:18:14.640 回答