首先,一些背景。在这个类库中,我有两个抽象层。底层实现了一个用于扫描内容的组件的插件架构(抱歉,不能更具体)。每个插件将以某种独特的方式进行扫描,但插件也可以根据其接受的内容类型而有所不同。由于各种原因,我不想通过插件接口公开泛型。因此,我最终得到了一个IScanner接口和一个派生接口,用于每种内容类型。
顶层是一个方便的包装器,接受包含各种部分的复合内容格式。不同的扫描程序将需要复合的不同部分,具体取决于他们感兴趣的内容类型。因此,我需要针对每个IScanner派生接口具有特定逻辑,以解析复合内容,查找所需的相关部分。
一种解决方法是简单地向IScanner添加另一个方法,并在每个插件中实现它。但是,两层设计的整个重点在于插件本身不需要知道复合格式。暴力解决方法是在上层进行类型测试和向下转换,但这些需要在未来添加对新内容类型的支持时仔细维护。在这种情况下,访问者模式也很尴尬,因为实际上只有一个访问者,但可访问类型的数量将随时间增加而增加(即-这些是访问者适用的相反条件)。此外,双重分派感觉像是过度设计,当我只想劫持IScanner的单一分派时!
如果我使用Objective-C,我会在每个IScanner派生接口上定义一个分类,并在那里添加parseContent方法。该类别将在上层中定义,因此插件不需要更改,同时避免了类型测试的需要。不幸的是,C#扩展方法无法工作,因为它们基本上是静态的(即-与调用站点使用的引用的编译时类型相关联,而不像Obj-C类别那样连接到动态分派)。更不用说,我必须使用C# 2.0,因此扩展方法甚至对我都不可用。:-P
那么,在C#中是否有一种干净简单的方法来解决这个问题,类似于如何使用Objective-C分类解决这个问题?
interface IScanner
{ // Nothing to see here...
}
interface IContentTypeAScanner : IScanner
{
void ScanTypeA(TypeA content);
}
interface IContentTypeBScanner : IScanner
{
void ScanTypeB(TypeB content);
}
class CompositeScanner
{
private readonly IScanner realScanner;
// C-tor omitted for brevity... It takes an IScanner that was created
// from an assembly-qualified type name using dynamic type loading.
// NOTE: Composite is defined outside my code and completely outside my control.
public void ScanComposite(Composite c)
{
// Solution I would like (imaginary syntax borrowed from Obj-C):
// [realScanner parseAndScanContentFrom: c];
// where parseAndScanContentFrom: is defined in a category for each
// interface derived from IScanner.
// Solution I am stuck with for now:
if (realScanner is IContentTypeAScanner)
{
(realScanner as IContentTypeAScanner).ScanTypeA(this.parseTypeA(c));
}
else if (realScanner is IContentTypeBScanner)
{
(realScanner as IContentTypeBScanner).ScanTypeB(this.parseTypeB(c));
}
else
{
throw new SomeKindOfException();
}
}
// Private parsing methods omitted for brevity...
}
编辑:为了澄清,我已经深思熟虑过这个设计了。我有很多原因,其中大部分我不能分享,来解释为什么它是这样的。我还没有接受任何答案,因为尽管有趣,它们回避了最初的问题。
事实是,在Obj-C中,我可以简单而优雅地解决这个问题。问题是,我能否在C#中使用相同的技术,如果可以,怎么做呢?我不介意寻找替代方案,但公平起见,这不是我提出的问题。:)