Decorators and why you want to roll your own IDataFactory

Imagine the following scenario: You’ve developed a bigish EPiServer site and obviously uses DataFactory in various places to show menus and other types of collection of data. Suddenly the client realizes that they need to utilize the Show page in menus functionality to be able to controll if a page should be visible or not. The (few) places you’ve used EpiServers built in controls this will work out of the box. There are however quite a lot of places where you uses other means of displaying this data. So what do you tell your customer? That this change request will have a ripple effect over the entire site and it will take quite a lot of time?

Your IDataFactory

Creating an abstraction is not something to be taken too lightly. Just as prematurly fixing performance problems before they are performance problem can be counterproductive adding needless abstractions can have a negative impact on your code base. However, if there’s one case that have felt like a silver bullet (yea, I went there) it’s abstracting away DataFactory. The reason why this abstraction makes sense is fairly obvious. The DataFactory is a central part of your site and almost all requirements changes will touch it in one way or another.

While it’s entirely possible to extract an interface from the current DataFactory it’s not something that I’d necessarily recommend. The reason for this is partly because that interface would be way too big and there are probably ways to seperate the functionality into smaller interfaces that share some common goal (for instance, GetPage and GetChildren feels more connected than GetDefaultPageData). There can also be ways to add methods that makes sense in your domain or does something in an opinionated fashion. A good example of this is the PageRepository from EPiAbstractions. For the sake of this post we’ll have a very tiny PageStore interface

public interface IPageStore
{
    IEnumerable<PageData> GetChildren(PageReference pageLink);
}
public class PageStore : IPageStore
{
    public IEnumerable<PageData> GetChildren(PageReference pageLink)
    {
        return DataFactory
            .Instance
            .GetChildren(pageLink);
    }
}

 

Decorators

Usually the reason for creating the abstraction is to be bring testability to your code but that are of course other reasons as well. Decorator, according to the wikipedia article, “is a design pattern that allows behaviour to be added to an existing object dynamically”. In the scenario outlined in the introduction to this post the customer wanted to add a check for visible in menu. In code this decoration could look like this

public class PageStoreVisibleInMenuDecorator : IPageStore
{
    readonly IPageStore _inner;

    public PageStoreVisibleInMenuDecorator(IPageStore inner)
    {
        _inner = inner;
    }

    public IEnumerable<PageData> GetChildren(PageReference pageLink)
    {
        return _inner
            .GetChildren(pageLink)
            .Where(p => p.VisibleInMenu);
    }
}

And using this in your code would be as simple as changing your IoC-container (you are using an IoC-container, right?) setup. Everywhere you use you IPageStore it will automatically only include pages that are visible in menus. So what are the benefits of doing it this way?

No ripple effect

You can easily change the behavior of you application without changing the code in more than in one place.

OCP (open / closed principle)

The changes made didn’t require any changes to your existing PageStore class. All “new” logic was performed in the decorator.

Conditional use and chains

Since a decorator perform some action (eg decorates) some object you can create a chain of sort of actions to perform. Let’s say that certain pages under certain conditions should perform additional actions. For instance, if the page you’re getting children for is a news container some sort of date filtering should be used. You could easily create a decorator for this, which decorates the PageStoreVisibleInMenuDecorator which in turn uses the original PageStore. Then it’s simply a matter of configuring your IoC-container to create the right decorator for the right condition.