Pages

Saturday, 9 January 2010

Supercede Instance Pattern

This is a pattern introduced (to me at least) by Michael Feathers in his book "Working Effectively with Legacy Code". It deals with occasions where you have a Singleton pattern that it consumed by a large number of classes. This impedes these classes being Unit Tested as they all have a hidden dependency.

The first solution to this is to add a constructor overload to every class. You will also need to extract an interface for the Singleton. For instance...

public class MyClass
{
    public MyClass()
    {
    }

    public void DoWork()
    {
        MySingleton.Instance.DoWork();
    }
}

... would become ...

public class MyClass
{
    private IMySingleton _worker;

    public MyClass()
        : this (MySingleton.Instance)
    {
    }

    public MyClass(IMySingleton worker)
    {
        _worker = worker;
    }

    public void DoWork()
    {
        _worker.DoWork();
    }
}

You won't have to change any other code once the above it implemented. What this achieves is that your Unit Tests have the opportunity to use a Dependency Inversion pattern by using the non-default constructor. Simple, and nothing new.

However, this can be an awful lot of work - directly proportional to the number of classes that consume your singleton. This is where the Supercede Instance Pattern comes in.

This is probably a fair representation of the Singleton Pattern...

public class MySingleton : IMySingleton
{
    private MySingleton()
    {
    }

    private static IMySingleton _instance;

    public static IMySingleton Instance
    {
        get
        {
            // Double locking mechanism omitted for brevity...
            if (_instance == null)
                _instance = new MySingleton();
        }
    }
}

All we need to do is add a new method to the above class...

public class MySingleton : IMySingleton
{
    // All previous members omitted for brevity

    public static void SupercedeInstance(IMySingleton newInstance)
    {
        _instance = newInstance;
    }
}

Et voila. Your unit tests can call this new method and inject their own, chosen object into the Singleton. You've now broken the dependency of masses of classes in one hit. Nice.

Final point, if your language allows you could conditionally remove this method. That would keep your singleton 'safe' in the wild. For example...

public class MySingleton : IMySingleton
{
    #if RUNNING_UNIT_TESTS

    public static void SupercedeInstance(IMySingleton newInstance)
    {
        _instance = instance;
    }

    #endif
}

Thursday, 7 January 2010

ASP.Net MVC Groups


Here's a fictional example of a problem I've been having in ASP.Net MVC...
In an MVC application I have 1000 controllers. 2 of these controllers share a partial view so I put it in the ~/Views/Shared/ folder. I then find that I have 499 more pairs of controllers that also share different partial views so I do the same. The problem is that I now have a shared folder cluttered with 500 partial views - each of which is only relevant to 0.2% of my entire application! I also have to give each of these views unique names which becomes unwieldy.
So what's the solution?

What I'd like to do is group multiple controllers together and give them their own shared area for views. Any controller can also belong to more than one group.

Firstly, we need to extend the ViewEngine that our application is using. This is, primarily, responsible for finding the correct view in our filing system. Out of the box the framework uses  WebFormViewEngine. Our new view engine will inject extra, temporary locations in which to search for views. The information about which locations will be supplied by the current controller. Here's the code for the new engine...

using System;
using System.Collections.Generic;
using System.Web.Mvc;

namespace MyMvcApp.Web.Mvc
{
    ///
    /// GroupWebFormViewEngine extends WebFormViewEngine and allows Controllers to be
    /// decorated with the MvcGroupAttribute to indicate extra locations Views can be found.
    ///
    public class GroupWebFormViewEngine : WebFormViewEngine
    {
        private string[] _defaultMasterLocationFormats;
        private string[] _defaultPartialViewLocationFormats;
        private string[] _defaultViewLocationFormats;

        public GroupWebFormViewEngine()
            : base()
        {
        // We want to make a note of the base formats to that we can reset them prior to each Find operation
            _defaultMasterLocationFormats = MasterLocationFormats;
            _defaultPartialViewLocationFormats = PartialViewLocationFormats;
            _defaultViewLocationFormats = ViewLocationFormats;
        }

        public override ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            AddGroupLocations(controllerContext);

            return base.FindView(controllerContext, viewName, masterName, useCache);
        }

        public override ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
        {
            AddGroupLocations(controllerContext);

            return base.FindPartialView(controllerContext, partialViewName, useCache);
        }

        private void AddGroupLocation(ControllerContext controllerContext)
        {
            ResetAllLocations();

            var type = controllerContext.Controller.GetType();

            var attrs = type.GetCustomAttributes(typeof(MvcGroupAttribute), true);

            Array.ForEach(attrs, attr =>
            {
                AddGroupLocations((attr as MvcGroupAttribute).GroupName);
            });
        }

        private void ResetAllLocations()
        {
            MasterLocationFormats = _defaultMasterLocationFormats;
            PartialViewLocationFormats = _defaultPartialViewLocationsFormats;
            ViewLocationFormats = _defaultViewLocationFormats;
        }

        private void AddGroupLocations(string folderName)
        {
            AddMasterLocationFormats(folderName);
            AddPartialViewLocationFormats(folderName);
            AddViewLocationFormats();
        }

        private void AddMasterLocationFormats(string folderName)
        {
            List<string> currentLocations = new List<string>(MasterLocationFormats);

            currentLocations.Add("~/Views/Shared/" + folderName + "/{0}.master");

            MasterLocationFormats = currentLocations.ToArray();
        }

        private void AddPartialViewLocationFormats(string folderName)
        {
            List<string> currentLocations = new List<string>(PartialViewLocationFormats);

            currentLocations.Add("~/Views/Shared/" + folderName + "/{0}.aspx");
            currentLocations.Add("~/Views/Shared/" + folderName + "/{0}.ascx");

            PartialViewLocationFormats = currentLocations.ToArray();
        }

        private void AddViewLocationFormats()
        {
            ViewLocationFormats = PartialViewLocationFormats;
        }
    }
}

Now we just need our MvcGroupAttribute. This will be used to decorate our controller classes and should apply also to any inherited controllers. It is also possible to have more than one attribute on each controller as it might belong to multiple groups. Here's the code...

using System;

namespace MyMvcApp
{
    [AttributeUsage(AttributeTarget.Class, AllowMultiple = true, Inherited = true)]
    public class MvcGroupAttribute : Attribute
    {
        ///
        /// Gets or sets the name of a shared group that this controller belongs to.
        ///
        public string GroupName
        {
            get;
            set;
        }
    }
}

Now that we have all our building blocks we can finally use the functionality. Here's a trivial example...

using System;
using System.Web.Mvc;

namespace MyMvcApp
{
    [MvcGroup(GroupName = "Group1")]
    public class MyFirstController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }

    [MvcGroup(GroupName = "Group1")]
    public class MySecondController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }

    public class MyThirdController : Controller
    {
        public ActionResult Index()
        {
            return View();
        }
    }
}

In the example, above, all 3 controllers have access to views in the ~/Views/Shared/Group1/ directory.

Conclusion

This simple addition to our Mvc applications has been an enormous difference. We now have so much more flexibility to construct our views in a meaningful, clean way. Hopefully you'll find it useful too?

Tuesday, 22 December 2009

Muzzling an Angry Dog

I've been setting aside as much time as possible recently to read Michael Feather's Working Effectively with Legacy Code. A large amount of our code-base for one product is very much in this court and now, unfortunately, needs dealing with. Luckily, though, the more I refactor this type of code the more I realise how much I enjoy it.

I was explaining some of the governing principals to a colleague today...

  • "First you have to get your legacy code into a test harness, then you can safely alter it."
  • "OK."
  • "However, you'll need to alter it before you can do that."
  • "Huh?"

I have come to the conclusion that Working with Legacy code does not so much involved harnesses as muzzles. It is akin to Muzzling an Angry Dog. First, you have to sneak up to it, from behind, without making any noise or disturbing it's environment. Then, once the muzzle is safely on you can relax and poke it as much as you like. From now on, our estimates will have a 'Dog Muzzling' factor added to them.




[And, no, I do not condone taunting any dog, angry or otherwise.]

Tuesday, 8 December 2009

Annual Company Review

We have just had our annual Company Meeting today - accompanied by some stellar Italian food. Part of my presentation was to review our development achievements over the past 12 months. In December 2008 we were using Visual Studio 2005 and Visual Source Safe - and that was pretty much it. Today, just 12 months later we have implemented the following:

  • Visual Studio 2008 Developer + Database Editions
  • Team Foundation Server 2008
  • TFS Source Control
  • Continuous Integration Server
  • Nightly Builds
  • Fully automated build and deploy to test servers
  • Unit Tests (20% Code Coverage so far)
  • Code Reviews
  • 2 projects using Scrum
We now score 10 / 12 on the Joel Test.

Wow, what a difference a year makes!

Monday, 7 December 2009

Moq Setup Causes Compiler Error CA1506

I have been using Moq to help enable some unit tests and I've been getting intermittent build exceptions caused by the Code Analysis engine when calling the Setup method of my new mock object.

Example:

var mock = new Mock<IDoSomething>();

mock.Setup(x => x.DoSomething());

CA1506 is raised as a warning to Avoid excessive class coupling. This is, obviously, a little strange. Stranger still is that this warning results in a compilation error.

Anyway, it's a bug in Visual Studio. The workaround is just to remove this check from your Code Analysis (goto Project Properties | Code Analysis | Maintainability Rules and uncheck CA1506).

Wednesday, 18 November 2009

Reach Out and Touch A Developer!

I can only imagine what you're expectations are of this post given the title! You may be reading this for entirely the wrong reasons. If so, remember, Google is your friend.

Over the weekend I caught the DDD bug and started buying books, subscribing to blogs etc. As part of my random hops I stumbled upon Stephen Oakman of The Agile Workshop. He says in his Contact Us page that anyone is welcome to just pop in if they're passing. Having found myself in Cambridge yesterday lunchtime I did just that. Bit of a risk || leap of faith but I thought, hey, why not?

Turned out to be a fantastic use of a lunch hour. Once they'd gotten over the shock of someone actually taking them up on their offer we settled into some truly geeky discussions.

So, why on earth am I blogging about this?

Simple: if you work remotely you need to actively build a network of peers.

With the advent of Skype, Mikogo etc. remote working has never been so productive; but a developer in this environment still won't have a pool of knowledge comparable to, say, working in a large, office-based company. The guys at The Agile Workshop not only make half-decent coffee but seem pretty spot-on in many fields of software development that I am passionate about. They also loaned me a raft of books from their 'library', which I will return - honest :).

Here's to a mutually beneficial relationship.

Tuesday, 3 November 2009

2 Week Sprints!

Both of our applications that use the Scrum development framework have now halved their iteration cycles from 4 weeks to 2 weeks.

This felt rather daunting at first. I was worried that there just wouldn't be enough time to actually develop something of billable, business value for our customers. However, I have been pleasantly surprised by the universal acceptance of this change. Developers, analysts and managers all report to less confusion about the relevant content of the Product Backlog - this has many cross-overs with some Getting Things Done principles regarding removing clutter.

With this one simple process change we now feel much more 'sprinty': able to really focus on a smaller number of work items. We have also doubled the amount of feedback measured and adaption possible; velocity is also more meaningful.

So, highly recommended. I wonder if we'll move to a one week sprint as Ken Schwaber currently recommends? Xtreme sprinting!