1

概述

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

  • 核心库定义了许多接口,并包含根据接口实现核心功能的类。这样,我希望将核心算法编写一次,但使其适用于任意数量的接口实现。
  • “实现”库使用第三方 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);

换句话说: anIDocument可以将其接收者设置为先前获得的ICorrespondent. 当SetRecipient被调用时,IDocument需要与 - 关联但不被 - 暴露的状态(对核心不重要的主键)的实现ICorrespondent,以便实际影响更改。

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

问题的根源似乎是接口专门为核心库的通用需求而设计,尽管它们实际上有两个具有不同需求的消费者:核心和产生它们的实现库。

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

4

1 回答 1

1

实际上,您想要做的是交叉铸造。

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

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

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();

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

这样做的好处是您不必绑定到特定的子类。任何同时实现ICorrespondent和的类IHasPrimaryKey都是合适的。

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

于 2013-05-16T13:46:20.093 回答