面向对象编程:所有属性都需要有getter和setter吗?

3

1
这不是getter和setter的问题,而是公有和私有的问题。即使在私有属性中,您也需要getter和setter以便对属性进行读写控制。 - Shiplu Mokaddim
4个回答

5

必须始终使用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的可见性。


1
我认为这基本上忽略了所引用文章的要点。 - Peter Ritchie
see the answer that I posted. - Peter Ritchie

2
那篇文章的意思是面向对象编程(OO)意味着封装数据并提供行为。通过提供属性,你并没有封装数据。例如,你可以像这样实现一个名为 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来访问私有数据。这提供了信息隐藏 - Shiplu Mokaddim
本文的重点是属性并不能真正提供隐藏。通过属性接口,您正在公开私有数据。是的,您可以在幕后更改实现细节(例如计算而不是存储相同类型的数据),但属性基本上表示某些东西接受该时间段内所有值的范围。例如,十进制数。如果我有一个余额属性,它应该支持所有十进制数的范围。十进制数的最大值的余额毫无意义。 - Peter Ritchie

1

这篇文章的意思是,你不应该通过公共的getter和setter暴露你的私人数据。对于所有成员都有getter和setter并不是显然错误的(一些人会认为这是个好主意,尽管在实际情况下很少有人会费心为每个私有变量编写getter和setter...我还没有遇到过任何一个真正这样做的人)。

真正不好的主意是为每个变量都有公共的getter和setter。这几乎保证了一个封装不良的类。

在C++中,可以通过友元来绕过私有访问以提供白盒测试,我认为你可以使用其他语言中的其他机制来做到同样的事情,但我不确定(我只在C++中进行商业开发,并且我不会在Java和C#中为自己编写的应用程序进行测试)。可能反射将允许您寻找所需的行为。


-1

除非需要,否则不要添加getter/setter,否则当您需要进行更改时,很难知道其他类依赖的数据/方法是什么。此外,过度使用getter/setter可能是面向对象设计不良的症状,这与数据封装有关。

Getter可以帮助基于状态的单元测试,尽管您可以考虑限制范围,以便只有测试类可以访问它们。或者更好的是,在类本身中通过创建一个equals方法来进行相等性检查。

依赖注入可以帮助您避免使用getter,因为您可以通过注入模拟对象来执行基于交互的测试interaction-based testing


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接