7

在我以前的 C++ 工作中,我们总是非常小心地封装成员变量,并且仅在绝对必要时才将它们公开为属性。我们会有非常具体的构造函数来确保您在使用对象之前完全构造它。

这些天来,使用 ORM 框架、依赖注入、序列化等,看起来你最好只依赖默认构造函数并在属性中公开你的类的所有内容,这样你就可以注入东西,或者构建和填充对象更动态。

在 C# 中,对象初始化器更进一步,它使您能够基本上定义自己的构造函数。(我知道对象初始化器并不是真正的自定义构造器,但我希望你明白我的意思。)

这个方向有什么普遍的担忧吗?为了方便起见,封装似乎开始变得不那么重要了。

编辑:我知道您仍然可以仔细封装成员,但我只是觉得当您尝试创建一些类时,您要么必须坐下来仔细考虑如何封装每个成员,要么将其公开为属性,并担心它以后如何初始化。现在似乎最简单的方法是将事物公开为属性,而不是那么小心。也许我完全错了,但这只是我的经验,尤其是新的 C# 语言功能。

4

3 回答 3

3

我不同意你的结论。有很多很好的方法可以将上述所有技术封装在 c# 中,以保持良好的软件编码实践。我还要说这取决于您正在查看谁的技术演示,但最终归结为减少对象的状态空间,以便您可以确保它们始终保持其不变量。

对象关系框架为例;它们中的大多数允许您指定它们将如何水合实体;例如 NHibernate 允许你这么说access="property"access="field.camelcase"类似的。这允许您封装您的属性。

依赖注入适用于您拥有的其他类型,主要是那些不是实体的类型,即使您可以以一些非常好的方式结合 AOP+ORM+IOC 来改善这些事物的状态。如果你正在构建一个数据驱动的应用程序,我猜你就是这样,因为你在谈论 ORM,所以 IoC 通常用于域实体之上的层。

它们(“它们”是应用程序和域服务以及程序的其他内在类)暴露了它们的依赖关系,但实际上可以在比以前更好的隔离中封装和测试,因为按合同设计/按接口设计的范例您在基于模拟的测试(与 IoC 结合)中模拟依赖项时经常使用它,这将使您转向类即组件“语义”。我的意思是:每个类,当使用上述构建时,将被更好地封装。

为 urig 更新:这对于公开具体依赖项都适用并暴露接口。首先关于接口:我上面暗示的是,服务和其他具有依赖关系的应用程序类可以使用 OOP 依赖于契约/接口而不是特定的实现。在 C/C++ 和更早的语言中,没有接口,抽象类只能走这么远。接口允许您将不同的运行时实例绑定到同一个接口,而不必担心泄漏内部状态,这是您在抽象和封装时试图摆脱的。使用抽象类你仍然可以提供一个类实现,只是你不能实例化它,但是继承者仍然需要知道你的实现中的不变量,这可能会弄乱状态。

其次,关于作为属性的具体类:您必须警惕哪些类型的类型;)您作为属性公开。假设您的实例中有一个列表;然后不要将 IList 公开为属性;这可能会泄漏,您不能保证界面的使用者不会添加或删除您所依赖的东西;而是公开 IEnumerable 之类的内容并返回 List 的副本,或者更好的是,将其作为方法执行: public IEnumerable MyCollection { get { return _List.Enum(); } } 并且您可以 100% 确定获得性能和封装。没有人可以添加或删除该 IEnumerable,您仍然不必执行昂贵的数组复制。对应的辅助方法:

static class Ext {
    public static IEnumerable<T> Enum<T>(this IEnumerable<T> inner) {
        foreach (var item in inner) yield return item;
    }
}

因此,虽然在创建重载的等于运算符/方法时无法获得 100% 的封装,但您可以接近您的公共接口。

您还可以使用基于 Spec# 构建的 .Net 4.0 的新功能来验证我上面谈到的合约。

序列化将永远存在并且已经存在了很长时间。以前,在 Internet 区域之前,它用于将对象图保存到磁盘以供以后检索,现在它用于 Web 服务、复制语义以及将数据传递到例如浏览器时。如果您将一些 [NonSerialized] 属性或等效项放在正确的字段上,这不一定会破坏封装。

对象初始化器与构造器不同,它们只是折叠几行代码的一种方式。{} 中的值/实例在所有构造函数都运行之前不会被分配,因此原则上它与不使用对象初始化器相同。

我想,你需要注意的是偏离了你从以前的工作中学到的好的原则,并确保你的领域对象充满了封装在良好接口后面的业务逻辑,并且同样适用于你的服务层。

于 2009-02-18T05:39:40.677 回答
1

私人成员仍然非常重要。控制对内部对象数据的访问总是好的,不应被忽视。

很多时候,我发现私有方法太过分了。大多数情况下,如果您正在做的工作足够重要而需要突破,您可以通过以下方式重构它:a) 私有方法是微不足道的,或者 b) 是其他函数的一个组成部分。

此外,在单元测试中,拥有许多私有方法使得单元测试变得非常困难。有一些方法可以解决这个问题(让测试对象成为朋友等),但会增加困难。

不过,我不会完全打折私有方法。任何时候,只要有重要的内部算法在类之外真的没有意义,就没有理由公开这些方法。

于 2009-02-18T05:42:22.213 回答
0

我认为封装仍然很重要,它在库中的帮助比恕我直言。您可以创建一个执行 X 的库,但您不需要每个人都知道 X 是如何创建的。如果你想更具体地创建它来混淆你创建 X 的方式。我学习封装的方式,我还记得你应该总是将你的变量定义为私有的,以保护它们免受数据攻击。防止黑客破坏您的代码并访问他们不应该使用的变量。

于 2009-02-18T05:36:02.260 回答