我一直为大多数类属性提供getter和setter。
虽然我已经阅读过这是不好的 - http://www.codeweavers.net/getters-and-setters-are-evil/
但是,依赖注入和单元测试是否需要大多数属性都有setter呢?
我一直为大多数类属性提供getter和setter。
虽然我已经阅读过这是不好的 - http://www.codeweavers.net/getters-and-setters-are-evil/
但是,依赖注入和单元测试是否需要大多数属性都有setter呢?
必须始终使用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的可见性。
Account
的类:public class Account
{
public decimal Balance { get;set; }
}
但是,从概念上讲,这与以下情况没有区别:
public class Account
{
public decimal Balance;
}
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和setter...我还没有遇到过任何一个真正这样做的人)。
真正不好的主意是为每个变量都有公共的getter和setter。这几乎保证了一个封装不良的类。
在C++中,可以通过友元来绕过私有访问以提供白盒测试,我认为你可以使用其他语言中的其他机制来做到同样的事情,但我不确定(我只在C++中进行商业开发,并且我不会在Java和C#中为自己编写的应用程序进行测试)。可能反射将允许您寻找所需的行为。
除非需要,否则不要添加getter/setter,否则当您需要进行更改时,很难知道其他类依赖的数据/方法是什么。此外,过度使用getter/setter可能是面向对象设计不良的症状,这与数据封装有关。
Getter可以帮助基于状态的单元测试,尽管您可以考虑限制范围,以便只有测试类可以访问它们。或者更好的是,在类本身中通过创建一个equals
方法来进行相等性检查。
依赖注入可以帮助您避免使用getter,因为您可以通过注入模拟对象来执行基于交互的测试interaction-based testing。