将第三方组件(如MS企业库或Log4net)进行包装是否是一种好的实践?

10

这更像是一个良好的实践问题。我想提供不同的通用库,如日志记录、缓存等。这些库包括许多第三方库,如MS企业库、log4Net、NCache等。

我想知道直接使用这些库是否是一个好的实践,或者创建每个服务的包装器,并使用DI将该服务注入代码中会更好。

敬礼

8个回答

9
这是主观的,但也取决于库的使用。例如,log4net或NHibernate具有严格的基于接口的API。不需要包装它们。如果您没有任何接口,则其他库将使您的类难以测试。因此,编写干净的接口可能是明智的选择。
有时候,只允许代码的一小部分访问API(例如NHibernate或GUI库)是一个好建议。(注意:这不是包装,而是构建抽象层。)另一方面,减少对log4net调用的访问并没有意义,因为这将在整个应用程序中使用。 log4net可能是包装过度的一个很好的例子。有些人患有“wrappitis”,这是一种反模式(有时称为将羊毛包裹在棉花里),只会产生更多的工作。Log4net具有如此简单的API,并且高度可定制,他们使其比您的包装器更好。
你会发现包装库并不能让你轻松地与其他产品交换。升级到新版本也不会更容易,相反你需要为无用的包装更新它。

我认为Log4Net是一个非常简单的API,但是无法从log4net中提取所有接口并将其提供给开发人员,以便他们只能使用接口进行编码。没有包装器会使升级库的版本变得非常困难,特别是如果有数百个应用程序使用。还有其他解决方案吗? - rauts
@rauts:不太确定你的意思。使用log4net时,你已经在使用接口。当他们决定更改时,会有原因,他们会高度考虑兼容性,并且只有在确实非常值得的情况下才会打破它。如果您决定使用导致接口变化如此之大的新功能或概念,您的包装器将无法支持此功能,而适应您的包装器将导致相同的更改。存在着概念上的更改和依赖关系,您无法通过编写包装器来“编码消除”它们。 - Stefan Steinegger
@stefan: 如果您决定不支持新功能,则可以保持您的封装器不变。或者,如果有破坏性的接口更改,您可能能够在您的封装器中处理它,以避免破坏其他100个应用程序。当然,如果您想使用新功能,则您的观点是100%准确的,您无法将它们编码消除。 - Randy Levy
@Tuzo:如果你决定不使用新功能,那么你就不需要升级到新版本。如果可以将新接口映射到旧接口,那么它已经可以在产品中使用了,除非编写它的人太蠢了。因此,你免费获得破坏性变化的集成的机会几乎为零,而每次工作量增加的机会几乎为百分之百。我所知道的产品都非常关注兼容性,因为如果他们不这样做,没有人会使用或更新它,这将是自杀行为。 - Stefan Steinegger
2
+1 for "wrappitis"。谷歌搜索只会出现一个结果 - 这个页面 :) - Ohad Schneider

4
如果您想能够交换这些概念的实现,创建一个包装器是正确的方法。
对于日志记录,已经有类似 Common.Logging 的东西可用。

2
使用包装接口确实使单元测试更容易,但同样重要的是,它使得使用模拟对象成为可能。
例如,PRISM 框架适用于 Silverlight 和 WPF 定义了一个名为 Log 的简单方法的 ILoggerFacade 接口。使用这个概念,以下是我如何在单元测试中定义一个模拟记录器(使用 Moq):
var loggerMock = new Mock<ILoggerFacade>(MockBehavior.Loose);

loggerMock.Setup(lg => lg.Log(It.IsAny<string>(), It.IsAny<Category>(), It.IsAny<Priority>()))
  .Callback((string s, Category c, Priority p) => Debug.Write(string.Format("**** {0}", s)));

稍后您可以通过构造函数或属性将loggerMock.Object传递给被测试的对象,或者配置使用它的依赖注入器。

我从未真正理解这个论点,因为可以模拟抽象类、具体类以及接口。我同意最好模拟接口。 - Jamie Ide

2

看起来你正在考虑将日志记录实现封装起来,然后与不同的团队共享。基于此,以下是一些优缺点。

封装的优点

  • 可以从实现中抽象出接口和依赖项。这提供了一定程度的保护,以防实现库中出现破坏性变化。
  • 可以使标准执行更容易,并对齐不同项目的实现。

封装的缺点

  • 需要额外的开发工作。
  • 可能需要额外的文档工作(如何使用新库)。
  • 包装代码中的错误可能比成熟库更多。(部署您的错误修复可能会很麻烦!)
  • 开发人员需要学习新的库(即使非常简单)。
  • 有时候很难封装整个库以避免泄漏实现接口。这些类型的包装器类通常除了混淆之外没有任何价值。例如,MyDbCommand类包装了一些其他的DbCommand类。

我之前曾经封装过企业库的一部分,但我认为它并没有增加太多价值。我认为你最好:

  • 记录最佳实践和用法
  • 提供参考实现
  • 验证合规性(代码审查等)

1

这更多是一个主观问题,但在我看来,将任何应用程序/库特定的使用包装到具有深思熟虑的接口的服务模型设计中是一件好事,这样您就可以轻松地使用 DI,以后如果您需要从 EntLib 数据应用程序块切换到 NHibernate,您不需要重新架构整个应用程序。


1
通常我会创建一个“helper”或“service”类,以静态方式调用这些库的常见功能。
我不知道直接引用/调用它们是否存在很大风险,因为它们肯定是成熟的项目(至少EntLib和Log4Net),但设置一个包装器可以使你免于版本更改的混乱等问题,并为未来提供更多选项,成本相对较低。

0

个人认为最好使用一个封装器(wrapper),因为这些是你不希望在运行单元测试时运行的东西(假设你也在进行单元测试)。


0
如果现在或将来替换实现是一个要求,那么
否则不是
在这里,定义应用程序将用于所有日志记录/企业/...目的的接口是核心工作。编写包装器只是一种使编译器强制使用此接口而不是实际实现的方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接