如何在C#中对接口进行多次调度?

pro*_*kor 5 c# oop design-patterns multiple-dispatch

假设我有以下类结构:

班级结构

Page,StaticPage并且DynamicPage接口是由客户来实现.它们根据页面类型(静态或动态)提供各种数据,用于通过页面呈现页面Renderer.这些接口可能有很多实现.

渲染

Renderer的render页面.此外,此接口可能有多个实现(针对不同的呈现技术).

RenderManager

这只是一个简单的外观,它应该根据给定的页面类型在提供的渲染器上调用相应的渲染方法.这就是谎言

问题

如何Renderer根据提供的页面类型确定在对象上调用哪个方法?

目前(不满意)的解决方案

目前我正在使用条件派遣:

void render(Page page, Renderer renderer) {
    if (page is StaticPage) {
        renderer.renderStaticPage(page);
    } else if (page is DynamicPage) {
        renderer.renderDynamicPage(page);
    } else {
        throw new Exception("page type not supported");
    }
}
Run Code Online (Sandbox Code Playgroud)

怎么了

这个解决方案的问题是每当我想添加另一个页面类型(即扩展Page界面)时,我也需要调整这个方法.实际上,这是应该使用面向对象语言中的多态(虚拟)方法,但在这种情况下,这不起作用(见下面的原因).

我考虑但拒绝的其他解决方案

  1. 抽象类而不是接口.这将在实现者的类型层次结构上放置一个不必要的约束:它们将不再能够扩展他们想要的任何类,而是强制扩展抽象StaticPageDynamicPage类,这很糟糕.

  2. dispatch(Renderer render)向接口添加方法并强制实现者根据页面类型调用渲染器对象上的相应方法.这显然很糟糕,因为实现者不应该关心渲染:他们只需要提供要渲染的数据.

那么,也许有一些模式或一些替代设计可能有助于这种情况?欢迎任何想法.:)

Ser*_*kiy 4

用于dynamic在运行时选择适当的方法重载:

public class RenderManager
{
    public void Render(IPage page, Renderer renderer)
    {
        try
        {
            renderer.RenderPage((dynamic)page);
        }
        catch (RuntimeBinderException ex)
        {
            throw new Exception("Page type not supported", ex);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

但动态类型当然会带来性能成本。优点 - 当添加新类型的页面时,您需要更改的只是渲染器 - 只需添加另一个重载方法。


另一种选择是访客。在这种情况下,每个页面都应该进行调度(似乎是第二种方法):

public interface IPage
{
    void Render(Renderer renderer);
}

public class StaticPage : IStaticPage
{
    public void Render(Renderer renderer)
    {
        renderer.RenderPage(this);
    }
}

public class RenderManager
{
    public void Render(IPage page, Renderer renderer)
    {
        page.Render(renderer);
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,页面“了解”渲染。并且在添加新页面时仍然应该修改渲染器。