协慌网

登录 贡献 社区

为什么要使用 getter 和 setter / accessors?

使用 getter 和 setter 的优点是什么 - 只能获取和设置 - 而不是简单地使用公共字段来存储这些变量?

如果 getter 和 setter 做的不仅仅是简单的 get / set,我可以非常快地解决这个问题,但我并不是 100%清楚如何:

public String foo;

更糟糕的是:

private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

而前者需要很少的样板代码。

答案

实际上有许多充分的理由考虑使用访问器而不是直接暴露类的字段 - 除了封装的参数和使未来的更改更容易。

以下是我所知道的一些原因:

  • 与获取或设置属性相关联的行为的封装 - 这允许稍后更容易地添加附加功能(如验证)。
  • 在使用替代表示公开属性的同时隐藏属性的内部表示。
  • 保护公共接口免受更改 - 允许公共接口在实现更改时保持不变,而不会影响现有的使用者。
  • 控制属性的生命周期和内存管理(处置)语义 - 在非托管内存环境(如 C ++ 或 Objective-C)中尤为重要。
  • 为属性在运行时更改时提供调试拦截点 - 在某些语言中调试属性更改为特定值的时间和位置可能非常困难。
  • 改进了与旨在针对属性获取器 / 设置器进行操作的库的互操作性 - 可以想到 Mocking,Serialization 和 WPF。
  • 允许继承者通过重写 getter / setter 方法来更改属性的行为和语义的语义。
  • 允许 getter / setter 作为 lambda 表达式而不是值传递。
  • getter 和 setter 可以允许不同的访问级别 - 例如 get 可以是公共的,但是 set 可以受到保护。

由于 2 周(月,年)从现在开始,当你意识到你的制定者需要的不仅仅是设置的值做 ,你也意识到,财产已经在其他 238 类直接使用:-)

公共字段并不比除了返回字段并分配给它之外什么都不做的 getter / setter 对更差。首先,很明显(在大多数语言中)没有功能差异。任何差异必须与其他因素有关,如可维护性或可读性。

getter / setter 对经常提到的优点不是。有这样的说法,您可以更改实施,您的客户不必重新编译。据推测,setters 允许您稍后添加验证等功能,而您的客户甚至不需要了解它。然而,向 setter 添加验证是对其前提条件的改变, 违反了之前的合同 ,这很简单,“你可以把任何东西放在这里,你可以在以后从 getter 得到同样的东西”。

所以,既然您违反了合同,那么更改代码库中的每个文件都是您应该做的事情,而不是避免。如果你避免它,你会假设所有代码都假定这些方法的合同是不同的。

如果那不应该是合同,那么接口允许客户端将对象置于无效状态。 这与封装完全相反如果该字段从一开始就无法真正设置为任何东西,为什么从一开始就没有验证呢?

同样的论证适用于这些传递的 getter / setter 对的其他假设优势:如果你以后决定更改所设置的值,那么你就违反了合同。如果您覆盖派生类中的默认功能,超出了一些无害的修改(如日志记录或其他不可观察的行为),那么您就违反了基类的约定。这违反了 Liskov Substitutability Principle,这被视为 OO 的原则之一。

如果一个类对每个字段都有这些愚蠢的 getter 和 setter,那么它就是一个没有任何不变量的类, 没有契约 。这真的是面向对象的设计吗?如果所有课程都是那些吸气者和制定者,那么它只是一个愚蠢的数据持有者,而愚蠢的数据持有者应该看起来像愚蠢的数据持有者:

class Foo {
public:
    int DaysLeft;
    int ContestantNumber;
};

将传递 getter / setter 对添加到这样的类中不会增加任何值。其他类应该提供有意义的操作,而不仅仅是已经提供的字段的操作。这就是你如何定义和维护有用的不变量。

客户 :“我能用这个类的对象做什么?”
设计师 :“你可以读写几个变量。”
客户 :“哦...... 很酷,我猜?”

有理由使用 getter 和 setter,但如果这些原因不存在,那么以 false 封装之神的名义制作 getter / setter 对并不是一件好事。制作 getter 或 setter 的有效理由包括经常提到的事情,因为您可以在以后做出可能的更改,例如验证或不同的内部表示。或者值可能是客户端可读但不可写(例如,读取字典的大小),因此简单的 getter 是一个不错的选择。但是当你做出选择时,这些原因应该存在,而不仅仅是你以后想要的潜在事物。这是 YAGNI 的一个例子( 你不需要它 )。