Coding: Role based interfaces
I've read a bit about role based interfaces but I've never really quite understood how the idea could be applied into our code – this week my colleague Matt Dunn has been teaching me.
We had a requirement to show some content on every page of the website we're working on. The content would be slightly different depending on which business process you're doing.
Our first solution made use of an already defined 'BusinessType' property which allowed us to work out which content we needed to create.
public class ApplicationController : Controller { public string BusinessType { get { return GetType().Replace("Controller", ""); } } public override void OnActionExecuting(ActionExecutingContext context) { base.OnActionExecuting(context); ViewData["SomeViewDataKey"] = CreateThatViewDataStuff(); } private ViewDataStuff CreateThatViewDataStuff() { return new ViewDataStuff { Content = BuildContent() // and so on }; } private Content BuildContent() { if(BusinessType == "BusinessType1") { return CreateContentForBusinessType1(); } else if(BusinessType == "BusinessType2") { return CreateContentForBusinessType2(); } else { return CreateContentForEveryOtherBusinessType(); } } }
public class BusinessType1Controller : ApplicationController { } public class BusinessType2Controller : ApplicationController { }
The problem is that it's really hacky, way too much is going on in the ApplicationController and every time we have a business type which has different content we have to add to the mess.
We decided to make use of polymorphism to make the solution a bit cleaner and my initial thought was that we could just create a 'CreateContent' method on the ApplicationController and then override that if necessary in the other controllers.
Matt suggested that we should take this a step further and define that method as part of an interface called 'IContentFactory'.
With this approach we would then be able to just pass a reference to that interface into the 'ViewDataStuff' class which can delegate to the 'IContentFactory' when the content is requested.
In this case in the 'CreateContent' methods we were making network calls to retrieve data so it allowed us to delay these calls until strictly necessary.
public class ApplicationController : Controller, IContentFactory { public override void OnActionExecuting(ActionExecutingContext context) { base.OnActionExecuting(context); ViewData["SomeViewDataKey"] = CreateThatViewDataStuff(); } private ViewDataStuff CreateThatViewDataStuff() { return new ViewDataStuff(this) { // and so on }; } public virtual Content CreateContent() { return new Content(...); } }
public class BusinessType1Controller : ApplicationController { public override Content CreateContent() { // creates the Content object slightly differently. return new Content(...); } }
public class ViewDataStuff { private readonly IContentFactory content; public ViewDataStuff(IContentFactory content) { this.content = content; } public Content Content { get { return content.CreateContent(); } } }
We thought about using Udi Dahan's naming notation for interfaces but the factory pattern is quite well known so it seemed like we might creation more confusion by doing that.
I think it'd be interesting to see what we'd have come up with if we'd test driven this code rather than refactored it into this pattern.
Overall though it seems quite neat as an approach and the next time we came across some similar code I was able to introduce a role based interface having been taught how to do it by Matt on this one.
[...] Coding: Role based interfaces – Mark Needham discusses how you can support different user interfaces for users in different roles using the MVC pattern [...]
Reflective Perspective - Chris Alcock » The Morning Brew #457
19 Oct 09 at 5:32 pm
Hello Mark,
Why not using an IoC framwork? For example ninject allows you to do context sensitive binding that allows you to specify what kind of concrete instances need to be injected when a type is requested in a specific context. That would solve all your problems
Daniel
Daniel Marbach
19 Oct 09 at 5:33 pm
Coding: Role based interfaces – Mark Needham…
Thank you for submitting this cool story – Trackback from DotNetShoutout…
DotNetShoutout
20 Oct 09 at 1:23 am
@Mark: I quite like this, and your explanation. It seems a very good example of tell-don't-ask, or at least east-oriented code. Introducing the interface should make it easier to break in to a separate class, if we really need to.
@Daniel: We already use an IoC container in this code base. Unity. At the time the controller is resolved, we don't know which content we need. And, with the exception of one item, we don't resolve anything afterwards. We could, but this solution seems tidy for the time being.
Dave Cameron
22 Oct 09 at 2:03 pm