使用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; }
而前者需要的样板代码要少得多。
如果您想要一个只读变量,但不想让客户端改变访问它的方式,请尝试使用这个模板类:
template<typename MemberOfWhichClass, typename primative>
class ReadOnly {
friend MemberOfWhichClass;
public:
template<typename number> inline bool operator==(const number& y) const { return x == y; }
template<typename number> inline number operator+ (const number& y) const { return x + y; }
template<typename number> inline number operator- (const number& y) const { return x - y; }
template<typename number> inline number operator* (const number& y) const { return x * y; }
template<typename number> inline number operator/ (const number& y) const { return x / y; }
template<typename number> inline number operator<<(const number& y) const { return x << y; }
template<typename number> inline number operator^(const number& y) const { return x^y; }
template<typename number> inline number operator~() const { return ~x; }
template<typename number> inline operator number() const { return x; }
protected:
template<typename number> inline number operator= (const number& y) { return x = y; }
template<typename number> inline number operator+=(const number& y) { return x += y; }
template<typename number> inline number operator-=(const number& y) { return x -= y; }
template<typename number> inline number operator*=(const number& y) { return x *= y; }
template<typename number> inline number operator/=(const number& y) { return x /= y; }
primative x;
};
示例用途:
class Foo {
public:
ReadOnly<Foo, int> cantChangeMe;
};
记住,您还需要添加按位和一元运算符!这只是为了让你开始
公共字段并不比getter/setter对差,它除了返回字段并赋值之外什么都不做。任何差异都必须存在于其他因素,如可维护性或可读性。
getter/setter对的一个经常提到的优点不是。有一种说法是,您可以更改实现,而不必重新编译客户端。据推测,setter允许您稍后添加验证之类的功能,而您的客户甚至不需要知道它。然而,将验证添加到setter是对其前提条件的更改,违反了以前的合同,这很简单,“您可以将任何东西放在这里,稍后您可以从getter那里获得相同的东西”。
因此,现在您违反了合同,更改代码库中的每个文件是您应该做的事情,而不是避免。如果你避免这样做,你就假设所有的代码都假设这些方法的契约是不同的。
如果这不应该是约定,那么接口允许客户端将对象置于无效状态。这与封装正好相反。如果该字段从一开始就不能真正设置为任何值,为什么验证从一开始不存在?
同样的论点也适用于这些传递getter/setter对的其他假定优点:如果您稍后决定更改设置的值,那么您就违反了合同。如果您在派生类中重写默认功能,而不是进行一些无害的修改(如日志记录或其他不可观察的行为),那么您就违反了基类的约定。这违反了Liskov替代原则,这被视为OO的原则之一。
如果一个类对每个字段都有这些愚蠢的getter和setter,那么它就是一个没有不变量、没有契约的类。这真的是面向对象的设计吗?如果类只有那些getter和setter,那么它只是一个哑数据持有者,哑数据持有者应该看起来像哑数据持有者:
class Foo {
public:
int DaysLeft;
int ContestantNumber;
};
向此类类添加传递getter/setter对不会增加值。其他类应该提供有意义的操作,而不仅仅是字段已经提供的操作。这就是如何定义和维护有用的不变量。
客户:“我可以用这个类的对象做什么?”设计器:“你可以读写几个变量。”客户:“哦……我想很酷吧?”
使用getter和setter是有原因的,但如果这些原因不存在,那么以虚假封装之神的名义制作getter/setter对并不是一件好事。使用getter或setter的有效原因包括经常提到的稍后可以进行的潜在更改,如验证或不同的内部表示。或者,该值应该是客户端可读但不可写的(例如,读取字典的大小),因此简单的getter是一个不错的选择。但是,当你做出选择时,这些理由应该存在,而不仅仅是你以后可能想要的潜在原因。这是YAGNI(你不需要它)的一个例子。