我一直为大多数类属性提供 getter 和 setter。
虽然我读过这很糟糕 - http://www.codeweavers.net/getters-and-setters-are-evil/
依赖注入和单元测试不要求大多数属性都有一个设置器吗?
我一直为大多数类属性提供 getter 和 setter。
虽然我读过这很糟糕 - http://www.codeweavers.net/getters-and-setters-are-evil/
依赖注入和单元测试不要求大多数属性都有一个设置器吗?
应始终使用 getter 和 setter。getter 或 setter的原因不是为内部属性提供公共接口,而是提供对属性的读/写的控制。它们提供对类属性的抽象。
即使您的类属性是私有的,您也需要 getter 和 setter。这允许在分配或读取之前控制值。
想想你很久以前设计的一个类,你为每次阅读做一些共同的计算。
class A{
private decimal x;
public void do_stuff(){
decimal a = this.x/70;
// process with a
}
public void do_anoter_stuff(){
decimal a = this.x/70;
// process again a
}
}
现在您要更改因子 (70)。你怎么做呢?到处换?最好这样设计。
class A{
private decimal x;
private get_x(){ return this.x/70; }
public void do_stuff(){
// process with get_x()
}
public void do_anoter_stuff(){
// process again get_x()
}
}
事实是盲目地为每个属性使用 getter 和 setter 是邪恶的。经验法则是。使用私有 getter 和 setter 将所有属性声明为私有。稍后更改 getter 和 setter 的可见性,仅在需要时允许从外部世界访问
那篇文章的意思是,您不应该通过公共 getter 和 setter 公开您的私人数据。为所有成员设置 getter 和 setter 并没有隐含的错误(有些人会争辩说这是一个好主意,尽管实际上很少有人真正为每个私有变量而烦恼……我还没有遇到真正这样做的人反正)
一个坏主意是为每个变量设置公共getter 和 setter。这几乎保证了一个封装不良的类。
在 c++ 中,可以通过友谊绕过私有访问以提供白盒测试,我认为您可以使用其他语言中的其他机制来做同样的事情,但我不确定(我只在 c++ 中进行了商业开发,而且我不会测试我用 Java 和 C# 编写的有趣的应用程序)。可能反射将允许您正在寻找的行为。
那篇文章的意思是,OO 意味着封装数据和提供行为。通过提供属性,您不会封装数据。例如,您可以实现这样的Account
类:
public class Account
{
public decimal Balance { get;set; }
}
但是,从概念上讲,这与:
public class Account
{
public decimal Balance;
}
在任何一种情况下,该类都没有行为。所有操作的行为都Account
需要在Account
. 文章说的是行为和国家应该共存。所以,你可能有这样的东西:
public class Account
{
private decimal balance;
//...
public void DepositFunds(Money money)
{
balance += ValidateAndConvert(money);
}
public void WithdrawFunds(Money money)
{
balance -= ValidateAndConvert(money);
}
public void AdjustBalance(Money money)
{
balance -= ValidateAndConvert(money);
}
private decimal ValidateAndConvert(Money money)
{
// TODO: validate, convert
}
}
对于将余额作为属性或字段的帐户,外部逻辑可以根据需要对其进行修改。这通常将业务逻辑分散在代码库中。如果需要从帐户中提取资金的逻辑来检查余额并验证帐户不能透支,或者只能透支一定数量,那么代码中的许多地方都必须提供该逻辑。如果需要更改该逻辑,则必须找到并更改许多地方(风险是一个被遗漏并且发生不一致的撤回)。当数据被封装在对象中并且只提供行为时,就无法将该逻辑分散在代码库中。这也允许更明确的代码。可能account.Balance -= someValue;
是撤回、调整等。现在可以明确了:account.Widthdraw(someValue);
或 account.AdjustBalance(someOtherValue);`--这是明确的帐户发生了什么。
除非您需要,否则我不会添加 getter/setter,否则很难知道其他类的数据/方法取决于您何时需要进行更改。此外,过度使用 getter/setter 可能是面向对象设计不佳的症状,这完全是关于数据封装的。
Getter 当然可以帮助基于状态的单元测试,尽管您可能会考虑限制范围,以便只有测试类可以访问它们。或者更好的是,通过创建一个equals
方法在类本身中进行相等性检查。
依赖注入可以帮助您避免 getter,因为您可以通过注入模拟对象来进行基于交互的测试。