oop - 为什么使用getters 和setters?

  显示原文与译文双语对照的内容

于那些variables,相关使用getter和 setter - 那只获取和设置的优点是什么- 而不是简单地使用公共字段?

如果getter和setter的作用不仅仅是简单的获取/设置,我可以很快地计算出这一点,但我不是 100%清楚:

 
public String foo;

 

比以下情况更差:


private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

前者需要较少的样板代码。


在视点的javaweb dev, 编译列表中上移对我在这里似乎地顶部的获奖者, :

  1. 当你意识到你需要做的不仅仅是设置和获取值,你也不必改变代码库中的每个文件。
  2. 你可以在这里执行验证。
  3. 你可以更改正在设置的值。
  4. 你可以隐藏内部表示。 getAddress() 实际上可以为你获取几个字段。
  5. 你已经将公共接口与工作表中的更改隔离。
  6. 一些库需要这个。反射,序列化,mock 对象。
  7. 继承此类,你可以重写默认功能。
  8. 你可以为getter和setter具有不同的访问级别。
  9. 延迟加载。
  10. 人们很容易告诉你没有使用 python 。
时间:

公共字段不比只返回字段并分配给它的getter/setter对更糟糕。 首先,很明显( 在大多数语言中) 没有功能差异。 任何差异都必须在其他因素中,比如可维护性或者可读性。

getter/setter对的oft-mentioned优势,不是。 有一个声明,你可以更改实现,你的客户端不必重新编译。 假定,setter允许你稍后添加类似验证的功能,你的客户端甚至不需要知道它。 但是,添加相应的验证来一个设置器是一个更改到它的前提条件,上面的合同,这就是的冲突,简单来讲,"你可以在这里放入任何东西,以后你可以从getter获取同样的东西"。

现在,你打破了契约,改变代码库中的每个文件是你应该做的,而不是避免。 如果你避免了它,你就会假设所有代码假定这些方法的契约是不同的。

如果不应该是该协议,那么接口允许客户端将对象置于无效状态。 真是的相反的封装如果该字段可以不是真的被设置为开始,而不是验证怎么会在那里从一开始有线索?

同样的参数也适用于这些 pass-through getter/setter对的其他假设: 如果以后决定更改设置的值,则会破坏合同。 如果你在派生类中重写默认功能,而不是几个无害的修改,那么你将破坏基类的契约。 这违背了Liskov可以替代性原则,它被认为是面向对象的原则之一。

如果一个类包含了每个字段的这些哑的getter和 setter,那么它是一个没有任何变量的类,没有契约 。 那真的是object-oriented设计? 如果所有的类都是那些getter和 setter,它只是一个愚蠢的数据持有者,而哑数据持有者应该像哑数据持有者:


class Foo {
public:
 int DaysLeft;
 int ContestantNumber;
};

将 pass-through getter/setter对添加到此类中不会添加任何值。 其他类应该提供有意义的操作,而不仅仅是字段已经提供的操作。 这就是你如何定义和维护有用的不变量。

的客户:"我该如何处理此类的对象"?
设计器:"你可以读写多个变量。"
客户:"哦。。酷,我猜"?

不存在有原因要使用getter和 setter,但是如果这些原因,使得获取方法/设置方法偶的名义假封装神,不是件好事。 使getter或者setter包含的有效原因包括经常提到的可能的更改,比如验证或者不同的内部表示。 或者,这个值应该是客户端可读的,但不是可以写的( 例如读取字典的大小),所以一个简单的getter是一个不错的选择。 但是当你做出选择时,这些理由应该存在,而不仅仅是你以后可能想要的东西。 这是 YAGNI ( ,你不需要 )的一个实例。

很多人谈论了getter和setter的优点,但我想扮演魔鬼的角色。 现在我正在调试一个非常大的程序,程序员决定让所有的东西都成为getter和 setter 。 这看起来不错,但它是一个reverse-engineering的噩梦。

假设你正在查看数百行代码,你遇到了以下问题:

 
person.name ="Joe";

 

这是一个非常简单的代码,直到你意识到它是一个 setter 。 现在,你跟踪一个给定器和发现它还设置人。firstname 。人。姓氏。人。ishuman,人。hasreallycommonfirstname,并调用 person.update(), 中间,这样查询出来到数据库以及 等等 哦,就是在那里你的内存泄漏出现可能。

首先理解局部代码是很重要的一个重要属性,getter和setter往往会破坏。 这就是为什么我尽量避免它们,当我使用它们时最小化它们。

有很多原因。我最喜欢的是当你需要改变行为或者调整你可以设置的变量时。 例如假设你有一个 setSpeed(int speed) 方法。 但你希望只能设置 100的最大速度。 你将执行以下操作:


public void setSpeed(int speed) {
 if ( speed> 100 ) {
 this.speed = 100;
 } else {
 this.speed = speed;
 }
}

如果你的代码中的任何地方都是公共字段,然后你意识到你需要上面的需求? 查找公共字段的每个用法,而不是仅仅修改你的setter 。

我的2美分:)

访问器和转换器的一个优点是你可以执行验证。

例如如果 foo 是公共的,我可以轻松地将它设置为 null,然后其他人可以尝试调用对象上的方法。 但它已经不存在了 ! 使用 setFoo 方法,我可以确保 foo 从未被设置为 null

accessor方法和mutator方法也允许使用封装的值一旦它的设置- 如果你不应看到( 也许它在构造函数中被设置,然后被方法使用,但是不应该被改变) 上,它将永远不会被看到了出来。 但是如果允许其他类查看或者更改它,你可以提供适当的访问器和/或者赋值器。

取决于你的语言。你已经标记了这个"object-oriented"而不是"爪哇",所以我想指出chssply76的答案是 language-dependent 。 例如在 python 中,没有理由使用getter和 setter 。 如果你需要更改行为,你可以使用一个属性,它在基本属性访问周围包装一个getter和 setter 。 像这样:


class Simple(object):
 def _get_value(self):
 return self._value -1

 def _set_value(self, new_value):
 self._value = new_value + 1

 def _del_value(self):
 self.old_values.append(self._value)
 del self._value

 value = property(_get_value, _set_value, _del_value)

...