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!