C# 设计API以接受它生成的接口引用 概述
我正在开发一个应用程序,它使用两个主要的抽象级别:C# 设计API以接受它生成的接口引用 概述,c#,api,interface,abstraction,C#,Api,Interface,Abstraction,我正在开发一个应用程序,它使用两个主要的抽象级别: 核心库定义了许多接口,并包含实现接口核心功能的类。通过这种方式,我希望只编写一次核心算法,但使它们适用于任何数量的接口实现 “实现”库使用第三方SDK提供一组接口的特定实现。最终将有不止一个这样的图书馆;使用哪一个将由配置决定 应用程序本身实例化SDK库中的类,并使用它们来满足核心库的依赖关系 问题 我需要解决的问题通常如下所示: // Algorithm in the core (interfaces are all implemente
- 核心库定义了许多接口,并包含实现接口核心功能的类。通过这种方式,我希望只编写一次核心算法,但使它们适用于任何数量的接口实现
- “实现”库使用第三方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);
换句话说,i文档
可以将其收件人设置为以前获得的i响应
。调用SetRecipient
时,IDocument
的实现需要与ICorrespondent
关联但未公开的状态(对核心不重要的主键),以便实际实现更改
一种方法是向下转换ICorrespondent
到SetRecipient
中的实际实现类,但这感觉非常笨拙。更糟糕的是保持从接口引用到内部状态的映射
问题的根源似乎在于接口的设计完全是为了满足核心库的一般需求,尽管它们实际上有两个具有不同需求的消费者:核心和产生它们的实现库
有没有更好的方法来重新设计这种需求?实际上,您想要做的是交叉转换 您说,
IDocument
的特定实现与ICorrespondent
的所有子类都不兼容,因此如果传递的ICorrespondent
实例没有此主键,您对SetRecipient
的调用可能会合法地失败。拥有这个主键是兼容子类的一个“特征”
你可以使用的技术就是这个。定义一个接口
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();
这是从这种体系结构中可以得到的最强类型的解决方案。失败案例是合法的,因为typesystem不能保证您在任何情况下都能获得主键
这样做的好处是您不必绑定到特定的子类。任何同时实现ICorrespondent
和IHasPrimaryKey
的类都是合适的
当然,我会让您为特定代码找到更合适的名称。实际上,您要做的是交叉转换 您说,
IDocument
的特定实现与ICorrespondent
的所有子类都不兼容,因此如果传递的ICorrespondent
实例没有此主键,您对SetRecipient
的调用可能会合法地失败。拥有这个主键是兼容子类的一个“特征”
你可以使用的技术就是这个。定义一个接口
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();
这是从这种体系结构中可以得到的最强类型的解决方案。失败案例是合法的,因为typesystem不能保证您在任何情况下都能获得主键
这样做的好处是您不必绑定到特定的子类。任何同时实现ICorrespondent
和IHasPrimaryKey
的类都是合适的
当然,我会让您为您的特定代码找到更合适的名称。您不能在
ICorrespondent
公开的其他类型的接口中总结该状态吗?我想你还是得在某个地方沮丧,但这是并行抽象的代价。@siride谢谢你的建议。我考虑过这一点,但考虑到状态是特定于实现的,它似乎相当于向下转换到类。它仍然会将外部持久引用的问题分离到它自己的接口中。@siride按照您建议的方式实现了它,我可以看到这样做的好处。将状态封装在一个(内部)接口中有助于描述例如SetRecipient
方法的职责,相反,向下转换到实现类将使方法能够访问许多它不需要的东西。您不能将该状态包装到由ICorrespondent
公开的某种类型的其他接口中吗?我想你还是得在某个地方沮丧,但这是并行抽象的代价。@siride谢谢你的建议。我考虑过这一点,但考虑到状态是特定于实现的,它似乎相当于向下转换到类。它仍然会将外部持久引用的问题分离到它自己的接口中。@siride按照您建议的方式实现了它,我可以看到这样做的好处。将状态封装在(内部)接口中有助于描述例如SetRecipient
方法的职责,而向下转换到实现类将使该方法能够访问许多它不需要的东西。感谢您的回答!这正是我最终要做的