【问题标题】:How do get set methods stop dependencies?get set 方法如何停止依赖?
【发布时间】:2021-04-07 03:52:37
【问题描述】:

所以我知道如果我们想改变一个类的实现细节,在类之外使用这些细节会在改变时导致错误,这就是我们将这些字段设置为私有的原因。但是,如果我们使用带有私有字段的 get set 方法,这不是做同样的事情吗?如果我决定我不希望我的班级有名称和用户名,只是一个名称,并且我删除了私有用户名字段,那么 get / set 方法将与此中断,它将导致使用这些方法的地方也打破。不管我们改变类的方法或字段,引用一个类不是依赖吗?那么 Get Set 方法的意义何在?它们如何阻止代码像这样中断?

【问题讨论】:

  • Get 和 set 方法是将对象的某些属性(值)公开给其使用者的工具。如果你想保持属性私有 - 不要使用 get 或 set 方法。
  • @IanMercer - 该文件似乎不适用于这个问题。相关,但不是 OP 所要求的。
  • 如果您决定不需要“用户名”的私有​​支持字段,您只需删除该字段。目前尚不清楚为什么您认为公开暴露的财产会同时神奇地消失……显然,如果您决定删除该财产,您将破坏兼容性,但这似乎不是问题所要讨论的。请edit帖子澄清帖子中的示例究竟应该说什么......

标签: c# oop encapsulation


【解决方案1】:

但是,如果我们使用带有私有字段的 get set 方法,这不是做同样的事情吗?

是的。可以说,是的。面向对象编程的最初想法,正如创造这个术语的艾伦·凯(Alan Kay)最初所想的那样,已经被扭曲了。 Alan Kay 表达了他对二传手的厌恶:

许多所谓的面向对象的语言都有设置器,当你在一个对象上有一个设置器时,你把它转回数据结构。

-- Alan Kay - 编程和扩展 (video)。


如果我们改变类的方法或字段,引用一个类不是依赖吗?

正确。如果您从另一个类中引用一个类,那么您的类是紧密耦合的。在这种情况下,一个类的更改将传播到另一个类。 无论更改是在公共字段、getter、setter 还是其他方面。

如果您使用接口或类似的间接方式,它们是松散耦合的。这种松散性使您有机会阻止更改的传播。你可以做也可以不做。

最后,如果您使用观察者模式或类似模式(例如事件或侦听器),您可以将类解耦。这在某种程度上是对 Alan Kay 最初设想的传递消息的想法的改进。


那么 Get Set 方法的意义何在?它们如何阻止代码像这样中断?

它们允许您更改类的内部表示。虽然常见的方法是让 setter 和 getter 对应于一个字段,但这并不一定是这样。 getter 可能返回一个常量,或者从多个字段中计算一个值。同样,setter 可能会更新多个字段(甚至什么也不做)。

拥有二传手的原因:

  • 它们为您提供了实施验证的机会。
  • 它们让您有机会提出“改变”的事件。
  • 它们可能需要与其他系统一起使用(例如,一些依赖注入框架,还有一些用户界面框架)。
  • 您需要更新多个字段以保持不变。 大概更新这些其他字段不会导致某些公共属性以意外方式更改值(也不要破坏single responsibility principle,但这应该很明显)。见Principle of least astonishment

getter 的原因:

  • 它们为您提供了实现延迟初始化的机会。
  • 它们让您有机会返回计算值。
  • 它们可能会使调试更容易。 考虑一些仅用于 DEBUG 构建的 getter。

如果你有公共字段,然后你决定需要我上面描述的任何东西,你可能想要更改为 getter 和 setter。另外,该更改需要重新编译使用它的代码(即使源相同,C# 属性也是如此)。这就是建议抢先执行此操作的原因,尤其是在代码库中(这样,如果库更改为需要这些更改的较新版本,则使用它的应用程序不必重新编译)。


这些是没有 getter 的原因:通常,getter 的存在是为了访问成员以调用其上的方法,这会导致非常尴尬的接口(请参阅Law of Demeter)。或者做出决定,这可能会导致Time-of-check to time-of-use 错误,这也意味着接口不是线程安全的。或者进行计算,如果类有自己的方法(Tell, Don't Ask),这通常会更好。

对于 setter 来说,除了封装不好的代码味道之外,它还可能表示意外的状态机。如果代码需要调用 setter(更改状态),为了确保在调用方法之前它具有预期的值,只需将其设置为参数(是的,即使您要在很多方法中重复该参数)。这样的接口很容易被误用,而且不是线程安全的。一般来说,避免任何接口设计,其中使用它的代码必须以它不强迫你的顺序调用事物(一个好的设计不会让你以导致无效状态的顺序调用事物(参见@987654327 @)。当然,不是每个合约都可以在接口中表示,其余的我们都有例外。)。

线程安全的就绪接口,是一种可以以线程安全的方式实现的接口。如果接口不是线程安全的,那么在使用它时避免线程问题的唯一方法是使用外部锁来包装对它的访问,而不管接口是如何实现的。通常是因为接口阻止合并读取和写入导致检查时间到使用时间错误或ABA problem


在适当的时候,公共领域也有价值。特别是对于性能以及与本机代码的互操作性。 您会发现,例如,游戏开发库中使用的 Vector 类型通常具有公共字段作为其坐标。


如您所见,拥有和不拥有 getter 和 setter 都可能有充分的理由。同样,拥有或不拥有公共字段都可能有充分的理由。另外,如果使用不当,任何一种情况都会出现问题。

我们有指导方针和“最佳做法”来避免这些陷阱。没有公共字段是一个很好的默认设置。并不是每个领域都需要 getter 和 setter。但是,您可以创建 getter 和 setter,并且可以将字段公开。如果你有充分的理由这样做。

如果您将每个字段都公开,您可能会遇到麻烦,阻碍封装。如果您为每个字段都创建 getter 和 setter,那也好不到哪里去。谨慎使用它们。

【讨论】:

  • 非常全面的答案。我要补充的一点是,getter/setter 习语只存在于语法违反Uniform Access Principle 的语言中,即字段与函数的语法不同的语言中。不幸的是,这种违规行为包括最流行的面向对象语言。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-07-31
  • 1970-01-01
  • 2013-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-23
相关资源
最近更新 更多