【问题标题】:How can I prevent a leaky abstraction?如何防止泄漏抽象?
【发布时间】:2013-11-02 05:22:29
【问题描述】:

我正在编写一个 Active Directory 包装器,试图遵循 SOLID 和其他最佳实践。接口当前为“IActiveDirectory”。

我现在遇到的问题是实现 ActiveDirectory 必须实现 IDisposable 以处理在此包装器中创建和处理的一些资源,我不确定在尝试对接口进行编码时如何解决这个问题,等等...我不想创建一个泄漏的抽象(即用 IDisposable 装饰 IActiveDirectory。)由于底层依赖。

我目前有一个工厂,以便 IActiveDirectory 的消费者可以按需创建一个工厂,但我需要一种干净、方便的方式让消费者发出信号,表明资源已完成。

如何在不泄露资源包装抽象的情况下为消费者提供合同(即接口)?我应该只公开没有接口的实现吗?有没有办法让我的消费者或我的 DI 容器管理此服务的生命周期?

【问题讨论】:

  • “我需要一种干净、方便的方式让消费者发出信号,表明它已使用资源。” -- 这正是IDisposable 的含义。你不能抽象出一切
  • 是的,我知道这正是 IDisposable 的含义,我将它放在一个具体的类上没有问题。但是,我确实不喜欢将它放在接口上,因为我正在创建一个泄漏的抽象,据此我假设所有实现都需要一个 Dispose 方法调用,这违反了 Liskov。您认为只公开没有接口的混凝土会更好吗?
  • 在 LSP 和其他标准下,Dispose 什么都不做是完全有效的。将它放在接口上并不意味着假设所有实现都需要处理(任何不能简单地添加void Dispose() {} 的实现),它意味着假设 some 实现需要处理(这是真的)。​​
  • @delnan 我仍然认为这违反了 Liskov 正是因为接口正在弯曲到至少一个具体的需求,并且至少有一个实现可能不需要“需要”接口要求什么。尽管如此,我承认即使是违规行为,在实践中也不是什么大问题,因为该方法是无效的,并且按照惯例,在任何用例中都不应该抛出/导致异常或意外。
  • @delnan IMO liskov 违规行为通常应该被认真对待,因为它们暗示重构为更清洁/更可维护的代码,但我认为在这种情况下理论和实践无法收敛到解决方案。

标签: c# dependencies code-injection abstraction


【解决方案1】:

一方面,处置资源的责任落在获得资源的人身上。您的客户端不会创建 ActiveDirectory,工厂会创建 - 从概念上讲,工厂必须处理 ActiveDirectory。

这很难,但可以实现。一个示例是 Web 应用程序,您可以将工厂范围限制为请求的范围,并在请求完成时使用活动目录安全地处理它。另一个例子是当你的实例由知道如何处理 IDisposable 的 IoC 容器管理时(NInject 有一些棘手的技巧可以做到这一点)。

然后,如果这不适用于您,您应该承认通用解决方案不是性能最高的(这一点也不奇怪),如果您仍然想要抽象,您可以创建额外的抽象 IActiveDirectorySession明确表示与 AD 的通信会话,并且至少出于非常自然的原因必须实现 IDisposable。

【讨论】:

  • 我的想法完全正确。我已经使用环境上下文模式创建了一个准会话,通过该准会话,在服务的保护伞下创建的任何对象都被“注册”以在调用上下文的 Dispose 时进行处置。在这种情况下,使用 IoC 管理生命周期几乎是不可能的,因为我认为在需要很久之前打开“会话”并在需要很久之后关闭它是不可接受的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-15
  • 2020-10-20
  • 2011-09-30
  • 2010-12-20
  • 2022-11-09
相关资源
最近更新 更多