The other aim of this blog is to explore functional programming in C#. When I started getting interested in functional programming I'd often come across people asking questions along the lines of "is it true that design patterns are not needed in functional languages?" They were usually answered by someone confidently asserting that "yes, when you can pass code around you don't need the strategy pattern!" Occasionally someone else would join in and point out that functional languages have their own design patterns, and that perhaps the question should be "do you need to use object-orientated design patterns in functional languages," to which the answer is no. Since I use C# in my day job, I thought that a more interesting question would be (and this is the point of the whole blog-thingie) "do I need to bother with object-orientated design patterns when I have first class functions in my object-orientated language?"
And the answer appears to be not quite, depending on the design pattern. Or to put it another way, if you're working with an object-orientated language that supports first class functions, then you can do away with some of them. If you're lucky enough to be working with a functional language (say, Clojure, which I think is a fantastic language that I know too little of) then they don't really crop up because basically you're asking the wrong question. There are functional design patterns (about which I know very little), but the impression I've got is that you need to be using a functional language to even start worrying about them. Something else I noticed was that there were no obvious examples of replacing design patterns with first class functions, and so I thought I might start this blog - go through the Gang of Four patterns and see what I can do.
So, I decided to stop playing Battlefield 4 (for a while) and go through the design pattens one by one and see if I could do away with them using either first class functions or LINQ (a bunch of extension methods that make heavy use of first class functions). I'll introduce the design pattern, show how it works, and then replace it using first class functions.
Note that I'm assuming you know enough C# to understand what Func<T, T> and Action<T> represent and do.
First up:
Command Pattern.
I took my example from here: http://dotnetslackers.com/articles/designpatterns/The-Command-Pattern.aspx - which is one of many examples I've come across that creates a remote control for a ceiling fan. Exciting? Not at all, but I'm lacking in imagination so I'm going to copy their example, but with added badger.The Command Pattern separates out commands from the thing that they're commanding (called the receiver). Using the above ceiling fan example, we could create a "switch on" command, and a "switch off" command. These would expect to be passed an instance of a ceiling fan when they are executed, but they don't need one when they're created. Again, the command and the thing that they are commanding are completely separate. We can then queue up a whole collection of various commands, sourced from anywhere, and then run all of them against any instance of a ceiling fan we may happen to have lying around. If that doesn't make any sense, read throught the above link for a more well thought out description.
Think of a command as encapsulating an action (and that's a hint of how we can replace it with first class functions).
First up, meet our receiver, the exciting ceiling fan:
public interface IFan { void SwitchOn(); void SwitchOff(); void IncreaseSpeed(); void DecreaseSpeed(); void SetSpeed(int newSpeed); void ToggleLight(); }
As you can see, its got a bunch of public methods we can twiddle with. Meet our twiddlers - the command interface and the concrete commands that implement it (things that will twiddle):
public interface ICommand { void Execute(IFan fan); }
public class SwitchOn : ICommand { public void Execute(IFan fan) { fan.SwitchOn(); } } public class SwitchOff : ICommand { public void Execute(IFan fan) { fan.SwitchOff(); } } public class ToggleLight : ICommand { public void Execute(IFan fan) { fan.ToggleLight(); } } public class IncreaseSpeed : ICommand { public void Execute(IFan fan) { fan.IncreaseSpeed(); } } public class DecreaseSpeed : ICommand { public void Execute(IFan fan) { fan.DecreaseSpeed(); } } public class SetSpeed : ICommand { private int _newSpeed; public SetSpeed(int newSpeed) { _newSpeed = newSpeed; } public void Execute(IFan fan) { fan.SetSpeed(_newSpeed); } }
Finally, we need something to take the commands, and apply them to the ceiling fan (called the invoker):
public class CommandInvoker { private Queue<ICommand> _commandQueue = new Queue<ICommand>(); public void AddCommand(ICommand command) { _commandQueue.Enqueue(command); } public void Clear() { _commandQueue.Clear(); } public void RunCommands(IFan fan) { foreach(var command in _commandQueue) { command.Execute(fan); } } }
As you can see, you can add commands to it and then run them, one by one, against any instance of IFan you care to use.
Lets see that in action:
var test = new CommandPipe(); test.AddCommand(new SwitchOn()); test.AddCommand(new ToggleLight()); test.AddCommand(new IncreaseSpeed()); test.AddCommand(new SetSpeed(6)); test.RunCommands(new Fan());
Fan is on. Light is on Speed is 1 Speed is 6
Exciting? Still, no. But you should now see why this is a useful pattern. It separates what we want to do from what we want to do it to. It lets anyone throw together a bunch of commands (actions - hint hint) and then apply them (via an invoker) to any compatible receiver.
So, replacing that with first class functions....
We'll start with the invoker this time, as the Fan will remain unchanged. Note that it looks a lot like our original command invoker:
public class FunctionalInvoker { private Queue<Action<IFan>> _queue = new Queue<Action<IFan>>(); public void AddCommand(Action<IFan> command) { _queue.Enqueue(command); } public void Clear() { _queue.Clear(); } public void RunCommands(IFan fan) { foreach(var command in _queue) command(fan); }
}
The important difference is that instead of taking instances of ICommand, it takes pieces of code in the form of Action<IFan>. Instead of passing the fan to a class that will perform operations on it, the fan is passed into a void delagate (that's what Action<T> is, behind the scenes) as its (only) paramater.
So, how to add commands? Like so:
var func = new FunctionalInvoker(); func.AddCommand(x => x.SwitchOn()); func.AddCommand(x => x.ToggleLight()); func.AddCommand(x => x.IncreaseSpeed()); func.AddCommand(x => x.SetSpeed(6)); func.RunCommands(new Fan());
And when we run the functional invoker:
Fan is on. Light is on Speed is 1 Speed is 6
The end result is the same. What's changed however, is that the pattern is well and truly gone. There's no ICommand, and no concrete instance for each and every command.
But it gets better. If someone were to extend the IFan interface by say, adding a disco light, then we would need to create a whole new command to use it. Not so with the functional replacement - we just add another Action like so:
public interface IFan { . . . void DiscoLights(bool strobe); }
var func = new GenericFunctionalPipe<IFan>(); //... func.AddCommand(x => x.DiscoLights(true));
But we can go further. The functional invoker is still tied to the IFan interface, but there's no need for that. Change it to use a generic:
public class GenericFunctionalPipe<T> { private Queue<Action<T>> _queue = new Queue<Action<T>>(); public void AddCommand(Action<T> command) { _queue.Enqueue(command); } public void Clear() { _queue.Clear(); } public void RunCommands(T receiver) { foreach(var command in _queue) command(receiver); } }
and now the same invoker can be used to queue up and apply commands to ANY receiver:
var func = new GenericFunctionalPipe<IBadger>(); func.AddCommand(x => x.Feed()); func.AddCommand(x => x.PokeWithStick()); func.RunCommands(new Badger());
Badger is placated. Badger is enraged!
Remember, code is data :-)
No comments:
Post a Comment