封装在框架时代

And*_*ite 7 constructor frameworks encapsulation dependency-injection

在我以前的旧C++工作中,我们总是非常谨慎地封装成员变量,并且只在绝对必要时将它们作为属性公开.我们有真正特定的构造函数,确保在使用之前完全构造对象.

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

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

对这个方向有任何普遍关注吗?似乎封装开始变得不那么重要,有利于方便.

编辑:我知道你仍然可以仔细封装成员,但我只是觉得当你想要创建一些类时,你要么坐下来仔细考虑如何封装每个成员,或者只是将它作为属性公开,并担心以后如何初始化.这些日子似乎最简单的方法是将事物作为属性暴露,而不是那么小心.也许我只是错了,但这只是我的经验,特别是新的C#语言功能.

Hen*_*rik 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

我想,您必须注意的是偏离您从之前的工作中学到的良好原则,并确保您的域对象充满了封装在良好接口后面的业务逻辑,服务层也是如此。