我有一个以前从未遇到过的奇怪的设计情况......如果我使用的是Objective-C,我会用类别来解决它,但我必须使用C# 2.0。
首先,一些背景。我在这个类库中有两个抽象层。底层实现了用于扫描内容的组件的插件架构(抱歉,不能比这更具体)。每个插件都会以某种独特的方式进行扫描,但插件也会因它们接受的内容类型而异。由于与本次讨论无关的各种原因,我不想通过插件接口公开泛型。因此,我最终得到了 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# 中使用相同的技术吗?如果可以,如何使用?我不介意寻找替代品,但公平地说,这不是我问的问题。:)