14

这是一个非常简单的问题,我仍然很不安:

为什么现在广泛接受一个类通过访问器方法返回对其私有成员的引用?这不是完全破坏了封装原则吗?如果可以,那为什么不把会员公开!?

public class EncapsulationViolator
{
  private object abuseMe;

  public object AbuseMe 
  {
    get { return abuseMe; }
  }
}

编辑我正在考虑的情况是这样的

EncapsulationViolator ev = new EncapsulationViolator();

object o = ev.AbuseMe;

o.SetValue(newValue);

现在 ev 的状态通过传递性发生了变化,因为它的成员 abuseMe 的状态发生了变化。

在 DDD 的上下文中,如果对象是聚合根,这是不行的。我引用

只允许外部对象保存对根的引用。对内部成员的瞬态引用只能在单个操作中使用。因为根控制着访问,所以它不会被内部结构的改变弄得措手不及。

[领域驱动设计,埃里克·埃文斯]

... 二传手 schmetters ...

4

6 回答 6

12

您将 C++ 术语“引用”与 C#按值(引用的)传递对象的事实混为一谈。

在这种情况下,getter 的调用者AbuseMe不能换出私有字段abuseMe。因此,没有违反封装。

EncapsulationViolator x = new EncapsulationViolator();
object y = x.AbuseMe;
y = 17; // I have not changed x.AbuseMe

Debug.Assert(y != x.AbuseMe); // Passes!

此外,属性 getter 和 setter 允许对私有字段进行适当的封装,并且在功能上与将它们实现为方法相同(实际上它们由编译器实现为方法)。

返回私有变量可能会破坏封装的一种情况是,当您返回对数组的引用时:

class X
{
    private int[] amazing = new int[10];

    public int[] Amazing { get { return this.amazing; } }
}

X a = new X();
int[] x = a.Amazing;
int[] y = a.Amazing;

x[2] = 9;
Debug.Assert(x[2] != y[2]); // Fails!
于 2012-07-18T15:10:17.097 回答
10

这取决于成员是什么类型的对象。例如,如果它是一个字符串,那么它是不可变的,因此您无法更改该字符串。

如果是可变对象,可以从类外改变对象的内容,但不能替换对象本身。

如果不能从类外部更改对象,则 getter 应返回对象的不可变版本。

如果你做错了,模式可能会破坏封装,但如果做得正确,封装是完整的。

于 2012-07-18T15:08:28.723 回答
8

我认为它不会破坏封装。类仍然决定返回值的AbuseMe来源。它可能来自不同的成员,也可能每次都重新创建或复制。

关键是该类决定它允许用户对该成员做什么(获取/设置或两者以及它们的可见性),它可以执行验证并防止设置无效值,并且用户不需要知道该值在哪里来自。

此外,如果您想向 get/set 方法添加自定义逻辑,您可以这样做而不会破坏与其他程序集的兼容性。

于 2012-07-18T15:04:43.653 回答
1

IMO - 这里有太多的答案正在推广 getter/setter。Getter/setter 非常适合程序代码,您要么进行一些计算并设置结果,要么获取值并做出决定。

OO 编程中一个众所周知的原则是Tell don't ask基本上是说您不应该询问其内部状态的对象来做出决定。

话虽如此,我自己使用访问器/属性。但是,如果可能,我会尽量避免它们。

于 2012-07-21T18:36:35.663 回答
1

getter 和 setter 的重点是专门强制封装。关键是您不直接访问该对象,而是强制它由您定义的函数访问。getter 和 setter 是封装。如果您决定只返回对象,那是您的事,但您不允许在不点击 getter 的情况下直接访问。

阅读: http ://en.wikipedia.org/wiki/Mutator_method

于 2012-07-18T15:11:00.080 回答
1

这只是语法糖。它与 Java 的 getXXX 和 setXXX 方法没有什么不同。

于 2012-07-18T15:05:30.627 回答