views:

112

answers:

3

I've inherited a code base that makes heavy use of switch statements (C#, FWIW) to drive some logic. It's a multi-tenant web app where one set of switch statements pertains to how content displays and the other pertains to links to features, at least in most cases.

I have an opportunity to refactor, so I'm taking the content-related switch statements and planning to create a base Content class with a Render() method and then setup specific children for types that need to override the base implementation.

But there are rare cases where rendering HTML is based on content type and the specific tenant and I don't want to have all the new Render() methods with the exact same problem I started with. Is there a pattern that can help with this type of situation?

I see a few other similar questions on SO, but I'm not sure how to apply the answers here.

+1  A: 

Sorry, my OOP design skills are a bit rusty. You can thank Lisp macros for that ;]

Maybe using two factory classes would work?

The Content() class constructor accepts variables $content_type and $feature. The constructor then returns an instance of a Content() subclass that contains a property initialized to an instance of a Feature() subclass generated by the Feature() factory class using the value of $feature to select the appropriate subclass. When a Content() subclass render() method is called, this render() method could include a call to a method within the Feature() factory that can supplement or further process the data generated by the render() method. In cases where further processing via Feature() methods are not necessary, you could simply leave out these calls.

jkndrkn
The OP mentions that he's working in C#. I've edited the tags to emphasize this.
Wim Coenen
A: 

If at all possible you could abstract the rare differences that occur based on content type and then create a set of Renders per tenant with helper objects per content type. That turns the problem into an n+m problem instead of an n*m. Where n and m are the number of users and content-types respectively. Then you could construct the renderer like this:

// uses a DefaultContentTypeDelegate
IRenderer tenantADefault = new TenantARenderer();
// specify a specific content type helper object
IRenderer tenantAType1 = new TenantARender(new ContentType1Delegate());

Then your render methods for each tenant's renderer could implement the Template Method Pattern and once in a while call some method on the contentTypeDelegate.

class TenantARenderer : IRenderer {
    ...
    public render() {
        // do a bunch of tenant A specific stuff
        this.contentTypeDelegate.doSomeContentTypeSpecificStuff();
        // do some more tenant A stuff
        this.contentTypeDelegate.doSomeOtherContentTypeSpecificStuff();
    }
    ...
}

This design of course depends on the ability of cleanly abstracting the content-type differences.

DerrickH
+1  A: 

I think I get you. What I'd do is this:

Create 1 class to handle the rendering control flow:

class Renderer
{
    Tenant _tenant;

    void Render(ContentType type)
    {
        switch (type)
        {
            case ContentType.JSON: 
                _tenant.RenderJSON();
                break;
            default:
                _tenant.RenderHTML();
                break;
        }
    }
}

Then create 1 super class for Tenant:

class Tenant
{
    virtual void RenderJSON() { ... };
    virtual void RenderHTML() { ... };
}

Finally, create subclasses for the tenant specific stuff:

class JoeBlow : Tenant
{
    override void RenderJSON() { // joe blow's json };
}

This should leave you with a nice model:

  • 1 class per tenant (which is nice since you say the behavior changes on a per tenant basis)
  • 1 super class to put common behavior (for all tenants)
  • 1 place where your contentType gets resolved to the render method.

Adding a new content type is simple: Just update the (1) switch. If necessary you can add a new method to the super class and add tenant specific handling of the new content type to subclasses.

Adding a new tenant is easy too. Just subclass them.

It's ok to have switches, but if you're repeating them something is wrong imo.

TheDeeno