Getter和Setters何时合理


175

getter和setter经常被批评为不合适的OO。另一方面,我见过的大多数OO代码都有大量的getter和setter方法。

何时使用吸气剂和塞特剂?您是否尝试避免使用它们?他们是否普遍滥用?

如果您最喜欢的语言具有属性(我的语言),那么此类问题也被视为此问题的获取者和准备者。从面向对象方法论的角度来看,它们是同一回事。他们只是语法更好。

Getter / Setter批评的来源(部分摘自评论以使他们更清晰可见):

简单地说一下批评:Getters和Setters允许您从对象外部操纵对象的内部状态。这违反了封装。只有对象本身应该关心其内部状态。

以及示例程序版本的代码。

struct Fridge
{
    int cheese;
}

void go_shopping(Fridge fridge)
{
     fridge.cheese += 5;
}

Mutator版本的代码:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

没有提供适当的封装,getter和setter会使代码变得更加复杂。由于其他对象可以访问内部状态,因此添加这些getter和setter不会带来很多好处。

该问题先前已在堆栈溢出中讨论过:


21
Getters and setters are often criticized as being not proper OO-请引用。
罗伯特·哈维


45
@Job,为什么因为有代码?如果认真对待,这是一个很好的问题,是一场神圣的战争。
彼得·特纳

2
@Winston Ewert:“ ...问题是,是否应该访问类中的数据。”:嗯,没有人强迫您为所有成员变量实现getter和setter方法,而仅实现所需的变量。这就是为什么您使用getter和setter而不是public成员变量的原因。
乔治

2
@Winston Ewert:我不知道缺少getters / setters怎么解决这个问题:必须有一种访问每条数据的方法,否则它是无用的。一个好的设计的任务是确定代码的哪一部分可访问数据的哪一部分。如果一个设计师是一个坏设计师,那么他或她将会有或没有getter和setter。只是我的2美分。
乔治

Answers:


162

拥有getter和setter方法本身并不会破坏封装。破坏封装的方法是自动为每个数据成员(Java术语中的每个字段)添加一个getter和setter ,而无需考虑任何问题。尽管这比使所有数据成员公开更好,但仅一步之遥。

封装的重点不是您不应该从对象外部知道或更改对象的状态,而是应该拥有合理的策略来做到这一点。

  • 一些数据成员可能完全在对象内部,并且既不应该具有getter也不应该具有setter。

  • 一些数据成员应该是只读的,因此它们可能需要getter但不需要setter。

  • 某些数据成员可能需要保持彼此一致。在这种情况下,您将不会为每个设置程序提供设置程序,而是提供一种同时设置它们的单一方法,以便您可以检查值的一致性。

  • 某些数据成员可能仅需要以某种方式进行更改,例如以固定量递增或递减。在这种情况下,您将提供一个increment()和/或decrement()方法,而不是一个setter。

  • 还有一些实际上可能需要读写,并且同时具有getter和setter。

考虑一个例子class Person。假设一个人有一个名字,一个社会保险号和一个年龄。假设我们不允许人们更改其姓名或社会保险号。但是,该人的年龄每年应增加1。在这种情况下,您将提供一个构造函数,该构造函数会将名称和SSN初始化为给定值,并将年龄初始化为0。您还将提供一个方法incrementAge(),该方法会将年龄增加1。所有三个的吸气剂。在这种情况下,不需要二传手。

在此设计中,您允许从类外部检查对象的状态,并允许从类外部更改对象的状态。但是,您不允许随意更改状态。有一项策略有效地声明了名称和SSN完全不能更改,并且年龄可以一次增加1年。

现在,假设一个人也有薪水。人们可以随意更换工作,这意味着他们的工资也将改变。为了模拟这种情况,我们只能提供一种setSalary()方法!在这种情况下,允许随意更改薪水是一个完全合理的政策。

顺便说一句,在你的榜样,我会给类FridgeputCheese()takeCheese()方法,而不是get_cheese()set_cheese()。这样您将仍然具有封装。


public class Fridge {
  private List objects;
  private Date warranty;

  /** How the warranty is stored internally is a detail. */
  public Fridge( Date warranty ) {
    // The Fridge can set its internal warranty, but it is not re-exposed.
    setWarranty( warranty );
  }

  /** Doesn't expose how the fridge knows it is empty. */
  public boolean isEmpty() {
    return getObjects().isEmpty();
  }

  /** When the fridge has no more room... */
  public boolean isFull() {
  }

  /** Answers whether the given object will fit. */
  public boolean canStore( Object o ) {
    boolean result = false;

    // Clients may not ask how much room remains in the fridge.
    if( o instanceof PhysicalObject ) {
      PhysicalObject po = (PhysicalObject)o;

      // How the fridge determines its remaining usable volume is a detail.
      // How a physical object determines whether it fits within a specified
      // volume is also a detail.
      result = po.isEnclosedBy( getUsableVolume() );
    }

     return result;
  }

  /** Doesn't expose how the fridge knows its warranty has expired. */
  public boolean isPastWarranty() {
    return getWarranty().before( new Date() );
  }

  /** Doesn't expose how objects are stored in the fridge. */
  public synchronized void store( Object o ) {
    validateExpiration( o );

    // Can the object fit?
    if( canStore( o ) ) {
      getObjects().add( o );
    }
    else {
      throw FridgeFullException( o );
    }
  }

  /** Doesn't expose how objects are removed from the fridge. */
  public synchronized void remove( Object o ) {
    if( !getObjects().contains( o ) ) {
      throw new ObjectNotFoundException( o );
    }

    getObjects().remove( o );

    validateExpiration( o );
  }

  /** Lazily initialized list, an implementation detail. */
  private synchronized List getObjects() {
    if( this.list == null ) { this.list = new List(); }
    return this.list;
  }

  /** How object expiration is determined is also a detail. */
  private void validateExpiration( Object o ) {
    // Objects can answer whether they have gone past a given
    // expiration date. How each object "knows" it has expired
    // is a detail. The Fridge might use a scanner and
    // items might have embedded RFID chips. It's a detail hidden
    // by proper encapsulation.
    if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
      throw new ExpiredObjectException( o );
    }
  }

  /** This creates a copy of the warranty for immutability purposes. */
  private void setWarranty( Date warranty ) {
    assert warranty != null;
    this.warranty = new Date( warranty.getTime() )
  }
}

4
请不要太重视我的冰箱示例。:)真正的冰箱应该是一个容器对象,我希望它知道如何容纳对象,而不必担心它们的确切含义。IE浏览器就像一个ArrayList。至于使它成为冰箱的原因,可以说它在存储对象时将对象序列化到磁盘上,以使它们在系统故障中幸免。好。认真地把我的冰箱当作例子。
温斯顿·埃韦特2010年

6
但是冰箱不应该知道有效期,以便它可以告诉您奶酪已经变质并且应该扔掉吗?:)好吧,我们必须停止这个!:)
Dima 2010年

10
您将在这里对冰箱逻辑进行有趣的讨论……
Mason Wheeler,2010年

35
哈哈,我希望看到它的广告:“这是一种全新的冰箱:当试图抓住不存在的东西时,它会向您扔东西!这样,您只会尝试一次!您担心塞满冰箱,让冰箱担心非法行为!”
gablin

42
该示例完全错误。不应存在​​Age字段或setAge()方法。与某个时间点相比,年龄是人员出生日期的函数。尽管看似微不足道,但这确实是对象完全可变性和其他不良设计的现代实践的错误所在,如专用字段的get / set方法所看到的,而不是仔细考虑什么是真正可变的,什么是字段,什么是对象应该知道其行为以及实际上有效的状态更改(哪些setX方法完全销毁)。
Darrell Teague 2014年

41

Java中的getter和setter的基本原因很简单:

  • 您只能在接口中指定方法,而不能指定字段。

因此,如果要允许字段通过接口传递,则将需要一个reader和writer方法。传统上将它们称为字段x的getX和setX。


44
这是语言限制。真正的问题是我们是否应该允许其他对象操纵该状态。
Winston Ewert 2010年

3
@Peter Turner,显然没有什么可以阻止语言具有包含属性的接口。在幕后,很可能实现为getter / setter,但添加对属性的接口定义的支持将很容易。
温斯顿·埃韦特2010年

2
@Winston,您最终将需要允许类在彼此之间来回传递信息以实际完成工作。您会建议什么呢?

6
一个对象应该为其内部提供更高级别的接口。Getter和Setter往往是一个低级接口。例如,假设您有一个实现二叉树的类。您可以使用GetLeft(),GetRight(),GetValue()和GetKey()之类的功能来导航树。但这是完全错误的方法。您的二叉树类应提供诸如查找,插入,删除之类的操作。
Winston Ewert 2010年

6
再举一个例子,考虑一块俄罗斯方块。俄罗斯方块有一些内部状态,例如block_type,旋转,位置。您可能具有GetBlockType(),GetRotation(),GetPosition()之类的吸气剂。但是你真的不应该。您应该拥有一个GetSquares()方法,该方法返回该部件占用的所有正方形的列表。您也不应该拥有SetPosition()或SetRotation()之类的东西。相反,您应该具有MoveLeft(),MoveRight(),Rotate(),Fall()之类的操作。
Winston Ewert 2010年

20

来自http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and

JavaBean样式:

connection.setUser("dukie");
connection.setPwd("duke");
connection.initialize();

面向对象风格:

connection.connect("dukie","duke");

好吧,显然我更喜欢后一种方法。它不会泄漏实现细节,它更加简单明了,并且所有需要的信息都包含在方法调用中,因此更容易实现。我还更愿意在可能的情况下使用构造函数中的参数设置私有成员。

您的问题是,什么时候需要使用吸气剂/吹丝机?也许当需要更改模式时,或者您需要询问对象以获取某些信息时。

myObject.GetStatus();
myObject.SomeCapabilitySwitch = true;

考虑这一点时,当我第一次开始使用C#进行编码时,我以上述的Javabeans风格编写了许多代码。但是随着我在语言方面的经验的积累,我开始在构造函数中进行更多的成员设置,并使用看起来更像上述OO风格的方法。


6
为什么“将所有参数作为参数”为OO样式?

5
尽管您的“ OO风格”适合2或3个选项,但一旦您达到了上述目的,我将更喜欢JavaBean风格。对于每种可能的参数组合都有20种重载方法看起来很糟糕
TheLQ 2010年

1
@TheLQ:C#4.0通过在构造函数和方法调用中允许使用可选参数和命名参数,使此操作变得更容易。
罗伯特·哈维

7
@TheLQ这就是使用流利语法的Builder的用途,请参见例如Guava的MapMaker
maaartinus 2011年

5
@ThorbjørnRavn Andersen,我想说“将所有参数作为参数”比“调用JavaBean setters”更好的OO风格,因为setter暗示着正在设置一个基础属性,根据定义它公开了内部细节。在示例中使用connect(..)方法没有做出这样的假设;也许您设置了用户和密码属性,也许没有。重要的是要关注正确的面向对象概念:“连接”消息。
Andres F.

13

何时使用吸气剂和塞特剂?

当行为“ get”和“ set”实际上与模型中的行为相匹配时,这几乎是不可能的

其他所有用途只是作弊,因为业务域的行为尚不清楚。

编辑

这个答案可能是轻率的,所以让我扩展一下。上面的答案大部分是正确的,但侧重于OO设计的编程范例,还有一些遗漏的地方。根据我的经验,这使人们认为避免使用getter和setter是OO编程语言的一些学术规则(例如,人们认为用属性替换getter很重要)。

实际上,这是设计过程的结果。您不必深入研究接口,封装和模式,也不必争论它们是否破坏了这些范式以及什么是好的OO编程还是不好的OO编程。最终最重要的一点是,如果您的域中没有任何东西像这样通过将它们放入您的工作中而没有建模您的域。

现实情况是,您域空间中的任何系统极不可能有吸气剂和吸气剂。您不能走到负责工资单的男人或女人,而只能说“将此薪水设置为X”“请给我这个薪水”。这种行为根本不存在

如果将其放入代码中,则不是在设计与域模型匹配的系统。是的,这破坏了接口和封装,但这不是重点。关键是要建模一些不存在的东西。

更重要的是,您可能错过了重要的步骤或过程,因为可能有一个原因,我不能只是走上工资单,并说将薪水设置为X。

当人们使用吸气剂和吸气剂时,他们倾向于将这一过程的规则推到错误的地方。这将进一步远离您的域。使用现实世界中的示例,这就像薪水,假设走进的随机人有权获取这些值,否则他不会要求这些值。这不仅不是域的方式,实际上还在于域的方式。


在先前的17个答案中,这似乎并没有提供任何实质性的要点和解释
gna

1
其他答案讨论对象,接口和其他语言范例。很好,但这不是中心问题。Getter和Setter不仅不好,因为它们违反了一些编码约定,而且主要是因为您几乎不会在所表示的实际模型中遇到这种行为。这一点在有关最佳编码实践的讨论中往往会迷失。
Cormac Mulhall 2014年

如果不是set/get薪水,您认为薪资界面是什么?
Winston Ewert 2014年

1
如果它们围绕授权层包装并且仅限于某些外部实体,则它们不是获取程序和设置程序。我并不是说薪资部门没有任何改变薪水的方法,但是这种方法应该与公司内部的内部流程保持一致。例如,大多数公司根据员工合同(由经理授权)设置工资。如果员工的薪水发生变化,则应草拟新合同,经理必须签署该合同。因此,工资单应该期望合同对象和某些授权对象可以更改工资单
Cormac Mulhall 2014年

3
换句话说,set_salary(new_salary,employee_id)和authorized_salary_update(employee_contract,authorising_manager)之间有很大的区别。这一过程应在内部业务流程模型
科马克马尔霍尔

11

通常,吸气剂和吸气剂是个坏主意。如果字段在逻辑上不是接口的一部分,并且您将其设为私有,那很好。如果从逻辑上讲它是接口的一部分,并且您将其公开,那就很好。但是,如果您将其设为私有,然后通过提供一个getter和setter将其转回并再次有效地公开,则您将回到开始的地方,只是您的代码现在变得更加冗长和混乱。

显然,也有例外。在Java中,您可能需要使用接口。Java标准库具有向后兼容性的要求,因此其极端性足以超过正常的代码质量度量。甚至有可能您实际上是在处理传奇但罕见的情况,即很有可能以后可以用即时计算替换存储的字段而不会破坏接口。但是这些都是例外。Getter和Setter是需要特殊证明的反模式。


6
获取器和设置器意味着属性(而不是字段)是接口的一部分。假设getBirthDate()和SetBirthDate()取“ yyyy / mm / dd”,但存储的字段是自01/01/1000以来的天数。您可以将其更改为01/01/0001,getter和setter将使接口保持不变。尤其是因为“公共”意味着公开可丢弃,因此变量永远不应公开。始终使用setter验证传入的值,更新任何相关字段,根据需要进行转换等。请尝试使用getter以防内部存储发生更改。
安迪·坎菲尔德

@AndyCanfield可以说公众意味着公众可以丢垃圾。.只有当您对它进行不好的编程时才这样。如果您有一个setter并且错误的值被发送给它,那么您可能会抛出一个异常,您可以调用该程序来破坏程序,但是它会因错误而退出。此外,如果对象的用户可以给变量赋值,而cud则给定错误的值。值,那么您可以牢记这一点并对其进行测试。u可能会说“啊,但是如果我使用setter的话,我只需要进行1次测试”,但是也许您自己的代码给变量赋予了错误的值。所以,setter可能不会减少您的tests.n与测试,您的编不是“被破坏”
barlop 2015年

3

是直接访问字段还是通过方法访问字段并不重要。

类不变式(有用的)很重要。为了保护它们,我们有时有时无法从外部进行更改。例如。如果我们将Square的宽度和高度分开,则更改其中之一会使它变成不是Square的东西。因此,我们需要方法changeSide如果它是Rectangle,则可以有setter / public字段。但是setter比会测试其大于零是否更好。

在具体的语言中(例如Java)是我们需要这些方法(接口)的原因。方法的另一个原因是兼容性(源和二进制)。因此,添加它们然后考虑公共领域是否足够容易。

顺便说一句 我喜欢对公共final字段使用简单的不可变值持有类。


这几乎是唯一应避免的事情。在大多数语言中,从领域到财产/获取者/设定者都是一项重大突破。
MrDosu 2014年

2

您可能希望将内部结构更改为任何内容,同时保持接口相同。如果您的界面不变,则您编写的代码不会中断。您仍然可以根据需要更改内部结构。


1
我很惊讶这个答案如此之遥。使用Getter和Setter可以在不破坏现有API的情况下,在将来做更多的事情(例如,触发事件,进行更多的输入验证,进行内部记账)。即使使用非公开代码,您也需要更改的API更少,要更新的依赖项也更少,并且进行这些更新所引入的错误也更少。
AmadeusDrZaius

0

我的方法是-

当我期望稍后再使用数据时,有理由使用getter / setter。另外,如果发生更改,我经常将数据推送到getter / setter中。

如果是POD结构,则保留可用的插槽。

从更抽象的角度讲,问题是“谁来管理数据”,这取决于项目。


0

如果使用getter和setter方法感到复杂,那么问题可能出在语言上,而不是概念本身。

这是用Ruby编写的第二个示例中的代码:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

注意,它看起来很像Java中的第一个示例吗?如果将吸气剂和装夹器视为头等公民,那么它们就不是一件繁琐的事,而增加的灵活性有时则是真正的好处-例如,我们可以决定在新冰箱上返回奶酪的默认值:

class Fridge
  attr_accessor :cheese

  def cheese
    @cheese || 0
  end
end

当然,会有许多不应该公开暴露的变量。不必要地公开内部变量会使您的代码更糟,但是您几乎不能将其归咎于getter和setter。


这说得很漂亮。添加了生成器功能,因为我们希望在逐步构建配方的同时创建不可变的对象:也许,如果添加人造甜味剂,则无需添加糖。使用命名参数,甚至使用重载,也很难构建一种可以完成所有工作的方法。然后,当然Java没有命名参数,因此,人们使用Builder模式的另一个原因是。
YoYo

0

考虑一个Size封装宽度和高度的类。我可以通过使用构造函数消除二传手,但是这如何帮助我绘制一个矩形Size呢?宽度和高度不是该类的内部数据;它们是必须可供Size使用者使用的共享数据。

对象由行为和状态-或属性组成。如果没有暴露状态,那么只有行为是公开的。

没有状态,您将如何排序对象的集合?您将如何搜索对象的特定实例?如果仅使用构造函数,那么当对象的属性列表很长时会发生什么?

未经验证,不得使用任何方法参数。因此,懒惰地写:

setMyField(int myField){
    this.myField = myField;
}

如果确实以这种方式编写,那么您至少已准备好使用setter进行验证;它比公共领域要好-但仅此而已。但是至少您拥有一个一致的公共接口,您可以返回该接口并放入验证规则,而不会破坏客户的代码。

getter,setter,property,mutator称呼您想要的东西,但它们是必需的。


但是至少您拥有一个一致的公共接口,您可以返回该接口并放入验证规则,而不会破坏客户的代码。这不是真的。进一步添加验证规则很可能会“破坏客户的代码”。以一个int实例变量为例,假设您决定添加一个验证规则以仅接受非负值。问题在于您客户的代码可能已经依赖于将其设置为负值的可能性……
jub0bs 2015年

0

如果getter和setter违反了封装和真正的OO,那么我就很麻烦了。

我总是觉得一个对象代表着您需要它做的一切。

我刚写完一个用Java生成迷宫的程序,并且有一个表示“迷宫方块”的类。我在此类中的数据表示坐标,墙和布尔值等。

我必须有某种方式来更改/操作/访问该数据!没有getter和setter的情况下我该怎么办?Java不使用属性,因此将我在此类本地的所有数据都设置为public无疑是对封装和OO的违反。


6
这是一个问题:如果将所有这些字段公开,并且停止使用getter和setter,您的代码将如何不同?
Winston Ewert 2010年

从理论上讲,它不是。为什么还要在那时上课?任何事情都可以提出同样的观点。但是我下面的帖子似乎更优雅地讲了这一点。(getter和setter的办法值安全交给一个对象,或检索该对象的值。)邮政推移..
布莱恩·哈林顿

3
我要指出的是:那个海报最终同意了我的看法,并发布了另一个答案。本质上,使用getter和setter等同于直接操作数据。这样做并不能使您真正获得OOness。为了成为OO,您应该为数据提供更高级别的接口。
Winston Ewert 2010年

4
我认为最好的描述方法是说应该从外而内而不是由内而外设计接口。对象提供的接口应由对象的使用方式而不是对象的实现方式决定。因此,请勿编写吸气剂并允许外部对象解释数据。找出他们需要回答什么问题,并编写回答该问题的方法。不允许外部对象通过设置程序修改状态,不要发现它们需要执行哪种操作并编写执行该操作的方法。
Winston Ewert 2010年

2
在某些情况下,Getters和Setters是最好的方法。有时它们是您可以提供的最佳界面。但是,在许多情况下并非如此。在许多情况下,您只是将实现细节泄漏到其他类中。
温斯顿·埃韦特

-1

首先,我将评论两种类型的对象:价值和服务类型。

服务类型永远不应该有设置器,无论它们需要什么依赖关系都不能被获取。传递依赖关系的最佳方法是通过构造函数或工厂,这种方法从一开始就简单,完整地形成了所有实例。

值类型也应该是不可变的,另一方面,有时还不可行,例如ORM或其他一些映射(例如从小部件到对象)。在系统中从一层或一部分传递到另一层或另一部分的所有其他值类型应该是不可变的,并且不应具有setter。


我建议使用第三种类型:可变容器,其目的是保存一个或多个值。在许多情况下,不应期望容器具有太多的逻辑或验证方式。需要一种计算六个正整数的方法的代码可以将六个整数的容器传递给将结果存储在其中的方法。确保数字为正的责任通常应由调用者或被调用方法承担,而不应由容器承担。
超级猫

-1

Getter / Setter就像其他任何公共方法一样是合理的。理由主要是因为我们要提供一个与外界的接口,以定义我们的类与其他类的交互方式。我们这样做主要是因为我们要减少单独实体之间的耦合。减少耦合是一件好事,原因有很多:

  • 我可以从当前代码使用相同的类接口,尽管与此同时,该类添加了新方法(但保留了旧接口)
  • 可以更改类的内部表示形式而不会影响其使用方
  • 减少错误的机会:如果将内部数据设为私有,则可以肯定地知道外部代码不会与内部数据混淆


-4

我希望由公司开发的自动开发您的编程语言的IDE不会违反“对象定向原理”。

话虽如此,只要您有公共范围的变量,就可以使用getter和setter来达到目标​​。在我看来,这是极其面向对象的封装原理的必要元素-即使它看起来像很多垃圾代码。

使用它们的原因是,可能仍会进行适当的封装,只要您已经抽象掉了让让人摸索您的物体的物体感觉到您的物体的层,就可以在他甚至不知道正确的情况下将其取走?

可以这么说,一间分开的房子无法忍受,OO的一个租户不会自行打开,并且您不能同时进行封装和过早优化。


4
每次处理变量时,通过添加大量代码来调用setFoo()和getFoo()到底能获得什么呢?
温斯顿·埃韦特2010年

7
这是东西,我不认为您会得到封装。您仍在从其他地方操纵对象的状态。您只是在做更冗长的操作。要获得封装,您需要在拥有该值的类中集中对值进行操作。其他所有只是OO服装中的过程代码。(这不一定是错的,但这就是事实)。
Winston Ewert 2010年

2
封装的优势在于,与特定值相关的所有逻辑都在一个地方(或多或少)。我不能通过getter和setter来理解。因此,我认为我没有为他们带来很多好处。
温斯顿·埃韦特2010年

1
但是,我同意,我宁愿使用getter和setter编写代码,然后使用通过公共访问自由操作数据的代码。这样,我至少可以附加对传入数据的验证。
Winston Ewert 2010年

3
我当然不喜欢冗长。但是我的反对意见是真正的人,他们将变量设为私有,然后自由调用它们的getter和setter,最终几乎没有区别。如果您想在课堂上使用getter / setter,那很好。问题是为什么?我知道的主要好处是,您可以对数据进行一些验证以更早地捕获错误。当所有用于处理数据的代码都位于同一位置时,这没有多大用处。但这仍然会有所帮助。理想情况下,您使用的语言应支持属性并且可以避免冗长。
Winston Ewert 2010年

-4

我以为我们在这里会有更多平常的答案?

通常,getter和setter都非常面向对象(任何人说其他什么都不对,就这么简单)。

尽管您需要记住不要过度设计,但切勿进行不需要的吸气剂或吸气剂。任何其他类都不打算使用的信息,都不应有任何获取或设置方法。

虽然,您不公开字段而是使用getter和setter的原因主要是:

  • 使用字段无法进行正确的测试。在这方面,吸气剂/定阻剂要好得多。

  • 在实时编程设置中正确使用字段是不可能的。同步获取器/设置器是必经之路。

  • 界面主题(已经解释,所以我不会)。

  • 正确重用系统时,使用起来更容易。

  • 与使用getter / setter方法相比,要知道哪个类正在使用您的类以及要更改的内容要难得多。

因此,唯一的一次使用公共场所而不是使用getter / setter的情况是,您没有制定正式项目,而是只是出于娱乐目的而不会打扰getter / setter的人。


2
+1以提高可测试性。震惊的是没有人提到它。属性的另一个有用功能是调试它们。(在代码中插入断点比要求字段的硬件/内存断点要容易得多)。
Mark H

9
对吸气剂和吸气剂的异议是,它们经常被用来遵循OOP法律,而忽略了精神和意图。OOP表示您不应该让其他物体与您的内部物体混在一起。如果定义反正的getter和setter,那么最终会违反OOP。
Winston Ewert 2010年

6
我会同意,与简单的公共领域相比,getter和setter有很多好处。但是,我不清楚使用它们进行测试如何更好。你能解释一下吗?
温斯顿·埃韦特

4
@Skarion:我不同意你的回答。您似乎提出了两种选择:使用getter / setter或公开字段。但是还有第三种选择:既不使用getters / setter方法,也不使用字段!为什么需要测试内部字段的状态?您不应该测试对象的公共API吗?
Andres F.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.