Introduction
I’ve written an entire series of articles about Dependency Injection and the Inversion of Control. I’ve exhorted you to decouple your code and to ask for your dependencies instead of creating them yourself. I hope you’ve listened.
Now I’m going to discuss another way you can invert the control of your code – by using anonymous methods.
Anonymous methods were one of two major language features added in Delphi 2009 – the other being generics. Generics are probably easier to understand and use as they have a great use case with collections. Anonymous methods are a little harder to understand because their use and their purpose are not as easily seen. I hope that you’ll start seeing their advantage after reading this post.
Basic Idea
The basic idea is pretty simple – anonymous methods allow you to pass code, i.e. functionality, like any other variable. Thus, you can ask for functionality via an anonymous method just like you can ask for a dependency via an interface. Because you can assign code to a variable – or even pass code directly into a method – you can easily invert the control of your class by passing in code to your class instead of embedding it in the class itself.
One of the principles of Inversion of Control is “Don’t call us, we’ll call you”. That is, if a class needs something, the class itself should ask for (call) the functionality, rather then doing that functionality itself. If a class needs to, say, record the processing of widgets as it happens, it should ask for a widget recording class and record things using the dependency that is passed in. A class should not create it’s own instance of a widget recording class and then use it.
Given that example, it’s not difficult to see how one could pass recording functionality into a class as an anonymous method.
Doing it the “Normal” Way
Let’s use the above discussion in a code example. As noted, perhaps your class needs to record the processing of widgets. Typically, you might do something like this in order to use good dependency injection techniques:
type
IWidgetRecorder = interface
procedure RecordWidgets(aNumberOfWidgets: integer);
end;
Then, you’d pass an instance of that interface into your class:
var
MyWidgetRecorder: IWidgetRecorder;
...
MyWidgetRecorder := TWidgetRecorder.Create;
MyWidgetProcessor = TWidgetProcessor.Create(MyWidgetRecorder);
You’d store the widget recording interface and use it in your class as needed. And that’s an excellent thing to do – your class is decoupled completely from the recorder, and you are using constructor injection to ask for your dependency.
Again, this is a great technique, and probably preferable most of the time. I don’t want to leave the impression that what I discuss below is always a replacement for good, sound use of interfaces. However, it may not always be what you want to do.
IOC with Anonymous Methods
As noted above, you can use anonymous methods to invert the control of your class. Perhaps declaring an interface and instantiating a class to implement it is too heavy. Perhaps the dependency is really simple and straightforward. In those cases, using a simple anonymous method to inject your dependency might be a better solution for decoupling your code.
For instance, you can declare an anonymous method type:
type
TWidgetRecorderProc = reference to procedure(const aCount: integer);
This will allow you to declare code that takes an integer – presumably the count of widgets being recorded – and does whatever you need it to without bothering the class that uses it.
From there, you can use this type in your classes:
type
TWidgetProcessor = class
private
FWidgetRecorder: TWidgetRecorderProc;
public
constructor Create(aWidgetRecorder: TWidgetRecorderProc);
procedure ProcessWidgets(aNumberToProcess: integer);
property WidgetRecorder: TWidgetRecorderProc read FWidgetRecorder write FWidgetRecorder;
end;
This simple class declaration takes an anonymous method as a parameter to its constructor, and then exposes that function as a property. That’s a bit of a commitment – you may not always want to do widget recording. If you wanted, you could not pass the anonymous method in the constructor and simply assign it to the property as desired. In any event, the widget recording functionality is implemented externally, and your class merely depends on the anonymous method type.
Then, you can implement the class as follows:
{ TWidgetProcessor }
constructor TWidgetProcessor.Create(aWidgetRecorder: TWidgetRecorderProc);
begin
inherited Create;
FWidgetRecorder := aWidgetRecorder;
end;
procedure TWidgetProcessor.ProcessWidgets(aNumberToProcess: integer);
begin
WriteLn('Processing ', aNumberToProcess, ' widgets.');
WidgetRecorder(aNumberToProcess);
end;
Now, you have a class that can record widgets any way you want to – to the console, to a file, to email, a database -- whatever. And you don’t have to know anything about the implementation of how the widgets are recorded. You just have a variable that refers to the code, which you can easily call in your code.
Using Your New Widget Recording Tool
So, now we have a TWidgetProcessor that can record widgets with an anonymous method. Cool – but how? Well, you simply declare an anonymous method and pass it in. Here’s an example which just writes out to the console:
procedure ProcessSomeWidgetsAndRecordToConsole;
var
WidgetProcessor: TWidgetProcessor;
RecorderProc: TWidgetRecorderProc;
begin
RecorderProc := procedure(const aCount: integer)
begin
Writeln('The Widget Recorder recorded ', aCount, ' widgets');
end;
WidgetProcessor := TWidgetProcessor.Create(RecorderProc);
try
WidgetProcessor.ProcessWidgets(Random(100));
finally
WidgetProcessor.Free;
end;
end;
All this code does is declare an anonymous method that matches the declaration of TWidgetRecorderProc and writes the information out to the console. That’s pretty simple. But what if you wanted to write it out to a file? Easily done without changing the TWidgetProcessor class at all.
procedure ProcessSomeWidgetsAndRecordToFile;
var
WidgetProcessor: TWidgetProcessor;
RecorderProc: TWidgetRecorderProc;
const
cFilename = 'WidgetRecord.txt';
begin
RecorderProc := procedure(const aCount: integer)
var
SL: TStringList;
S: string;
begin
SL := TStringList.Create;
try
if TFile.Exists(cFilename) then
begin
SL.LoadFromFile(cFilename);
end;
S := Format('%s: %d widgets processed', [DateTimeToStr(Now), aCount]);
SL.Add(S);
SL.SaveToFile(cFilename);
finally
SL.Free;
end;
end;
WidgetProcessor := TWidgetProcessor.Create(RecorderProc);
try
WidgetProcessor.ProcessWidgets(Random(100));
finally
WidgetProcessor.Free;
end;
end;
Of course, you could write a similar anonymous method to record the information to a database, send an email, or any combination thereof.
And just as importantly, if you want to test The TWidgetProcessor class, you might want to do so by passing in a TWidgetRecorderProc that does absolutely nothing:
RecorderProc := procedure(const aCount: integer)
begin
// Do nothing, as this is a stub for testing purposes
end;
And of course if your classes are easy to test, then they are very likely well designed. The ability to stub out functionality as above is a hallmark of well designed classes.
Conclusion
So now you have another tool in your Dependency Injection/Inversion of Control tool box. The power here is in the ability to decouple functionality from the class that needs that functionality. Anonymous methods provide a nice way to wrap up functionality in a single variable. Instead of injecting an implementation of an interface, with anonymous methods you can inject a specific chunk of code that will provide the functionality. Either way, you end up with testable code that will be more maintainable in the future.
(I have included the full source for this demo, as well as other demos, available on my Demo Projects repository on BitBucket.org)