How to do multiple dispatch on interface in C#?

932 views Asked by At

Suppose I have the following class structure:

Class structure

Page

The Page, StaticPage and DynamicPage interfaces are to be implemented by the clients. They provide various data depending on the page type (static or dynamic), which is used to render the page by the Renderer. There might be many implementations of these interfaces.

Renderer

The Renderers render the pages. Also, there might be multiple implementations of this interface (for different rendering techniques).

RenderManager

This is just a simple facade which is supposed to invoke the appropriate render method on the provided renderer depending on the given page type. And here lies

The problem

How to determine which method to invoke on the Renderer object depending on the provided page type?

Current (unsatisfactory) solution

Currently I am dispatching using a conditional:

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");
    }
}

What's wrong

The problem with this solution is that whenever I wish to add another page type (i.e. extending the Page interface) I need to adjust this method too. Actually, this is what polymorphic (virtual) methods in object oriented languages are supposed be used for, but in this case this doesn't work (see below why).

Other solutions I considered but rejected

  1. Abstract class instead of interface. This would place an unneccessary constraint on the type hierarchy of the implementors: they would no longer be able to extend any class they want and would instead be forced to extend the abstract StaticPage or DynamicPage class, which sucks.

  2. Add a dispatch(Renderer render) method to the interface and force the implementors to call the appropriate method on the renderer object depending on the page type. This obviously sucks, because the implementors should not care about the rendering: they just need to provide data which is to be rendered.

So, maybe there is some pattern or some alternative design which might help in this situation? Any ideas are welcome. :)

1

There are 1 answers

0
Sergey Berezovskiy On BEST ANSWER

Use dynamic to select appropriate method overload at runtime:

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

But of course dynamic typing has performance costs. Benefits - when new type of page added, all you need to change is renderer - just add another overloaded method.


Another option is visitor. In this case each page should do dispatching (seems like your second approach):

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

In this case page 'knows' about rendering. And you still should modify renderer when adding new pages.