Decorator or Russian Dolls (part 2)

It has taken a few weeks, but i have finished an implementation of “extras” in the making a cuppa instructions app. Part 1, for those that missed it. All the code can be found on github, https://github.com/chrishey/CupOfCoffee-DecoratorPattern

So ideally i wanted to keep a similar style to getting the extras, passed in via the command line. I also wanted to be able to specify via the command line what beverage i was making. So lets tackle that last one first.

First i brought in a command line args parser (see the git repository to see how it works) to read the arguments and give me an dictionary of arguments and their values to work with. In our initial Main method now we can read the “drink” argument and from there initialise the right beverage type class.


if (args.Length == 0)
{
Console.WriteLine("Sorry, we couldn't find your beverage");
}
else
{
var commandLineArgs = new CommandLineArgsParser(args);

switch (commandLineArgs["drink"].ToLower())
{
case "tea":
var tea = new Tea();
tea.CuppaFather();
break;
case "coffee":
var coffee = new Coffee();
coffee.CuppaFather();
break;
default:
Console.WriteLine("Sorry, we couldn't find your beverage. Would you like a cuppa Tea, Father?");
break;
}
}

We can now run the program to give us instructions to make us a cup of tea

makebrewdrink

Moving swiftly on past that small hurdle, I started thinking (and hacking) about how to get extras in. I wanted extras, whatever they were to be an IStep, so i could chain them. I also need a quantity potentially on an extra i.e. spoons of sugar. I tried a few ways to do this and after several failed attempts to make it work 100% cleanly i ended up with the following.

An extra always has a quantity, so let’s create a class to hold that.


public class Extra
{
protected readonly int Quantity;

public Extra(int quantity)
{
Quantity = quantity;
}
}

We can now use this as base for all extras and inject the quantity in via the constructor. I also wanted the extras to implement IStep so i can keep calling .Do() if another IStep is injected into it.


public class AddSugar : Extra, IStep
{
private readonly IStep _step;

public AddSugar(IStep step, int quantity):base(quantity)
{
_step = step;
}

public void Do()
{
Console.WriteLine(string.Format("Add {0} spoons of sugar", Quantity));

if (_step == null)
return;

_step.Do();
}
}

As you can see here, we take an IStep instance and a quantity in the constructor of an extra. We pass the quantity to the base, print out our instruction and then see if we have a subsequent step to call. I now needed to put this together with the command line arguments along the lines of Make.exe -drink tea -sugar 2. Due to the quantity constructor parameter it wasn’t just a case of chaining the extras into the Tea/Coffee class, also there might be multiple extras and i need to know their names and what instances to create.

My solution was to read any extras off the command line arguments.


var commandLineArgs = new CommandLineArgsParser(args);

var extras = GetExtras(commandLineArgs);

Where GetExtras is as follows:


private static Extra[] GetExtras(CommandLineArgsParser commandLineArgs)
{
var extras = new List();

if (commandLineArgs["sugar"] != null)
{
var sugar = int.Parse(commandLineArgs["sugar"]);
extras.Add(new AddSugar(null, sugar));
}

return extras.ToArray();
}

Not ideal and it does “itch” with me a bit, but it works, which some of my other attempts didn’t. In this example we aren’t passing in another step, just the amount of sugar off the arguments. I also have to know the name of the extra to deal with it, i couldn’t see how to do this dynamically, this might be part 3! 🙂

Now the Tea and Coffee classes need to accept these extras to add the instructions.


public class Tea : BrewUp
{
private readonly Extra[] _extras;

public Tea(Extra[] extras) : base (new FillKettle(new BoilKettle(new AddTeaBag(new AddMilk(null)))))
{
_extras = extras;
}

public new void CuppaFather()
{
base.CuppaFather();

if (_extras.Any())
{
foreach (var extra in _extras)
{
var step = extra as IStep;
if (step != null) step.Do();
}
}
}
}

We move through the ISteps via the BrewUp method of CuppaFather and then if we have extras we loop through them and call any subsequent ISteps chained onto them.

makeabrewwithextras

We can now get extras like sugar printed out in our instructions.

Any feedback or possible tweaks to the solution are very welcome. The code is there on Github, fork it and send a pull request or get in contact.

Thanks for reading….i am off to make a brew!

Patterns and Practices or use your brain?

There was a time when i thought you needed to use patterns and best practices to keep consistent and maintainable code. After all if everyone follows them then we are all building software in a similar way. New and existing team members and freelancers can all understand the code and maintain it. Problem solved, blog post over.

However do you end up forcing a pattern into an architecture for the sake of it? This could lead you to not build the best solution for the problem, but the solution with the best use of several patterns. Could your code be simpler and more maintainable without using the patterns?

Best practice treated like a magic bullet.
By applying “best practices” you limit the chances of failing from a certain point of view, but does it stop you thinking and using your brain? One of the many things I learnt working with @grumpydev was that if you use common sense and engage your brain then your code will more than likely be maintainable and others will understand what you are trying to achieve. Also when you come back to that code that you haven’t seen for months does applying common sense and engaging your brain mean you understand your own code easily.

Principles over patterns.
Patterns can be useful, but it depends on context. Just because the pattern worked in one instance does not mean it will always work for the code you are writing. Principles however should when applied well, assist you in writing quality code. My wife bakes cakes as one of many jobs she does (shameless plug – @itcakestwobaby) and started out using a recipe for cupcakes. It describes everything you need to do to make cupcakes just like her. Does this mean anyone can make them like her? Maybe…however I have watched her and she doesn’t stick to the recipe, it is all her own judgement from experience and knowing her ingredients and our oven. I don’t think you can add a spoonful of factory pattern and a dash of decorator pattern and create a perfect solution by keeping to them.