设计API以接受它生成的接口引用

时间:2012-12-10 22:34:12

标签: c# api interface abstraction

概述

我正在开发一个使用两个主要抽象级别的应用程序:

  • 核心库定义了许多接口,并包含根据接口实现核心功能的类。通过这种方式,我希望能够编写一次核心算法,但是它们适用于任意数量的接口实现。
  • “实现”库使用第三方SDK提供接口集的一个特定实现。最终将有不止一个这样的库;使用哪一个将由配置确定。

应用程序本身从SDK库中实例化类,并使用它们来满足核心库的依赖关系。

问题

我需要解决的问题通常是这样的:

// Algorithm in the core (interfaces are all implemented by the SDK library):
ICorrespondentRepository allCorrespondents = ...;
ICorrespondent correspondent = allCorrespondents.FindByName(...);
...
IDocumentRepository allDocuments = ...;
IDocument document = allDocuments.FindByTitle(...);

// Problem: Implementation needs state not exposed
// on ICorrespondent in order to do this:
document.SetRecipient(correspondent);

换句话说:IDocument可以将其收件人设置为之前获得的ICorrespondent。调用SetRecipient时,IDocument的实现需要状态(对核心不重要的主键)与 - ICorrespondent不相关,以便实际影响更改。

实现这一目标的一种方法是将ICorrespondent向下转换为SetRecipient内的实际实现类,但这感觉非常笨重。更糟糕的是保持从接口引用到内部状态的映射。

问题的根源似乎是接口专门用于满足核心库的通用需求,即使它们实际上有两个具有不同要求的消费者:核心和生成它们的实现库。 / p>

有没有更好的方法来重新设计这种要求?

1 个答案:

答案 0 :(得分:1)

实际上,你想要做的就是交叉投射。

您说IDocument的具体实施与ICorrespondent的所有子类不兼容,因此如果SetRecipient的实例,您对ICorrespondent的调用可能会合法地失败传递没有这个主键。拥有此主键是兼容子类的“特征”。

您可以使用的技术就是这个。定义一个界面。

internal interface IHasPrimaryKey {
    PrimaryKey GetPrimaryKey();
}

兼容的ICorrespondent类应该实现两个接口。

internal class CompatibleCorrespondent : ICorrespondent, IHasPrimaryKey {
    // ...
}

在这种情况下,SetRecipient应该尝试对方的交叉演员,看看它是否提供了必要的主键,否则就失败了。

var hasPrimaryKey = correspondent as IHasPrimaryKey;
if(hasPrimaryKey == null) {
    throw new InappropriateSubclassException();
}
// ...
var pk = hasPrimaryKey.GetPrimaryKey();

这是您可以从这种架构中获得的最强类型的解决方案。失败案例是合法的,因为类型系统并不能保证您在每种情况下都能获得主键。

这样做的好处是你没有绑定到特定的子类。任何同时实现ICorrespondentIHasPrimaryKey的类都是合适的。

当然,我会让您为特定代码找到更合适的名称。