Hodges 2017
Hodges 2017
Hodges 2017
Nick Hodges
This book is for sale at http://leanpub.com/dependencyinjectionindelphi
ISBN 978-1-941266-19-9
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean
Publishing process. Lean Publishing is the act of publishing an in-progress ebook
using lightweight tools and many iterations to get reader feedback, pivot until you
have the right book and build traction once you do.
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . i
Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . iii
Constructor Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Constructor Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Never Accept nil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
When to Use Constructor Injection . . . . . . . . . . . . . . . . . . . . . 28
Property Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
CONTENTS
Method Injection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
A Step-by-Step Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
The Beginning . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Introducing Constructor Injection . . . . . . . . . . . . . . . . . . . . . 51
Coding to an Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Enter the Container . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
The File Displayer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
Building Displayers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
Tying It All Together . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Connecting to the User Interface . . . . . . . . . . . . . . . . . . . . . . 103
Ways to Improve This Application . . . . . . . . . . . . . . . . . . . . . 106
Conclusions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
more about it than I did when I wrote about it in my first book. There I dedicated
only two chapters to the topic. Here I will dedicate a whole book to it. I will endeavor
to help “the light bulb to go on” about Dependency Injection, so that its use becomes
second nature to you. I’ll help you see the anti-patterns that are all too commonly
used, and I’ll show you the proper patterns for building loosely coupled applications.
Most of the material in this book will be new. I will not use the same examples from
Coding in Delphi, but you may see some of the same principles discussed in that book
also covered here.
I think it is also important to note that this is really a book about Dependency
Injection that happens to use Delphi as the language for the demos. At least, I want it
to be that. The principles discussed herein are true for any development language, and
so if you are by chance a C# developer or a user of some language other than Object
Pascal who is reading this, fear not. Delphi code is easy to read and understand, and
you should be able to learn all I have to say without knowing a lick of Delphi.
This book is divided into two parts: One that discusses Dependency Injection, and
the other that discusses the use of what is commonly called a DI Container. DI does
not require the use of a container, as I will stress many times going forward.
This book will also make extensive use of the Spring for Delphi Framework. I’ll
use Spring4D’s DI Container, the Guard class, and maybe some of the collections.
If you haven’t added Spring4D to your arsenal, you should do so immediately. Its
excellent implementation of generic, interfaced-based collections alone is worth it.
The Spring4D Framework can be found at http://www.spring4d.org.
The code for this book is available at this link: http://bit.ly/diidcode
All the code in this book was written and compiled with Delphi 10.1 Berlin. I can’t
promise that it will work in any previous version, but I don’t see why it wouldn’t.
Okay, so let’s get to it. You have nothing to lose but your spaghetti code.
Nick Hodges
Gilbertsville, PA
February, 2017
Acknowledgements
First, I’d like to say thank you to my wife, Pamela, who supported this effort from
start to finish.
I’d like to thank Baoquan Zuo who originally wrote the Spring for Delphi framework,
including the Dependency Injection Container. Your contributions to the Delphi
community are many and much appreciated.
I’d also like to thank Stefan Glienke for all his great work in maintaining the Spring
for Delphi after taking it over from Baoquan. Without you, we wouldn’t have the
Spring for Delphi framework as it is today.
In addition, I am deeply indebted to Stefan for his careful and thorough technical
review of this book. It is vastly better as a result.
Bruce McGee graciously read the rough draft and provided invaluable feedback that
improved the book greatly. I am grateful for his help.
What is Dependency Injection?
Introduction
I have three kids. They are older now, but when they were younger, it was fun to
bake with them. We’d bake a cake or cookies or some other confection that they
would then enjoy eating. But there was one problem – we made a huge mess when
we baked. There was flour and sugar and all kinds of stuff all over the place and all
over us. The process of baking left us with a big job of cleaning up. It made for a lot
of work. Sure, we had fun, but the mess was there, nonetheless.
How to avoid the mess? Well, we could be more careful, I suppose, but kids will be
kids. Oftentimes, we would be baking a birthday cake. An alternative, of course, to
all of the hassle and mess of baking a cake would be to go to the store and buy a
pre-made cake. An even better solution would be to find a bakery that would deliver
birthday cakes to order, dropping off at your doorstep exactly what you wanted for
your birthday cake.
Now here is something else to consider. Say the delivery guy shows up and he’s been
tromping through mud and his boots are filthy, and he’s got a vicious cough. Are you
going to let him into your house? No – of course not. You’d probably take one look
at him through the crack in the door and tell him to put the cake box down on the
porch and get the heck off your lawn. You’d be awfully careful about the cake, too,
I guess, but for the sake of argument we’ll assume it is well wrapped and protected
from whatever phlegm the delivery guy was emitting. In other words, you’d want
your interaction with the delivery person to be as minimal as possible, but you still
want the cake. Your kid is going to be pretty upset if he or she doesn’t get to blow
out the candles on their birthday cake. You want the cake without the mess and with
the thinnest of interactions with the delivery person.
One might even say that you had a dependency on a birthday cake, and the bakery
was injecting that dependency by delivering the cake to you. Hmm.
What is Dependency Injection? 2
Or let’s say you are at the supermarket, and you have a grocery cart full of groceries.
You take them to the checkout guy, and he rings them all up. He says “That’ll be
$123.45”. So what do you do? Do you hand him your wallet and have him dig around
for cash or a credit card? Of course not – you want to keep the interaction as minimal
as possible with your wallet – you don’t want him rooting around in it. Who knows
what will happen. Instead, you give the guy cash, or you pull out your credit card
and hand it to him. Or better yet, you swipe the card yourself so that the checkout
guy never even touches your card. Again, you want to have that interaction be as
minimal as possible. You might say that you want to keep the interface between you
and the clerk to a minimum.
One more. Say you are looking at buying a house. You search all over town and
you finally find a place that you really like. Good location, good school district,
good neighborhood. Only there is one problem – all the electrical devices are hard-
wired into the house. All the lamps, the toaster, the hair-dryer, everything is hard-
wired. There are no plugs in the house. Everything is wired directly into the electrical
system, so you can’t easily replace anything. The normal interfaces – electrical plugs
– between the electrical devices and the electrical system simply are not there. You
couldn’t replace your toaster without calling an electrician. One might say that the
lack of interfaces has made things very difficult to deal with, and thus you decide
not to buy the house.
Are you seeing a pattern here?
Okay, enough stories. I’ll just say it. In your code, the interaction between objects
should be as thin and clean as possible. You should ask for precisely what you need
and nothing more. If you need something, you should ask for it – have it “delivered”
to you – instead of trying to create it yourself. That way, just like with the cake and
the cashier and the house, things stay neat and clean and flexible. I think code that
is neat and clean and flexible sounds pretty good.
That is the essence of Dependency Injection. It’s really nothing more than that.
Dependency Injection is a twenty-five dollar term for a ten cent concept: If you need
something, ask for it. Don’t create things yourself – push that off to someone else.
Every class of any substance will likely need help from other classes. If your class
needs help, ask for it; don’t try to “do it yourself.” Remember, every time you try
to “do it yourself” you create a hard-coded dependency. And again, those should be
avoided like global variables.
What is Dependency Injection? 3
That may sound a bit weird, but it makes an important point: “Doing Dependency
Injection” and “Using a Dependency Injection container” are really two very different
things. They are related – the latter making the former easier to scale – but they are
not the same thing. In fact, I’ll cover a lot of territory before I talk about the DI
Container again.
type
TClassB = class
end;
TClassA = class
private
FClassB: TClassB;
public
constructor Create;
end;
constructor TClassA.Create;
begin
FClassB := TClassB.Create;
end;
What you want is to keep the coupling between your classes and
modules as “loose” as possible. That is, you want your dependencies
to be as thin as you can make them. If you have a class that needs a
customer name, pass in just the customer name. Don’t pass in the entire
customer. And as we shall see, if your class needs another class, pass in
an abstraction – an interface, usually – of that class. An interface is like
a wisp of smoke – there is something there, but you really can’t grab on
to it.
What is Dependency Injection? 5
This notion is so important that we’ll discuss it more fully in the next
chapter.
How do you create these hard-coded, tightly coupled dependencies? You create
them by, well, creating things inside other classes. Look at the example above. The
constructor of TClassA creates an instance of TClassB and stores it internally. That
Create call creates the dependency. You need TClassB to compile TClassA.
What to Do?
Well, the first thing to do is not to create TClassB inside TClassA. Instead, “inject”
the dependency via the constructor:
type
TClassB = class
end;
TClassA = class
private
FClassB: TClassB;
public
constructor Create(aClassB: TClassB);
end;
By doing this, you loosen the coupling up a bit. First note that you can now pass
in any instance of TClassB that you want. You can even pass in a descendent if
that makes sense. You are still tied to TClassB, and TClassA still cannot be compiled
without TClassB, but you’ve added some flexibility by loosening the coupling just a
touch. TClassA and TClassB are still coupled, but they are more loosely coupled.
So at this point, we have two types of coupling: one where the first
class actually creates an instance of the second class and one where the
first class needs the second class, i.e. where it is injected. The latter is
What is Dependency Injection? 6
And that, friends, is the essence of DI. Inject your dependencies instead of creating
them. That’s it. That’s Dependency Injection. I could stop right here, and if that were
the only thing I taught you, your code would be better off than it is today (because,
face it, your code base is full of hard-coded dependencies, right?). I could end the book
right here and you’d have a new tool in your tool chest that would really improve
things.
But, of course, it really isn’t that simple. There’s more to it than that, and as projects
scale up, things get complicated, but as a first example, it goes a long way towards
describing what DI is and how it works. In the coming chapters, we’ll explore it more
deeply, but if you’ve understood and seen the power of this simple technique, you
are well on your way to writing better, cleaner, easier to maintain, and more flexible
code.
So what should be created? Well, there are two different kinds of objects that we
should concern ourselves with: “Creatables” and “Injectables.”
Creatables are classes that you should go ahead and create. They are RTL or utility
classes that are common and well-known. For Delphi developers, these are things
like TStringList and TList<T>. Generally, classes in the Delphi runtime should be
considered Creatables. Classes like this should not be injected, but should be created
by your classes. They often have short lifetimes, frequently living no longer than the
span of a single method. If they are required by the class as a whole, they can be
created in the constructor. One should pass only other Creatables to the constructor
of a Creatable.
Injectables, on the other hand, are classes that we never want to create directly. They
are the types of classes that we never want to hard-code a dependency to and that
should always be passed via Dependency Injection. They normally will be asked
for as dependencies in a constructor. Following the rule above, injectables should
be referenced via interfaces and not direct references to an instance. Injectables
will most often be classes that you write as part of your business logic. They
should always be hidden behind an abstraction, usually an interface. Note, too, that
injectables can ask for other injectables in their constructor.
any coding logic. An ‘if’ clause in a class’s constructor that isn’t checking for nil
is a cry for that class to be split into two classes. (There are ways to check for nil
value parameters that don’t involve an if statement. We’ll cover the notion of “Never
accept nil” in a later chapter.) A complex constructor is a clear sign that your class
is doing too much. Keep constructors short, simple, and free of any logic.
Conclusion
Okay, so that should serve as a basic introduction to the idea of Dependency Injection.
Dependency Injection is a means to an end, and that end is loosely coupled code.
What is Dependency Injection? 9
Obviously there is more to it than that, hence the rest of this book. But if you
understand the notions that you should code against abstractions and that you should
ask for the functionality that you need, you are well on your way to understanding
Dependency Injection and to writing better code.
Benefits of Dependency
Injection
Why should we do all this? Why go to all the trouble to arrange our code in the
particular way called for by the principles of Dependency Injection? Well, because
there are benefits. Let’s talk a bit about those benefits of Dependency Injection,
because they are many and compelling.
Maintainability – Probably the main benefit of Dependency Injection is main-
tainability. If your classes are loosely coupled and follow the single responsibility
principle – the natural result of using DI – then your code will be easier to maintain.
Simple, stand-alone classes are easier to fix than complicated, tightly coupled classes.
Code that is maintainable has a lower total cost of ownership. Maintenance costs
often exceed the cost of building the code in the first place, so anything that improves
the maintainability of your code is a good thing. We all want to save time and money,
right?
Testability – Along the same lines as maintainability is testability. Code that is easy
to test is tested more often. More testing means higher quality. Loosely coupled
classes that only do one thing – again, the natural result of using DI – are very easy to
unit test. By using Dependency Injection, you make creating test doubles (commonly
called “mocks”) much easier. If dependencies are passed to classes, it’s quite simple to
pass in a test double implementation. If dependencies are hard-coded, it’s impossible
to create test doubles for those dependencies. Testable code that actually is tested is
quality code. Or at least it’s of higher quality than untested code. I find it hard to
accept the argument that unit tests are a waste of time – they are always worth the
time to me. (Surely I’m not the only one who finds it strange that this is even in
dispute?)
Readability – Code that uses DI is simpler. It follows the Single Responsibility Princi-
ple and thus results in smaller, more compact and to the point classes. Constructors
aren’t as cluttered and filled with logic. Classes are more clearly defined, openly
Benefits of Dependency Injection 11
declaring what they need. Because of all this, DI-based code is more readable. And
code that is more readable is more maintainable.
Flexibility – Loosely couple code – yet again, the result of using DI – is more flexible
and usable in different ways. Small classes that do one thing can more easily be
reassembled and reused in different situations. Small classes are like Legos(tm) –
they can easily be pieced together to make a multitude of things, as opposed to
say, Duplo(tm) blocks, which are bulkier and less flexible. Being able to reuse code
saves time and money. All software needs to be able to change and adapt to new
requirements. Loosely coupled code that uses Dependency Injection is flexible and
able to adapt to those changes.
Extensibility – Code that uses Dependency Injection results in a class structure that
is more extendable. By relying on abstractions instead of implementations, code can
easily vary a given implementation. When you code against abstractions, you can
code with the notion that a radically better implementation of what you are doing
is just around the corner. Small, flexible classes can be extended easily, either by
inheritance or composition. An application’s code base never remains static, and
you will very likely need to add new features as your code base grows and new
requirements arise. Code that is extensible is up to that challenge.
Team Development – If you are on a team and that team needs to work together
on a project (when is that not true?), then Dependency Injection will facilitate team
development. (Even if you are working alone, it is very likely that your work will be
passed to someone in the future). Dependency Injection calls for you to code against
abstractions and not implementations. If you have two teams working together,
each needing the other’s work, you can define the abstractions before doing the
implementations, and then each team can write their code using the abstractions,
even before the implementations are written. In addition, because code is loosely
coupled, those implementations won’t rely on each other, and thus they are easily
split between teams.
So there you have it. Dependency Injection results in maintainable, testable, readable,
flexible, and extensible code that is easily spread between team members. It seems
hard to imagine that any developer wouldn’t want all that.
A Closer Look at Coupling:
Connascence
Introduction
If you’ve been paying attention, you’ve noticed that I don’t like coupling. Tightly
coupled code makes me queasy. I talk about it in my blog posts, in my books,
everywhere. Tight coupling is bad. We know that we want loose coupling and should
avoid tight coupling. That’s sort of been a given, but there has been precious little
discussion of exactly what it all means. It’s been notoriously hard to describe. It’s
been an “I know it when I see it” kind of thing. Since Dependency Injection is all
about reducing coupling, I thought it would be a good idea to include this chapter
covering a bit more about what coupling is.
Coupling is a measure of the relationships and connections between two modules.
Code has to be coupled somehow or else it can’t do anything. But how are those
measurements taken? What exactly is being measured? These are tough questions.
Given that tight coupling is bad, we want to limit it as much as possible. But how,
exactly, do we do that? What the heck is coupling exactly?
Connascence
Luckily, there is a way to measure coupling. It’s called “connascence.” (I’ve heard it
pronounced “Cuh-NAY-since”) In the realm of software development, two modules
are said to be “connascent” if changing one requires a change in the other in order to
maintain the overall correctness of the system. That’s pretty much what we think of
when we think of coupling. The term was first used in this way by Meilir Page-Jones
for the purpose of being able to quantify and qualify exactly what coupling is. He
first discussed it in his book “What Every Programmer Should Know About Object-
Oriented Design”. The book was published in 1995, so this notion is nothing new.
A Closer Look at Coupling: Connascence 13
However, as is often the case, this twenty-year old idea is only now coming into the
fore. Connascence is a way of measuring the coupling in your code.
Connascence is discussed in two dimensions. First, there are nine different levels of
connascence. Second, those levels all have certain qualities. I’ll discuss those qualities
first, and then we’ll move on to talk about the levels of connascence. The levels of
connascence allow us to form a taxonomy of coupling and give us a vocabulary for
talking about it. This is really useful, as previously discussions of coupling generally
fell into a very amorphous discussion about “tight vs. loose” – hardly a scientific
view of things.
Qualities of Connascence
Connascence has three qualities: Strength, Degree, and Locality.
Strength of Connascence
One level of connascence is said to be stronger than another if fixing it requires more
in depth and more difficult changes. For instance, if reducing the coupling between
two entities requires an easy, simple change, then the connascence is said to be less
strong than connascence requiring a complex change. If a level of connascence is hard
to refactor, it is said to be a strong level of connascence. The Levels of Connascence
described below are listed by increasing strength. That ordering gives you an idea of
how to prioritize your refactoring. That is, you should work to refactor the strongest
couplings down to weaker levels of coupling.
Degree of Connascence
The degree of connascence is a measure of the level to which the connascence
occurs. Connascence can occur to a small degree or a large degree. For example, two
modules might be connected by multiple references as opposed to a single reference.
A connection of multiple references is said to have a high degree of connascence. A
given method might have many parameters which couple it to many external classes.
Such a method has a high degree of connascence.
A Closer Look at Coupling: Connascence 14
Locality of Connascence
Sometimes coupling occurs close together – you have two classes in the same
unit that are interconnected. Connascence can occur within a single method. But
sometimes that coupling occurs in two units that are very far apart from each other.
We’ve all seen this – you make a change in the “lower left” part of your application
and it has an effect far away in the “upper right” of the program. Connascence that
is close together is better than that which is far apart.
Levels of Connascence
Some coupling is required. Without it, nothing can happen in an application.
However, we want to keep the strength of coupling as weak as possible, the degree
as small as possible, and the locality as close as possible. If we could measure
the coupling, then we could know that we are doing that, right? Well, Page-Jones
described nine levels of connascence, each stronger, of higher degree, and/or of more
distant locality than the previous one. Once we recognize these levels of coupling,
we can do things to reduce them to a lower level. Let’s take a look and see how it all
works.
Static Connascenses
The first five levels of Connascenses are said to be static because they can be found
by visually examining your code.
Connascence of Name
Connascence of Name occurs when two things have to agree on the name of
something. This is the weakest form of connascence, and the one that we should
aspire to limit ourselves to. It’s almost obvious, and can’t be avoided. If you declare
a procedure:
procedure TMyClass.DoSomething
A Closer Look at Coupling: Connascence 15
you have to call it using the name DoSomething. Changing the name of something
requires changes elsewhere. If you want to change the name of the procedure, you
need to change it everywhere that you’ve called it. That seems obvious, and of course
this level of connascence is unavoidable. In fact, it’s desirable. It’s the lowest level of
coupling we can have, and so we should seek it out and use it the most. If we can
limit our coupling to Connascence of Name, we’d be doing very well.
Connascence of Type
Connascence of Type occurs when two entities have to agree on the type of
something. The most obvious example is the parameters of a method. If you declare
a function as follows:
then any calling code must pass a TWidget and a TWidgetActionType as parameters
of the ProcessWidget function and must accept a Boolean as a result type. Delphi is
strongly typed, so this type of connascence is almost always caught by the compiler.
Connascence of Type isn’t quite as weak as Connascence of Name, but it is still
considered a weak and acceptable level of connascence. Indeed, you couldn’t really
get along without it, could you – you have to be able to call a method of a class to
get anything done, and it’s not very burdensome to ensure that your types match. In
fact, in Delphi, you have to couple code via Connascence of Type to even compile
your code.
Connascence of Meaning
If you want to use the above code, then you have to know the meaning of the result
code for the GetWidgetType function. If you change one of the result types or add
a new one, then you need to change the code that uses this function wherever it is
used. And to make that change, you have to know the meaning of each result code.
The obvious solution here is to refactor the code to use constant names for the result
code, or better, an enumerated type that defines the result codes. This reduces your
connascence from Connascence of Meaning to Connascence of Name, a desirable
outcome. Remember, any time you can refactor from a higher to a lower level of
connascence, you have lowered coupling and thus improved your code.
Another example of Connascence of Meaning is the use of nil as a signal. Often,
developers use nil to mean “no value” or “Sorry, I couldn’t do/find/complete that,”
and your code has to handle that. As we’ll see in later chapters, this use of nil should
be avoided as it creates coupling via the Connascence of Meaning.
Connascence of Position
Connascence of Position occurs when code in two different places must agree on the
position of things. This most commonly occurs in parameter lists where the order
of parameters in a method’s parameter list is required to maintain that order. If you
A Closer Look at Coupling: Connascence 17
add a parameter in the middle of an existing parameter list, all uses of that method
must add the new parameter in the correct position.
Some languages allow you to name your parameters so that they can be included
in any order, but Delphi doesn’t allow this. Thus, it is required to couple code using
Connascence of Position when writing Delphi code.
Now, the degree of your Connascence of Position can be reduced by limiting the
number of parameters in any given routine. To limit Connascence of Position, you
can reduce a parameter list to a single type, thus moving from Connascence of
Position to Connascence of Type. Connascence of Type is a weaker coupling, and
so this is something you should try to do.
Here’s an example. Consider this routine:
In order to use AddUser you have to make sure that you get all the parameters in
exactly the right position. If you add a parameter in the middle, you need to make
sure that any use of AddUser puts that new value in exactly the right place.
We can reduce this example of Connascence of Position by refactoring it to use
Connascence of Type instead. For example:
type
TUserRecord = record
FirstName: string;
LastName: string;
Age: integer;
Birthday: TDateTime;
Address: TAddress;
Privileges: TPrivileges;
end;
Now we have reduced the coupling by creating a type and having the AddUser
procedure depend on the type of the parameter instead of the position of a long list of
parameters. By reducing the strength of the coupling, we’ve improved the code. This
technique is also referred to as a “Parameter Object.” (See http://refactoring.com/catalog/
introduceParameterObject.html)
A Closer Look at Coupling: Connascence 18
Connascence of Algorithm
Dynamic Connascenses
The next four levels of connascence are said to be “dynamic” because they can only
be discovered by running your code. These levels are stronger than the static ones
because they only reveal themselves at runtime, making them hard to spot and very
often harder to fix.
Connascence of Execution
UserRecord.FirstName := 'Alicia';
UserRecord.LastName := 'Florrick';
UserRecord.Age = 47;
UserManager.AddUser(UserRecord);
UserRecord.Birthday := EncodeDate(1968, 12, 3);
This code adds the Birthday value after the user has been added. This clearly won’t
work. Obviously this is noticeable by examining the code, but you could imagine a
more complex scenario that was harder to spot. Consider this code:
A Closer Look at Coupling: Connascence 19
SprocketProcesser.AddSprocket(SomeSprocket);
SprocketProcessor.ValidateSprocket;
Does the order of those two statements matter? Does the sprocket need to be added
and then validated, or should it be validated before it is added? It is hard to say, and
someone not well versed in the system might make the mistake of putting them in
the wrong order. That is Connascense of Execution.
Here’s another example. Imagine a queue that holds messages. The first message says
“Start list”. Then the next two messages have list items in them for adding items to
the list. Then, finally, the queue has a message that says “End list”. This works great
if you have a single worker thread pulling items out of the queue. All the items will
be pulled out in order. But what if you had multiple threads pulling items out of
the queue, and one of the threads worked a bit faster than the others, and then it
pulled the “End list” message off of the queue before the final “Here’s another item”
message was processed? That would be bad. That would also be an error caused by
Connascense of Execution.
Connascence of Timing
Connascence of Value
[Test]
procedure TestCheckoutValue;
var
PriceScanner: IPriceScanner;
begin
PriceScanner := TPriceScanner.Create;
PriceScanner.Scan('Frosted Sugar Bombs');
Assert.Equals(50, PriceScanner.CurrentBalance);
end;
So we’ve written the test. Now, in the spirit of Test Driven Development, I’ll make
the test pass as easily and simply as possible.
We now have tight coupling between TPriceScanner and our test. We obviously have
Connascense of Name, because both classes rely on the name CurrentBalance. But
that’s relatively low level, and perfectly acceptable. We have Connascence of Type,
because both must agree on the type TPriceScanner, but again, that’s benign. We
have Connascence of Meaning, because both routines have a hard coded dependency
on the number 50. That should be refactored. But the real problem is the Connascence
of Value that occurs because both of the classes know the price – that is, the “Value”
– for the price of Frosted Sugar Bombs. If the price changes, even our very simple
test will break.
The solution is to refactor to a lower level of connascence. The first thing you could
do is to refactor so that the knowledge of the price (the value) of the Frosted Sugar
Bombs is maintained in only one place:
[Test]
procedure TestCheckoutValue;
var
PriceScanner: IPriceScanner;
begin
PriceScanner := TPriceScanner.Create;
PriceScanner.Scan('Frosted Sugar Bombs', 50);
Assert.Equals(50, PriceScanner.CurrentBalance);
end;
And we now no longer have Connascence of Value between the two modules and
our test still passes. Excellent.
Connascence of Identity
Connascence of Identity occurs when two components must refer to the same object.
If the two modules refer to the same thing, and then one changes that reference, the
other object must change to the same reference. It’s often a subtle and difficult to
detect form of connascence. As a result, this is the most complex form of connascence.
Consider the following code:
program Identity;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
type
TReportInfo = class
private
FReportStuff: string;
procedure SetReportStuff(const Value: string);
public
property ReportStuff: string read FReportStuff write SetReportStuff;
end;
type
TInventoryReport = class
A Closer Look at Coupling: Connascence 22
private
FReportInfo: TReportInfo;
public
constructor Create(aReportInfo: TReportInfo);
property ReportInfo: TReportInfo read FReportInfo write FReportInfo;
end;
TSalesReport = class
private
FReportInfo: TReportInfo;
public
constructor Create(aReportInfo: TReportInfo);
property ReportInfo: TReportInfo read FReportInfo write FReportInfo;
end;
var
ReportInfo: TReportInfo;
NewReportInfo: TReportInfo;
InventoryReport: TInventoryReport;
SalesReport: TSalesReport;
begin
try
ReportInfo := TReportInfo.Create;
InventoryReport := TInventoryReport.Create(ReportInfo);
SalesReport := TSalesReport.Create(ReportInfo);
try
// Do Stuff with reports
NewReportInfo := TReportInfo.Create;
try
InventoryReport.ReportInfo := NewReportInfo;
// Do stuff with report
// But the reports now point to different ReportInfos.
// This is Connascence of Identity
finally
NewReportInfo.Free;
end;
finally
ReportInfo.Free;
InventoryReport.Free;
SalesReport.Free;
end;
except
A Closer Look at Coupling: Connascence 23
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Here we have two reports: an inventory report and a sales report. The domain
requires that the two reports always refer to the same instance of TReportInfo.
However, as you can see above, in the middle of the reporting process the Inventory
Report gets a new ReportInfo instance. This is fine, but the Sales Report needs to
refer to this new TReportInfo instance, as well. In other words, if you change the
reference in one report, the other report has to change to the same reference. This is
called Connascence of Identity, as the two classes must both change the identity of
their reference in order for the system to continue working correctly.
Conclusion
We all agree that tight coupling is bad (at least I hope we do!). But the notion of
coupling has typically been rather ill-defined. I hope this review of connascence – the
idea that if something changes in one place, something else has to change somewhere
else – will allow you to talk a bit more specifically about what coupling is. I also
hope that the Levels of Connascence will allow you find code with a high degree of
coupling and refactor those areas to lower your coupling. If you endeavor to reduce
the overall level of Connascence in your code, you’ll have a cleaner and easier to
maintain codebase.
Constructor Injection
Okay, so in the first chapter, you saw code that injected one class into another. This
reduced the coupling and made the dependency weaker. The class was injected via
the constructor. You’ll probably be stunned to know that this is called “Constructor
Injection.” In this chapter, I’ll talk a bit more in depth about constructor injection.
Constructor Injection
Constructor Injection is the process of using the constructor to pass in the dependen-
cies of a class. The dependencies are declared as parameters of the constructor. As a
result, you cannot create a new instance of the class without passing in a variable of
the type required by the constructor.
That last point is key – when you declare a dependency as a parameter on the
constructor, you are saying “I’m sorry, folks, but if you want to create this class,
you must pass in this parameter.” Thus, a class is able to specify the dependencies
that it requires and be guaranteed that it will get them. You can’t create the class
without them. If you have this code:
TPayrollSystem = class
private
FBankingService: TBankingService;
public
constructor Create(aBankingService: TBankingService);
end;
TPayrollSystem = class
private
FBankingService: TBankingService;
public
constructor Create(aBankingService: TBankingService);
end;
FBankingService := aBankingService;
end;
This code will never allow the internal field to be nil. It will raise an exception if
someone dares pass in nil as the value for the constructor’s parameter. This is how
it should be. You could accept nil, but then you’d have to check for it everywhere in
your code, and who wants that? In the immortal words of Barney Fife, you should
nip the use of nil in the bud by refusing to accept it at the entry point.
Checking for nil is boilerplate code, and the Spring4D framework provides a means
for easily doing a nil check. Protecting against nil being passed as a parameter
is called the “Guard Pattern,” and you won’t be surprised to know that Spring4D
provides a record called Guard that has a number of static methods that allow you to
check – or “guard against” – certain situations from occurring.
Constructor Injection 27
In the case at hand, we are using the Guard Pattern to protect against a parameter
being nil, so we can use the Guard class to simplify our code:
TPayrollSystem = class
private
FBankingService: TBankingService;
public
constructor Create(aBankingService: TBankingService);
end;
Guard.CheckNotNull takes two parameters. The first is the item to be checked, and
the second is the name of the item that was checked as a string. Now, if you pass nil
to the constructor, you’ll get this error:
Okay, enough about passing nil. You should have the message by now.
Constructor Injection 28
An example, of course, will show how things are done. Let’s take a look at code
that does what I described above – a document class that has an optional grammar
checker.
First, we’ll start with an interface:
type
IGrammarChecker = interface
['{9CA7F68C-8A42-4B8C-AD1A-14C04CAE0901}']
procedure CheckGrammar;
end;
Now, we’ll implement it twice. Once as a do-nothing default and again as a “real”
grammar checker.
type
TDefaultGrammarChecker = class(TInterfacedObject, IGrammarChecker)
private
procedure CheckGrammar;
end;
procedure TDefaultGrammarChecker.CheckGrammar;
begin
// do nothing, but we'll WriteLn just to prove we were here
WriteLn('Do nothing');
end;
procedure TRealGrammarChecker.CheckGrammar;
begin
WriteLn('Grammar has been checked');
end;
TDocument = class
private
FText: string;
FGrammarChecker: IGrammarChecker;
procedure SetGrammarChecker(const Value: IGrammarChecker);
public
constructor Create(const aText: string);
procedure CheckGrammar;
property Text: string read FText write FText;
property GrammarChecker: IGrammarChecker read FGrammarChecker write SetGrammarChecker;
end;
procedure TDocument.CheckGrammar;
begin
FGrammarChecker.CheckGrammar;
end;
• Its constructor takes the document text as a parameter. It’s then exposed as
a read/write property, so you can change it if you want.
• The constructor also creates an instance of the default grammar checker. Note
again that this creates a hard-coded dependency – one of the perils of Property
Injection. But the dependency is a do-nothing default and prevents us from
having constantly to check for nil.
• The setter for the GrammarChecker property contains a Guard call, ensuring
that the internal value for FGrammarChecker can never be nil.
One further thing that you might note is that the GrammarChecker
property could actually be a “write-only” property if you wanted it to
be. That is, you could only set the value and never actually read it.
Property Injection 32
You can create a write-only property when the value being written is
only used internally and will never be called by code outside of the
class. Something like the GrammarChecker might qualify as a write-
only property.
Now, here’s some code that exercises everything and shows Property Injection in
action:
procedure Main;
var
Document: TDocument;
begin
Document := TDocument.Create('This is the document text.');
try
WriteLn(Document.Text);
// Use the default, no-op grammar checker
Document.CheckGrammar;
// Change the dependency to use the "real" grammar checker
Document.GrammarChecker := TRealGrammarChecker.Create;
// Now the grammar checker is a "real" one
Document.CheckGrammar;
// This will -- and should -- raise an exception.
Document.GrammarChecker := nil;
finally
Document.Free;
end;
end;
Here are things to note about the above code: * It creates a document, taking some
text as a constructor parameter. * It calls CheckGrammar, but the default grammar
checker doesn’t do anything, so it says so in the console. * But then we use Property
Injection to inject a “real” grammar checker, and when we call CheckGrammar, the
grammar gets checked “for real.” * Then we try to set the grammar checker to nil,
but that raises an exception because of the Guard clause.
Thus, Property Injection allows you to provide optional dependencies. It also allows
you to change a dependency, if required. For instance, your document class may
take texts from different languages and thus will require that the grammar checker
changes as the document’s language changes. Property Injection will allow for this.
Method Injection
What if the dependency that your class needs is going to be different much of the
time? What if the dependency is an interface, and you have several implementations
that you may want to pass in to the class? You could use Property Injection, but then
you’d be setting the property all the time before calling the method that utilized the
frequently-changing dependency, setting up the possibility of temporal coupling.
Constructor and Property Injection are generally used when you have one depen-
dency that isn’t going to change often, so they aren’t appropriate for use when your
dependency may be one of many implementations.
This is where Method Injection comes in.
Method Injection allows you to inject a dependency right at the point of use, so that
you can pass any implementation you want without having to worry about storing
it for later use. It is often used when you pass in other information that needs special
handling. For example:
unit uPropertyInjectionMultiple;
interface
type
TRecipe = class
private
FText: string;
public
property Text: string read FText write FText;
end;
IFoodPreparer = interface
Method Injection 34
['{3900BE64-B0EC-4281-9D92-96B191FDE5BC}']
procedure PrepareFood(aRecipe: TRecipe);
end;
TRestaurant = class
private
FName: string;
public
constructor Create(const aName: string):
procedure PrepareFood(aRecipe: TRecipe; aPreparer: IFoodPreparer);
property Name: string read FName;
end;
implementation
end.
Method Injection 35
Here, we have the notion of a recipe, which may require a different preparer
depending on that recipe. Only the calling entity will know what the proper preparer
type will be for a given recipe. For instance, one recipe might require a short-order
cook, and another recipe might require a baker or a chef. We don’t know when
writing the code what kind of IFoodPreparer will be needed, and thus we can’t really
pass the dependency in the constructor and be stuck with that one implementation.
It is also clumsy to set a property every time a new or different IFoodPreparer
is required. And setting the property in such a way induces temporal coupling
(Connascence of Execution) and will suffer from thread-safety issues because it
would require a lock around the code in a threaded environment.
The best solution is to just pass the IFoodPreparer into the method at the point of
use.
Method injection should be used when the dependency could change with every use,
or at least when you can’t be sure which dependency will be needed at the point of
use.
Here’s an example of using Method Injection when the dependency needs to change
every time it is used. Imagine a situation where a car-painting robot requires a new
paint-gun tip after every car it paints. You might start out like this using Constructor
Injection:
type
IPaintGunTip = interface
procedure SprayCar(aColor: TColor);
end;
TCarPaintingRobot = class
private
FPaintGunTip: IPaintGunTip;
public
constructor Create(aPaintGunTip: IPaintGunTip);
procedure PaintCar(aColor: TColor);
end;
FPaintGunTip := aPaintGunTip;
end;
end;
Here, when we paint the car, we have to get a new paint gun tip. But how? When we
paint the car, the tip is no good anymore, but it’s an interface, and we have no way
to manually free it, and even if we did, what would we do the next time we need
to paint a car? We don’t know what kind of tip is needed for a given car, and if we
have properly separated our concerns, we don’t even know anything about creating
a new tip. What to do? Well, use method injection instead:
type
IPaintGunTip = interface
procedure SprayCar(aColor: TColor);
end;
TCarPaintingRobot = class
public
procedure PaintCar(aColor: TColor; aPaintGunTip: IPaintGunTip);
end;
begin
aPaintGunTip.SprayCar(aColor);
end;
Now, when we pass the dependency directly to the method, the interface goes out of
scope when we are done painting and the paint gun tip is destroyed. In addition, the
next time a car needs to be painted, the consumer will pass in a new tip, which will
be freed upon use. Method Injection to the rescue!
Thus Method Injection is useful in two scenarios: when the implementation of a
dependency will vary and when the dependency needs to be renewed after each use.
In both cases, it is up to the caller to decide what implementation to pass to the
method.
The Dependency Injection
Container
Up to this point I’ve only made glancing reference to the notion of a Dependency
Injection Container. I’ve done that on purpose for a couple of reasons. First, I wanted
to make sure that you understood Dependency Injection before we talked about
the Dependency Injection Container. As I noted at the start, Dependency Injection
and using a Dependency Injection Container are two very different things. You can
do Dependency Injection without ever touching a Dependency Injection Container.
That’s what we’ve been doing so far. Second, there is a lot of confusion about what
a Container is, how to use it, where to use it and when to use it. I wanted to delay
answering those questions as long as possible so that you get the idea of Dependency
Injection without the potential confusion of what the Container is and does.
But the time has come, and so now we’ll take a look at the famous – or infamous –
Dependency Injection Container.
DPR file? That’s a lot of Create calls!” Well, you wonder that because that is exactly
what will happen. If you use Dependency Injection in a dedicated manner – and you
should – your constructors will all end up at what is called the “Composition Root”
of your application. In a Delphi application, this is the main block of the DPR file
– the place where every Delphi application starts. And it is there that you should
use the Container. It is there that you should make a single call to the Container to
Resolve the entire object graph for your application.
You should delay the connecting of your classes together – your Object Composition
– as long as possible. The longer you delay it, the more flexible you can be and the
more freedom you have in deciding how to do it. Doing this – delaying things – is
what makes the use of a DI Container possible. You can delay things to a single point
at the very root of your application and then let the DI Container do the composing
for you.
and if you use plain old Dependency Injection, the compiler will most often tell you
if you’ve made a mistake. However, with a DI Container, you lose that. Because your
object graph is created and bound at run-time, you’ll get runtime errors if you make
a mistake. If you forget to register an implementation for a given interface, you won’t
know that until you try to get that missing implementation at run-time. This is not a
small cost. You will definitely need to take care to ensure that your code is properly
composed to avoid this.
However, if one uses a DI Container properly, the benefits should far outweigh the
costs. Those benefits are what I’ll talk about in the rest of this book.
MyContainer.RegisterType<TMyClass>;
That’s it. Now your Container knows about TMyClass and can retrieve an instance of
TMyClass whenever it needs to. If another class has TMyClass as a dependency, the
Container will know how to get it automatically. So, for instance, say you register
another class that has TMyClass injected in the constructor:
The Dependency Injection Container 43
type
TAnotherClasss = class
constructor Create(aMyClass: TMyClass);
end;
....
MyContainer.RegisterType<TAnotherClass>
Then if you reference TAnotherClass, the Container will simply provide you with
an instance of TMyClass for your constructor. No need for you to do anything. The
Container will see your need, find the most specific constructor that has all the
dependencies it knows about, and create the class for you. In fact, it will do all this
before you even ask for it. This is all part of “creating your object graph” for you. This
is a simple example of how the Container is more than just a bag of classes waiting
to be used.
Interface Implementation
I mentioned previously that you should always code against an abstraction – usually
an interface – and not an implementation. Well, the Container makes that really easy
to do. By registering an interface and an implementation together, you can tell the
Container that a given class implements an interface, and then, as above, when you
need an implementation for a given interface, the Container will supply it:
MyContainer.RegisterType<ILogger, TLogger>;
Astute readers might note that this is a different syntax for registering
classes against interfaces from my previous books. Previously, you
registered them using the Implements method, but the above way to
do the registration is now the preferred method for associating a class
and an interface.
The code above basically says “Register the ILogger interface and use the TLogger
implementation for it.” In this way, you can use interfaces when doing Dependency
Injection, and the Container will support you in your quest to always code against
abstractions.
The Dependency Injection Container 44
MyContainer.RegisterType<ILogger, TFileLogger>('file');
MyContainer.RegisterType<ILogger, TConsoleLogger('console');
MyContainer.RegisterType<ILogger, TDatabaseLogger('database');
Given the above, you can choose your desired implementation from the three
registrations based on the name.
Lifetime Management
One of the key features of the Container is the ability to manage the lifetime of
objects that it supplies to you. In Delphi, of course, you are responsible for ensuring
the proper disposal of the memory allocated by the construction of an object. If you
have the Container allocate the object for you, then you can instruct the Container
on how to manage the lifetime of that object.
In Delphi, you are generally responsible for freeing any object that you create. In
the VCL, there is the exception that any VCL object that is owned by another VCL
object will be freed by the owning object. In addition, if your object is referenced
as an interface, the compiler will do reference counting and automatically free your
object when the reference goes completely out of scope and the reference count goes
to zero.
But if you turn the responsibility of creating your object graph over to the Depen-
dency Injection Container, then the Container takes on the task of managing the
lifetime of the objects it creates. This means you don’t have to do it because the
Container is responsible for an object’s lifetime. It also means that you need to let
the Container know how you’d like to have the lifetime of your objects managed.
You can do this with calls to the Container as defined by the table below:
The Dependency Injection Container 45
Type Description
AsTransient This is the default. It creates one instance per
request, and that request lives as long as the
variable is in scope. In other words, a new
instance is created for every request.
AsSingleton Creates a single instance of the class and
returns that instance for every request made
for that class. That single class is used for all
references to the object.
AsSingeltonPerThread Same as AsSingleton, but creates an
instance on a per thread basis.
AsPooled Creates a pool of objects of a
user-configurable size and then hands out
instances from that pool.
By now you might have noticed that the Spring4D Container uses the fluent
interface. A look into the source code shows that each call to the Container
always returns an instance of TRegistration<T>, enabling you to chain calls to the
Container together. TRegistration<T> is a class that manages all the functionality
of the Container, while at the same time allowing you to create a single statement
that defines an entire registration of a class. So, for instance, you can have statements
like the following:
which will register a class against an interface and declare it to be a singleton, as well
as inject a property value and a field value. It’s a powerful and easy to read way to
declare dependencies. Fluent interfaces often read like sentences.
If you want to have your reference be a singleton – that is, one instance for all calls
to the given registration – then declare it as follows:
MyContainer.RegisterType<IWeapon, TSword>.AsSingleton;
That will ensure that whenever you ask for an IWeapon, you will be given back the
same exact instance of TSword for all requests.
The Dependency Injection Container 46
AsSingletonPerThread does what its name implies – it provides the same instance
to each request within a given thread. There might be multiple instances of the
implementation, but each thread will always get its own instance.
AsTransient is the default behavior. AsTransient will create a new instance for each
request made. Assuming that you are using an interface reference, that transient
instance will live as long as the interface remains in scope (i.e., as long as its reference
count is greater than zero, presuming you are using “normal” reference counting).
Transient is the least efficient of the lifetime management choices, because it can
cause the Container to create a large number of classes that live a long time.
AsPooled will create a pool of instances for use upon request. You can determine
the minimum and maximum number of items in the pool. When an item is resolved
by the Container, it will be retrieved from the pool of items. All the items in the
pool are created when the Container is built, and thus all are available when the
program begins. Use pooling when creation is expensive and you want to pay that
cost up front or when you have a limited number of resources and want to ensure
that no more instances of the class representing the resource are created than there
are resources. Pooled items are never shared, so this may be a reason to choose it, as
well.
DelegateTo
Sometimes, the constructor of your class might not perfectly cooperate with the
Container and be a resolvable dependency. Consider the following class:
type
IWindowsUser = interface
['{432973CE-CDDF-45CC-9BA0-EC089F23EAF4}']
function GetUserName: string;
property UserName: string read GetUserName;
end;
Use DelegateTo when the Container otherwise won’t know how to create an instance
of a class. This might happen because the constructor’s parameters are not resolvable
by the Container, or because the information needed by the constructor is dependent
on external information that can’t be stored in the Container.
There are some caveats to using DelegateTo. You should be very careful to limit the
use of DelegateTo and find ways for the Container to solve this problem. Otherwise,
The Dependency Injection Container 48
you could very easily end up with many calls to DelegateTo which themselves
merely call the Container. This really becomes just another form of Pure DI, but
with the overhead of the container included.
Conclusion
That’s the basics of the Container. A DI Container is an extremely useful tool. It
can allow you to, in a single spot with a single line of code, compose your entire
object graph. Thus, your application can be made up of many loosely coupled classes
that ask for their dependencies and that know nothing of the Container itself. Later,
I’ll cover some more advanced topics that super-charge what you can do with the
Container, but for now you should have enough tools to take a look at a basic example
that illustrates how the Container works and how it should be used. That’s what I’ll
do in the next chapter.
A Step-by-Step Example
So far we’ve looked at simple examples that show off the Container’s specific
capabilities. But how does all of this really work? What are we really talking about
here? In this chapter, we’ll take a look at an example that starts out “normal” – how
things are traditionally done – and then move on to show how it can be refactored
to be clean and decoupled, as well as easy to test and maintain by taking advantage
of the power of a Dependency Injection Container.
The Beginning
The code for this chapter can be found at: http://bit.ly/diidcode
First, we’ll start off at the root of our demo application, the call to DoOrderProcess-
ing, which is called in the project’s DPR file:
procedure DoOrderProcessing;
var
Order: TOrder;
OrderProcessor: TOrderProcessor;
begin
Order := TOrder.Create;
try
OrderProcessor := TOrderProcessor.Create;
try
if OrderProcessor.ProcessOrder(Order) then
begin
WriteLn('Order successfully processed....');
end;
finally
OrderProcessor.Free;
end;
finally
Order.Free;
end;
end;
A Step-by-Step Example 50
Here, we see some pretty typical code. An order is created and then processed by
an order processor. Once the processing is done, the processor is freed. Very simple,
basic code. You probably have done something like this a million times.
What does TOrderProcessor look like?
type
TOrderProcessor = class
private
FOrderValidator: TOrderValidator;
FOrderEntry: TOrderEntry;
public
constructor Create;
destructor Destroy; override;
function ProcessOrder(aOrder: TOrder): Boolean;
end;
constructor TOrderProcessor.Create;
begin
FOrderValidator := TOrderValidator.Create;
FOrderEntry := TOrderEntry.Create;
end;
destructor TOrderProcessor.Destroy;
begin
FOrderValidator.Free;
FOrderEntry.Free;
inherited;
end;
Here are some things to note about the declaration and implementation of TOrder-
Processor:
Note that we are no longer responsible for the lifetime of the dependencies within
the function – the caller is – and so the destructor has gone.
This, of course, changes our DoOrderProcessing procedure a bit, as we’ve “pushed
back” the creation to closer to the base of the application:
A Step-by-Step Example 52
procedure DoOrderProcessing;
var
Order: TOrder;
OrderProcessor: TOrderProcessor;
OrderValidator: TOrderValidator;
OrderEntry: TOrderEntry;
begin
Order := TOrder.Create;
try
OrderValidator := TOrderValidator.Create;
OrderEntry := TOrderEntry.Create;
OrderProcessor := TOrderProcessor.Create(OrderValidator, OrderEntry);
try
if OrderProcessor.ProcessOrder(Order) then
begin
WriteLn('Order successfully processed....');
end;
finally
OrderProcessor.Free;
OrderValidator.Free;
OrderEntry.Free;
end;
finally
Order.Free;
end;
end;
Here, we create the order validator and the order entry classes in the “root” of the
application. (DoOrderProcessing is what we call in the DPR file). Already by this
simple change we’ve made TOrderProcessor a bit more flexible. While we still
couple ourselves to the specific implementations, TOrderProcessor itself is no longer
tied specifically to any given instance of TOrderValidator and TOrderEntry. It can
accept a fake of that class, or any descendent of the respective classes. It’s not much,
but it’s something.
Coding to an Interface
How do we loosen this coupling even further? We introduce interfaces, of course.
Remember, we always want to code to an interface, not an implementation. So far
we’ve been coding to an implementation, and you can see the limitations that this is
causing – we’re tied to those implementations. Let’s untie ourselves.
First, we’ll declare some interfaces:
A Step-by-Step Example 53
IOrderValidator = interface
['{CF8834A3-F815-4F6B-A177-7AB801BEC95E}']
function ValidateOrder(aOrder: TOrder): Boolean;
end;
IOrderEntry = interface
['{406EA68D-0733-429E-9E48-73BC660B1C72}']
function EnterOrderIntoDatabase(aOrder: TOrder): Boolean;
end;
IOrderProcessor = interface
['{C690B9D5-8C26-4DFE-AD27-2D7A4610ACBC}']
function ProcessOrder(aOrder: TOrder): Boolean;
end;
Once these have been declared, we can code against them instead of against
implementations:
type
TOrderProcessor = class(TInterfacedObject, IOrderProcessor)
private
FOrderValidator: IOrderValidator;
FOrderEntry: IOrderEntry;
public
constructor Create(aOrderValidator: IOrderValidator; aOrderEntry: IOrderEntry);
function ProcessOrder(aOrder: TOrder): Boolean;
end;
First, we’ve changed all the references to the order validator and the order entry
classes to be interfaces instead of class types. This means that now we really can pass
A Step-by-Step Example 54
procedure DoOrderProcessing;
var
Order: TOrder;
OrderProcessor: IOrderProcessor;
OrderValidator: IOrderValidator;
OrderEntry: IOrderEntry;
begin
OrderValidator := TOrderValidator.Create;
OrderEntry := TOrderEntry.Create;
Order := TOrder.Create;
try
OrderProcessor := TOrderProcessor.Create(OrderValidator, OrderEntry);
if OrderProcessor.ProcessOrder(Order) then
begin
WriteLn('Order successfully processed....');
end;
finally
Order.Free;
end;
end;
This is already a lot simpler. We are still hard-coded to the specific implementations
of TOrderValidator and TOrderEntry, but we are another step removed from that
coupling because their references are interfaces. The code is already looking a lot
cleaner as a result.
Those two Create calls can be “pushed back” even farther – let’s put them right in
the composition root so that DoOrderProcessor doesn’t require the tight-coupling to
them. Here’s the new DoOrderProcessing:
A Step-by-Step Example 55
and here’s the code in the DPR file that now calls it:
var
OrderValidator: IOrderValidator;
OrderEntry: IOrderEntry;
begin
try
OrderValidator := TOrderValidator.Create;
OrderEntry := TOrderEntry.Create;
DoOrderProcessing(OrderValidator, OrderEntry);
ReadLn;
except
on E: Exception do
WriteLn(E.ClassName, ': ', E.Message);
end;
end.
At this point, we’ve carried Dependency Injection as far as we can. We’ve pushed the
creation of our objects all the way to the Composite Root of our application – in this
case, the main block of the DPR file in Delphi. Here we create all of our dependencies
right at the very beginning of the DPR file. We can’t push things any farther back
than that.
Now, that’s all great and everything – all of our dependency creation is centralized
at the root of the application – but this can lead to a big problem. If our application
got much more complicated and we kept following this pattern, we’d have a huge
stack of things being created in the DPR file. This would be fine, but it could get
really clumsy really fast. Clumsy is not a word I’d like to describe my code. Oh, if
only there were a way to manage all that creation!
A Step-by-Step Example 56
unit uRegistration;
interface
procedure RegisterClassesAndInterfaces;
implementation
uses
Spring.Container
, uOrderEntry
, uOrderValidator
, uOrderProcessor
;
end.
What we’ve done here is pretty straight-forward. First, we’ve registered the TOrder-
Processor type with the container. This lets the Container know about the class
and, as we’ll see, lets the Container resolve all of its dependencies automagically. In
addition, we register the two classes to which we hold dependencies as implementing
specific implementations. This will cause the container to resolve references to the
interfaces using the concrete classes registered against them. The Container, when
asked for an instance of IOrderValidator, will be able to supply a valid instance of
TOrderValidator to implement it. Pretty cool. Finally, since all the classes can be
A Step-by-Step Example 57
singletons – they will always be asked to do the same exact thing – we register them
with the lifetime management call of AsSingleton.
And now that we’ve registered these classes and interfaces, all kind of cool things
happen. The first one is that the DPR file becomes really simple. The heart of the
DPR ends up looking like this:
var
Container: TContainer;
begin
try
Container := TContainer.Create;
try
RegisterClassesAndInterfaces(Container);
DoOrderProcessing;
ReadLn;
finally
Container.Free;
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
• If you’ve downloaded and run the demo application for this chapter, you’ll note
that the code all runs as expected. At first glance, that might be a surprise. But
of course, all will be explained.
• The variables for the two dependencies have disappeared – we don’t need
them anymore as we shall see.
• The parameters on DoOrderProcessing have disappeared, as well. We don’t
need them either.
• Nothing is getting created in code. That’s a bit mysterious. But again, all will
be explained.
• There is a call to Container.Build. This call is required to let the Container
know to build the object graph. That’s all you need to get the Container to do
its magic. You should make this call once and only once at the very root of the
application, as we’ve done here. Calling it more than once will merely cause
the process to run twice and waste CPU cycles.
A Step-by-Step Example 58
The really cool stuff happens within the call to DoOrderProcessing. Here it is in its
current incarnation:
Notice, again, that the parameters are gone. Then notice that the reference to
OrderProcessor is satisfied by a single call to aContainer.Resolve. This single
call does quite a few things. First, you’ll notice that it completely instantiates an
implementation of IOrderProcessor – in this case, an instance of TOrderProcessor.
And this is where the “magic” happens that makes the Container more than just a
factory.
Remember the declaration of TOrderProcessor?
As you can see, the constructor takes two dependencies. So all this code runs, and
nowhere do we create instances to attach to those two interface parameters. What is
going on here?
What is going on is that the Container is smart enough to know what to do. Since
you’ve registered implementations for both of those interfaces, the Container can
A Step-by-Step Example 59
• Drastically reduce the amount of code we have to write. Because the container
does all the creation, we don’t have any calls to Create. The less code we have
to write, the less code we have to maintain. That’s a win.
• Make our code very loosely coupled. Note that all of the classes stand alone,
each in their own units. We can put the interfaces into their own unit, and the
only place that things come together is in the unit that registers everything
with the container, and even there the relationship is merely between an
interface and the implementation. That’s pretty thin and thus results in loose
coupling. Put another way, we only have Connascence of Name to worry
about.
• Testing gets to be really easy because each class stands alone and clearly
declares its dependencies. Clearly declared dependencies means that you
can easily substitute fakes for those dependencies, making the classes easily
testable.
By the way, if you register a class that has more than one constructor,
the container will look at each one and find the most specific one that
it can resolve. That is, if you have a constructor with one resolvable
dependency as well as one with two resolvable dependencies, then the
A Step-by-Step Example 60
container will use the second one. However, I recommend that your class
have just one constructor that declares all the dependencies in the class.
Conclusion
And that is how the Container can build an entire object graph while only making
one Resolve call to the Container. Doing so results in some really sweet code. By
using the Container to do all of our work, we save ourselves from writing a lot of
plumbing code like creating and destroying objects, allowing us to focus on writing
business logic. And as I’ve beaten into you now, your code will be loosely coupled
and thus easy to maintain and test.
 
Advanced Container Use
So far, I’ve shown you the basics of Dependency Injection, including Constructor
Injection, Property (or Setter) Injection, and Method Injection. I’ve taken a look at
the basics of how the Dependency Injection container works, and shown you a basic
example of the Container doing its “magic.” Now it is time to take a look at some
advanced things that the container can do to make your code even more powerful.
type
IFruitPicker = interface
['{DD861C60-D9A0-411C-8448-2E3B798026DB}']
procedure PickFruit;
end;
Now that we have an interface to program against, we can pick fruit any way we
like. Here are three implementations:
Advanced Container Use 62
type
THumanFruitPicker = class(TInterfacedObject, IFruitPicker)
procedure PickFruit;
end;
procedure THumanFruitPicker.PickFruit;
begin
WriteLn('Carefully hand-pick the fruit.....');
end;
procedure TMechanicalFruitPicker.PickFruit;
begin
WriteLn('Pick the fruit with a mechanical device....');
end;
procedure TAndroidFruitPicker.PickFruit;
begin
WriteLn('Pick the fruit with android-like robots....');
end;
end.
We can now have humans, machines, and android robots pick the fruit. How can we
register all of these fruit-picking methods with the container and choose one to pick
the fruit? Well, as follows:
Notice that each of the three registrations includes a unique string that identifies
the registration. That way, we can choose the implementation we want by name
at runtime. Notice, too, that the THumanFruitPicker has been declared with the
AsDefault method. That means that THumanFruitPicker will be the one used if a
request is made for an IFruitPicker without specifying a name.
Here is the DPR file that allows you to choose which fruit picker to use:
Advanced Container Use 63
type
TPickerType = (human, mechanical, android);
var
Name: string;
Selection: integer;
FruitPicker: IFruitPicker;
Container: TContainer;
begin
try
Container := TContainer.Create;
try
RegisterFruitPickers(Container);
Here we declare a simple enumeration that defines the three fruit pickers. We then
allow you to choose a number for the fruit picker you want and use a little type
info (from the TypInfo.pas unit) to get a string that we then use to resolve the
IFruitPicker interface with the implementation that we want.
Running the application and selecting “2”, the machine picker, gets the following
result in the console:
Thus, you can register multiple implementations for a given interface and choose
whichever one you want whenever you want.
Lazy Initialization
When you call Container.Build, the Container will build the entire object graph
all at once. This is fine – your application should be at the ready to run things
when they are needed. However, sometimes a given implementation may be costly to
create, or maybe it’s an implementation that is unlikely to be used during the normal
running of your application. In that case, you may want to do “lazy” initialization of
the implementation. Lazy initialization means that the given object’s construction is
delayed until it is actually requested. This way, you can perhaps save some resources
that don’t need to be allocated right away, or prevent those resources from being
allocated at all if they are rarely used.
The Spring4D Framework provides a means for you to do lazy initialization via the
Lazy<T> record. You can declare your type as Lazy, and the framework won’t create
the given type until it is asked for.
I think an example is in order. Database connections often take time to create and
often you want to create them only when needed. Let’s take a look at an example of
the lazy initialization of a database connection.
As always, we’ll start by declaring an interface:
type
IDatabaseConnector = interface
['{8E00247F-F910-41C1-9122-523F2CB6E5CB}']
procedure Connect(const aName: string);
end;
This is just a demo, so we won’t be making real database connections, but we’ll
simulate it by causing a bit of a delay when pretending to “connect.” So, given that,
here is an implementation of our IDatabaseConnector interface:
Advanced Container Use 65
constructor TDatabaseConnector.Create;
begin
inherited Create;
FConnected := False;
Connect(Self.ClassName);
end;
This class implements the IDatabaseConnector interface, but takes a bit of time to
connect. It “connects” by writing out to the console, waiting three seconds, and then
writing again to the console that it is completed. Again, this is a demo connection,
but it is sufficient for our purposes, as you shall see.
The real fun starts when we declare this type in a class that actually uses it. For
instance, here’s a class, TDatabaseConnectionManager, that accepts an IDatabaseC-
onnector parameter in the constructor, which populates a field in the object.
Advanced Container Use 66
TDatabaseConnectionManager = class
private
FDatabaseConnector: IDatabaseConnector;
public
constructor Create(aDatabaseConnector: IDatabaseConnector);
end;
This class, when created and passed an instance of IDatabaseConnector, will store
that instance away for later use. But if we register everything with the Container:
procedure RegisterStuff(aContainer:TContainer);
begin
aContainer.RegisterType<TDatabaseConnector, IDatabaseConnector>;
aContainer.RegisterType<TDatabaseConnectionManager>;
aContainer.Build;
end;
TDatabaseConnectionManagerLazy = class
private
FDatabaseConnector: Lazy<IDatabaseConnector>;
public
constructor Create(aDatabaseConnector: Lazy<IDatabaseConnector>);
procedure ConnectToDatabase;
end;
procedure TDatabaseConnectionManagerLazy.ConnectToDatabase;
begin
FDatabaseConnector.Value.Connect(Self.ClassName);
end;
Advanced Container Use 67
WriteLn('Everything is registered');
ReadLn;
WriteLn('Note that Connection is made without anything being done on your part.');
WriteLn('The connection occurs as part of the magic of the Container.');
WriteLn('All Done');
WriteLn;
ReadLn;
WriteLn('Note that connection is not made until specifically asked for by you hitting Return.');
ReadLn;
WriteLn('Okay, now you have asked for the connection, and it will be made');
DatabaseConnectionManagerLazy.ConnectToDatabase;
WriteLn;
WriteLn('All Done');
WriteLn;
end;
the IDatabaseConnector interface and the two classes that use the interface,
one “regularly” and one “lazily.”
• Once that happens, the application reports that everything is registered and
calls ReadLn so you can see that all is registered and waiting for use.
• Then, when you hit the Enter key, it tells you it’s about to create a TDatabaseC-
onnectionManager and does so.
• At this point, you should see the connection being made. There is no delay –
the connection is made right way, even before you do anything. This happens
because the Container automatically creates everything upon the call to Build.
Note it takes three seconds for the connection to be made as we simulate a
delay in connecting.
• Next up, though, is TDatabaseConnectionManagerLazy. This class declares the
database connection as Lazy, and thus it isn’t created until you specifically
ask for it. We can Resolve an instance of it via the Container without the
Container creating and connecting. It only connects when we actually call
ConnectToDatabase and first ask for the IDatabaseConnector. You can see
this in action when you press Return and nothing happens until you press
Return a second time.
• The application reports all of this to you as it is happening, so if you read the
output carefully you can see what is happening as it happens. Remember, you
need to wait the three seconds for the connection to be made. If you want, you
can wait a really long time to make sure that the connection isn’t made until
it is specifically requested.
• You can tell – both times – when the connection is created because the
application writes “All Done” to the console when it’s finished.
There are only two basic differences between the two connection manager classes.
The first one – TDatabaseConnectionManager – doesn’t do anything special – it
merely stores the reference to IDatabaseConnector for later use. It is the second class
– TDatabaseConnectionManagerLazy – that has the differences. First, it declares its
reference to IDatabaseConnector as Lazy, ensuring that it is only created when first
referenced in code. Second, it does that actual referencing in the ConnectToDatabase
method. (Note that the Lazy record has a property called Value which contains
the actual reference to the lazily created instance.) It is at that point that the
implementation is assigned and the class actually instantiated.
Advanced Container Use 69
Registering Factories
Okay, that’s all great so far. But you’ve likely noticed an inconvenient fact: All of our
dependencies have been registered interfaces. What if our constructor needs strings
or integers or other primitive types?
Well, the Container has a means of dealing with that. It enables you to register a
“factory” to tell the Container how to create the class with constructor parameters
that aren’t registered.
ICoffeeMaker = interface
['{73436E03-EF65-44F5-9606-F706156CBEB5}']
procedure MakeCoffee;
end;
But as we said, the implementation needs a coffee type and a brewing time, so we
get this:
type
TCoffeeMaker = class(TInterfacedObject, ICoffeeMaker)
private
FCoffeeBrand: string;
FBrewingMinutes: integer;
public
constructor Create(const aCoffeeBrand: string; const aBrewingMinutes: integer);
procedure MakeCoffee;
end;
procedure TCoffeeMaker.MakeCoffee;
Advanced Container Use 70
begin
WriteLn('Pour hot water over the ', FCoffeeBrand, ' so that it brews for ', FBrewingMinutes, ' minut\
es.');
end;
Now this class, as it stands, doesn’t need to be put into the Container, but we can
easily imagine the notion of, say, a TKitchen class that would take TCoffeeMaker as
a dependency, and thus the class would need to be registered with the container for it
to properly resolve. But again, the constructor takes a string and an integer. What
to do?
Well, we can register a factory to tell the Container how to create the coffee maker.
This type of factory takes the form of an anonymous method:
type
{$M+}
TCoffeeMakerFactory = reference to function(const aCoffeeBrand: string; const aBrewingMinutes: integ\
er): ICoffeeMaker;
{$M-}
It’s easy to think of an anonymous function as a factory. It’s a “blueprint” for creating
a specific thing, an implementation for ICoffeeMaker – in this case, TCoffeeMaker.
Notice, too, that the blueprint’s signature matches that of the constructor for
our implementing class. This is not a coincidence, as you can imagine. For the
anonymous method to be a factory as far as the Container is concerned, it must
have {$METHODINFO} turned on. Once we have declared the anonymous function, we
can register it as a factory with the Container:
procedure RegisterStuff(aContainer: TContainer);
begin
aContainer.RegisterType<ICoffeeMaker, TCoffeeMaker>.AsDefault;
aContainer.RegisterFactory<TCoffeeMakerFactory>;
aContainer.Build;
end;
var
CoffeeName: string;
BrewingMinutes: integer;
CoffeeMakerFactory: TCoffeeMakerFactory;
CoffeeMaker: ICoffeeMaker;
begin
Write('What kind of coffee do you want to make? ');
ReadLn(CoffeeName);
CoffeeMakerFactory := Container.Resolve<TCoffeeMakerFactory>();
CoffeeMaker := CoffeeMakerFactory(CoffeeName, BrewingMinutes);
CoffeeMaker.MakeCoffee;
end;
This code is a bit interesting, and so you should notice the following:
By the way, if you are really feeling adventurous, you can do all that in one line of
code:
Advanced Container Use 72
Container.Resolve<TCoffeeMakerFactory>()(CoffeeName, BrewingMinutes).MakeCoffee;
Now we have a class registered with the Container that can be resolved with its
constructor parameters, whatever type they are. Nice.
But now you are thinking (I know this because you are really smart, and of course
you are thinking this) “What if I have, say, two implementations of ICoffeeMaker
and they have different constructor parameter lists?” That isn’t a problem. You just
need to do a bit more work. First, here is a different implementation with a different
constructor parameter list:
procedure TCupCoffeeMaker.MakeCoffee;
begin
WriteLn('Put the ' + FCupType + ' cup in the coffee maker and press the "Brew" button');
end;
This one is a “cup” style coffee maker that only needs the type of the cup in the
constructor, so here is what our factory declarations look like now:
type
{$M+}
TCoffeeMakerFactory = reference to function(const aCoffeeBrand: string; const aBrewingMinutes: integ\
er): ICoffeeMaker;
TCupCoffeeMakerFactory = reference to function(const aCupType: string): ICoffeeMaker;
{$M-}
aContainer.RegisterFactory<TCoffeeMakerFactory>.AsFactory('regular');
aContainer.RegisterFactory<TCupCoffeeMakerFactory>.AsFactory('cup');
aContainer.Build;
end;
• First, the RegisterType calls have names, since there is more than one class
implementing ICoffeeMaker.
• Next, note that the call to RegisterFactory has a call to AsFactory which
takes as a parameter the name of the implementation for that factory. This is
required so that the Container knows which implementation to associate with
which factory.
• Finally, the rest of the code is what you expect. You get a reference to the
factory (which of course is an anonymous method) and then call it, passing in
the cup type that you asked for previously.
type
TPerson = class
private
FOccupation: string;
FName: string;
FAge: integer;
function GetName: string;
function GetAge: integer;
function GetOccupation: string;
public
constructor Create([Inject('name')]aName: string; [Inject('age')]aAge: integer; [Inject('occupatio\
n')]aOccupation: string);
property Name: string read GetName;
property Age: integer read GetAge;
property Occupation: string read GetOccupation;
end;
It’s nothing remarkable, except that each of the constructor parameters is tagged with
an [inject] attribute. They have names attached as a parameter that will identify
them to the container. These attributes will point to registrations of their primitive
types in the container. Those registrations look like this:
aContainer.RegisterType<integer>('age').DelegateTo(
function: integer
begin
Result := Random(100);
end
);
aContainer.Build;
Advanced Container Use 75
TempName := aContainer.Resolve<TPerson>;
WriteLn(TempName.Name, ' is ', TempName.Age, ' years old', ' and is a ', TempName.Occupation);
ReadLn;
end;
There should be nothing unfamiliar here – you probably can figure out what is going
on. The types being registered are primitives – two strings and an integer – and they
are registered using the same string values we saw in the attributes. We then call
DelegateTo to define an anonymous function that tells the container how to obtain
the strings and the integer. (Note that we use the call GetLocalUserName from the
previous chapter to get the name value.)
The difference here from all the code up to this point is that we are registering
primitive types. Previously, we’ve registered classes, but here we register arbitrary
primitive types. You can register any type that you like as long as you provide a name
for the registration and an anonymous method to tell the container what the value
of the primitive should be. As shown, you then label the parameters (they could be
parameters on a method if you like) and the values are automatically set for us by
the Container. Now you have another way of making a class completely resolvable
inside the container.
Attributes
I start this section assuming that you know how attributes work in Delphi. (If not, feel
free to read all about them in my book, “Coding in Delphi”.) The Spring for Delphi
framework provides a number of attributes that can be used in place of registration
calls.
[Inject] Attribute
type
IHorse = interface
['{BDBB0FE7-D369-4EC7-B2A0-FC012136B87E}']
procedure Ride;
end;
ICowboy = interface
['{337002BB-2219-46A2-BF28-5CFD8A6873AD}']
procedure SetHorse(aValue: IHorse);
function GetHorse: IHorse;
procedure DoCowboyStuff;
property Horse: IHorse read GetHorse write SetHorse;
end;
implementation
procedure TCowboy.DoCowboyStuff;
begin
Advanced Container Use 77
procedure THorse.Ride;
begin
WriteLn('Gallop along the prairie!');
end;
There is nothing in this code that shouldn’t be familiar. The “work” gets done in
the registration code with the call to InjectProperty. That tells the Container that
there will be a property named ‘Horse’ that will be resolved by the given registration
call. This allows you to never call Create anywhere in your class and instead let the
Container do that work for you.
Now, you can do almost exactly the same thing with the following:
Advanced Container Use 78
I say “almost” the same thing, because when you use the attribute, you specify
precisely which property you want to associate with the IHorse registration. If you
use InjectProperty, the Container will associate that with any property on any class
registered with the container that has a property named “Horse” of type IHorse. The
attribute, on the other hand, limits the association to the property tagged with the
attribute. This is a subtle but important difference.
Lifetime Attributes
Earlier, we discussed how the Container can be instructed to manage the lifetime of
the instances that it returns and assigns for you. As I showed you then, this was done
with a call during the registration process:
Container.RegisterType<IWeapon, TSword>.AsSingleton;
This code tells the container to always return the same instance of TSword whenever
it retrieves an implementation of TSword. However, you can do the same thing with
the [Singleton] attribute by attaching it to the TSword class:
Advanced Container Use 79
[Singleton]
TSword = class(TInterfacedObject, IWeapon)
procedure Wield;
end;
This will tell the Container that the implementation for IWeapon should be a singleton
– that is, when the Container provides an instance of IWeapon, it should always
return the exact same instance.
Conclusion
The Spring4D Dependency Injection container is quite powerful and capable. It can
do more than merely match up interfaces and implementations, as we’ve seen.
Dependency Injection
Anti-Patterns
So far, I’ve tried to show you good patterns and practices for doing Dependency
Injection and for using the Dependency Injection Container. There are definitely
correct ways that things should be done. And conversely, there are definitely ways
that things shouldn’t be done. This chapter covers some of these wrong ways –
commonly called anti-patterns – that you may be tempted to use but should not.
These anti-patterns are ones that have been tried but have shown to be wanting in
producing clean, good, well-written code.
Service Locator
Easily the most well-known, most controversial, and most abused anti-pattern is
the ServiceLocator anti-pattern. I call it well-known and controversial because for
a long time ServiceLocator was actually accepted as a useful pattern for doing
Dependency Injection. I call it abused because people still use it and believe it to
be a useful pattern. I’ll argue here that ServiceLocator is indeed an anti-pattern
and that you should not be using it in your applications.
ServiceLocator is tempting. It’s super easy to replace your calls to create with
a call to ServiceLocator.Resolve<IMyInterface>. It’s really natural to use the
ServiceLocator whenever you need an implementation of an interface.
But as we’ve seen, ServiceLocator isn’t needed. The Container can do 99% of the
work that you are tempted to use the ServiceLocator for. Because the Container can
automatically resolve any registered dependency that another registered class may
have, you can use the ServiceLocator only once at the root of your application. The
Container is fully capable of wiring up your entire object graph before the application
ever starts. You do need that one resolve call at the root of your application, but it
becomes a small price to pay for the huge benefit that the Container brings.
Dependency Injection Anti-Patterns 81
There are several further reasons why the ServiceLocator should be avoided.
Field Injection
unit uFieldInjection;
interface
uses
Spring.Container.Common
;
type
IBrake = interface
['{74DBE39C-F52F-42C4-B7CB-8009F7EDF1E1}']
procedure StopVehicle;
end;
IEngine = interface
['{0BC34CC8-DE81-4073-9CA4-A160CFB9A64A}']
procedure PropelVehicle;
end;
ICar = interface
['{B2C1C9FB-E388-4F0B-9197-2BCAB5A2A396}']
procedure Drive;
end;
implementation
uses
Spring.Container
;
procedure TBrakes.StopVehicle;
begin
WriteLn('Step on the brake pedal and make the car stop');
end;
procedure TEngine.PropelVehicle;
begin
WriteLn('Burn gas and make the car go');
end;
procedure TCar.Drive;
begin
FEngine.PropelVehicle;
FBrakes.StopVehicle;
end;
• It uses Field Injection for two fields, FBrakes and FEngine. When registering
TCar as implementing the ICar interface, it attaches a call to InjectField that
registers FBrakes as a field that will be injected upon creation. To register
the FEngine field, it uses the [Inject] attribute. Both techniques are exactly
identical and do the same thing.
Dependency Injection Anti-Patterns 84
• Because TCar is registered in the container and there are classes registered for
the types of the fields, everything gets “wired up” automatically and a call to
MakeCarGo behaves as expected.
Bottom Line: Use Constructor Injection and Property Injection, and don’t use Field
Injection.
Constructor Over-Injection
I hinted at this a bit above when discussing Field Injection. Constructor Over-
Injection is the tendency to continue passing references backwards towards the
composite root in each subsequent class’s constructor, causing those constructors to
bloat and take on too many parameters. Imagine ClassA which depends on ClassB,
which in turn depends on ClassC, and then ClassD, and so on. You could end up with
a constructor that looks something like this:
constructor TClassG.Create(aClassA: TClassA; aClassB: TClassB; aClassC: TClassC; aClassD: TClassD; aCl\
assE: TClassE; aClassF: TClassF);
This is clearly not desirable, though one can understand how it might happen if one is
assiduous in using Constructor Injection. If you find yourself doing this, it is time to
Dependency Injection Anti-Patterns 86
take a step back and realize that your class hierarchy has issues, the primary of which
is likely that your class is not following the Single Responsibility Principle. A class
that takes that many dependencies, even if it is merely passing those dependencies
through to other classes, is very likely trying to do too much. Look to break your
classes into smaller, more focused classes that do one thing and one thing only. This
should prevent Construction Over-Injection.
Constructor Over-Injection can also occur in a more subtle way. Consider the
following code:
unit uOverInjection;
interface
type
IBankingService = interface
['{E367001A-94D1-4694-A0B9-FB0B3FD822ED}']
procedure DoBankingStuff;
end;
IMailingService = interface
['{ED17BB98-CC8C-4DBF-9466-C20F6BBC4AE2}']
procedure MailPayrollInfo;
end;
TEmployee = class(TObject)
private
FLastName: string;
FFirstName: string;
FWantsMail: Boolean;
public
property WantsMail: Boolean read FWantsMail write FWantsMail;
property FirstName: string read FFirstName write FFirstName;
property LastName: string read FLastName write FLastName;
end;
TPayrollSystem = class
private
FBankingService: IBankingService;
FMailingService: IMailingService;
public
constructor Create(aBankingService: IBankingService; aMailingService: IMailingService);
Dependency Injection Anti-Patterns 87
implementation
procedure TBankingService.DoBankingStuff;
begin
WriteLn('Doing banking stuff');
end;
procedure TMailingService.MailPayrollInfo;
begin
WriteLn('Mail out payroll information');
end;
end.
This looks great. But notice that, while there are two dependencies passed in, one
of those dependencies – IMailingService – isn’t always used. IMailingService is,
however, always required. The TMailingServices class is being “greedy” and asking
for more than it always needs. A class’s constructor should only ask for dependencies
that it really needs, and not for dependencies that it only sort of needs. This type of
Constructor Over-Injection should be considered a Code Smell. Instead, you might
consider using some other method, such as a Factory, to get an instance of the mailing
service.
We should also recognize this as a (simple but illustrative) violation of the Single
Responsibility Principle. That is, the TPayrollSystem class is doing too much. It
is doing payroll and mailing things. Instead, it probably should just worry about
payroll and then have a dependency that does the worrying about “sending”. In other
words, there should probably be a notion of ISendPayrollInfo and then various
Dependency Injection Anti-Patterns 88
implementations based upon what the employee wants – snail mail, email, hand-
delivered, whatever. TPayrollSystem should not be concerned with how the sending
happens.
Thus, there are two kinds of the anti-pattern Constructor Over-Injection. First, there
is the passing of too many dependency parameters in the constructor (I start getting
nervous when the number hits three…). This should cause a reconsideration of the
class design in light of the Single Responsibility Principle. The second is the passing
of dependencies that aren’t used directly by the class, or dependencies that aren’t
always needed. This should be considered a Code Smell and refactored, as well.
• VCL controls have their own lifetime management system and putting them
in the Container causes confusion, both on the part of the developer and
the Container, as to who owns the components and when they should be
destroyed.
• Many VCL controls are visual, and as such, they need to be managed by their
visual containers (such as forms, panels, groupboxes and the like). Putting
these components into the Dependency Injectino Container mucks up these
ownership relationships. The Dependency Injection Container and visual
controls don’t mix well and shouldn’t be used together.
• It’s hard. You have to jump through a lot of hoops even to make it work. Keep
things simple and reserve the container for business classes and other objects
that you write yourself to enable your application to work.
Dependency Injection Anti-Patterns 89
Multiple Constructors
A class involved with Dependency Injection should have only one definitive con-
structor. That constructor should declare all the dependencies the class requires.
Remember, a constructor’s parameters should be the definitive list of dependencies
that a class cannot live without. If a class has more than one constructor, then it is
declaring more than one set of required dependencies, and that doesn’t make any
sense.
Multiple constructors will also make it difficult, if not impossible, for the Container to
resolve how to create a class properly. Even if all of the parameters are resolvable for
multiple constructors, the question of which constructor the Container should select
is left unclear. A DI Container could have specific rules for selecting one of multiple
constructors, but that decision might not be clear to the developer. And changes to
the class might result in a different code path, unbeknownst to the developer. Not
good.
Mixing the registering your classes and interfaces with your code by putting the
registrations in the initialization section of your unit is an anti-pattern. When
you do this, you couple your class to the Container, and as we’ve discussed, you
should avoid unnecessary coupling.
For example: Unit testing protocol states that you should test your classes in isolation.
If you do your registration in the initialization section, you can’t test your code
without involving the Container.
Dependency Injection Anti-Patterns 90
Instead, create a separate unit and put your registration code in the initialization
section of that unit, and then use that unit only in the DPR file by including it in the
project. This will allow you to register everything that needs to be registered while
keeping the Container decoupled from your code. The example code for this book
illustrates this technique.
Conclusion
Just as there are right ways to do things with Dependency Injection, there are also
wrong ways to do things. This chapter discussed some anti-patterns that are tempting
but not a good idea when doing Dependency Injection. Stay away from them and
you’ll avoid many problems.
A Simple, Useful, and Complete
Example
Introduction
Okay, we’ve had a lot of theory and a lot of basic examples, so now it is time to put
it all together into a real, working demo that actually does something useful.
The following example is a simple application that allows you to view files. It’s
expandable in that you can write more file type viewers and add them to the
application at design-time. By default, it provides a text file viewer and a viewer
for the most popular graphics types.
And, of course, it uses Dependency Injection as its basic design pattern. The main
use is via Constructor Injection, but I included some Property Injection as well.
Interfaces
Naturally, the application depends on interfaces and not implementations of those
interfaces (have I made that point enough yet?). So here are those interfaces, in their
own unit, of course:
A Simple, Useful, and Complete Example 92
unit uFileDisplayerInterfaces;
interface
uses
Vcl.ExtCtrls
;
type
IDisplayOnPanel = interface
['{C334B1AE-F562-4EA6-B98D-BB52F1CBE7A7}']
procedure DisplayOnPanel(const aPanel: TPanel);
end;
IDisplayFile = interface
['{3437F0E6-2974-4C1A-BA07-2598A2774855}']
procedure DisplayFile(const aFilename: string; const aPanel: TPanel);
end;
IFileExtensionGetter = interface
['{9E28B18F-3CDF-4F6E-B629-D3E02E0E0E6C}']
function GetExtension(const aFilename: string): string;
end;
IFilenameGetter = interface
['{48E1FFD8-73EA-43DF-B722-4A86206BDFCE}']
function GetFilename: string;
end;
IFileDisplayerRegistry = interface
['{7211F4E0-0E7E-4216-912D-069E21660DE1}']
procedure AddDisplayer(aExt: string; aDisplayer: IDisplayFile);
function GetDisplayer(aExt: string): IDisplayFile;
function GetExtensions: TArray<string>;
end;
implementation
end.
unit uFileDisplayer;
interface
uses
Vcl.ExtCtrls
, uFileDisplayerInterfaces
;
type
implementation
uses
System.Classes,
uFileDisplayerRegistry
;
Component.Free;
end;
end;
end.
As usual, let’s take a look at some things to note about the above code:
application. This is nice, because we can loosely couple to the user interface
and do most of the work by coding against abstractions. Note that the above
class does exactly that. Every line of code in the class is coding against an
interface. It doesn’t know or care about how those interfaces are implemented.
unit uFilenameGetter;
interface
uses uFileDisplayerInterfaces;
type
TFilenameGetter = class(TInterfacedObject, IFilenameGetter)
private
function GetFilename: string;
end;
implementation
uses
Vcl.Dialogs
, Spring.Container
;
end.
unit uFilenameExtensionGetter;
interface
uses uFileDisplayerInterfaces;
type
TFileExtensionGetter = class(TInterfacedObject, IFileExtensionGetter)
private
function GetExtension(const aFilename: string): string;
end;
implementation
uses
System.IOUtils
, Spring.Container
;
end.
Both are quite simple and self-explanatory. Because they are implementing inter-
faces, you can easily implement them differently if you like, and as long as they
meet the contract of the interface, they will perform just fine inside the application.
That’s why you code to an interface and not an implementation. If you want to get
your filename from a database, you can do that without changing anything but the
implementation of the particular interface.
Building Displayers
Okay, so the next interface we have to implement is IDisplayFile. This interface is
the one where the real work gets done. For our application, we implement two – one
that can display text files and one that can display graphic files.
Here is one that can display text – TTextFileDisplayer:
A Simple, Useful, and Complete Example 97
unit uTextFileDisplayer;
interface
uses
uFileDisplayerInterfaces
, Vcl.ExtCtrls
;
type
TTextFileDisplayer = class(TInterfacedObject, IDisplayFile)
procedure DisplayFile(const aFilename: string; const aPanel: TPanel);
end;
implementation
uses
Vcl.StdCtrls
, Vcl.Controls
;
end.
This class is simple (as all classes should be, right?). It implements the one method of
IDisplayFile by adding a TMemo to the panel that is passed in and opening the text
file into it. It couldn’t be simpler. Another lovely example of a class doing one thing
and doing it well.
Here’s the very similar implementation for displaying graphics:
A Simple, Useful, and Complete Example 98
unit uPictureDisplayer;
interface
uses
uFileDisplayerInterfaces
, Vcl.ExtCtrls
;
type
TPictureDisplayer = class(TInterfacedObject, IDisplayFile)
procedure DisplayFile(const aFilename: string; const aPanel: TPanel);
end;
implementation
uses
System.Classes
, Vcl.Graphics
, Vcl.Controls
, Vcl.Imaging.JPEG
, Vcl.Imaging.PngImage
, Vcl.Imaging.GIFImg
;
Image.Picture.Bitmap := TBitmap.Create;
Image.Picture.LoadFromFile(aFilename);
end;
end.
Not much explanation needed for this – the class simply leverages the capabilities of
TImage to enable the opening of any Bitmap, JPEG, GIF, or PNG file. Nifty.
Registering Extensions
First, we need a place to contain all the file viewers. Since we have a few file viewers
and file extensions that tell us which of those viewers to use for those extensions,
an IDictionary suits our purposes just fine. We can wrap it up in a registry, so that
we store and retrieve the pairings. Since we want to code against an interface, we
created the IFileDisplayerRegistry (seen above) that does the work we want it to
do. Here’s the implementation:
unit uFileDisplayerRegistry;
interface
uses
uFileDisplayerInterfaces
, Spring.Collections
;
type
TFileDisplayerRegistry = class(TInterfacedObject, IFileDisplayerRegistry)
private
FDictionary: IDictionary<string, IDisplayFile>;
FDefaultDisplayer: IDisplayFile;
public
constructor Create;
procedure AddDisplayer(aExt: string; aDisplayer: IDisplayFile);
function GetDisplayer(aExt: string): IDisplayFile;
function GetExtensions: TArray<string>;
// Property Injection: We have a default displayer, but you can provide your own if
// you want to.
property DefaultDisplayer: IDisplayFile read FDefaultDisplayer write FDefaultDisplayer;
end;
implementation
uses
Spring.Container
, uDefaultDisplayer
;
var
FDR: IFileDisplayerRegistry;
end;
Result := FDR;
end;
constructor TFileDisplayerRegistry.Create;
begin
FDictionary := TCollections.CreateDictionary<string, IDisplayFile>;
FDefaultDisplayer := TDefaultFileDisplayer.Create;
end;
end;
end.
TFileDisplayerRegistry class has three methods. Two are for managing the con-
nection between the file extensions and the displayers that can display that particular
type of file. You can both add and retrieve displayers. The third method is used to get
a list of supported extensions for display to the user so they can know what types of
files can be opened.
It also has a single property – DefaultDisplayer – that follows the Property Injection
pattern. It allows you to set a value for a default file displayer that will be used in
A Simple, Useful, and Complete Example 101
the event that there is no registered extension for the chosen file. The class provides
a default displayer (seen below) that is returned when no registered displayer can
be found. (Note that GetDisplayer follows the “Never return nil” rule by always
returning an implementation of IDisplayFile no matter what). In addition, if you
want to provide your own default file displayer, you can do so by setting the
DefaultDisplayer property – just like we talked about in the Property Injection
chapter. See, this stuff really does work.
Note, too, that the TFileDisplayerRegistry class follows the Registry Pattern and
thus is exposed as a Singleton. The Registry Pattern pretty much calls for the resulting
Registry class to be a Singleton, despite the fact that the Singleton Pattern has fallen
out of a favor (it’s really just a glorified global variable….). I did it this way for demo
purposes – a more robust version of the application would manage the registration
without resorting to this.
Here’s the default displayer, which merely outputs some simple information about
the selected file:
unit uDefaultDisplayer;
interface
uses
uFileDisplayerInterfaces
, Vcl.ExtCtrls
;
type
TDefaultFileDisplayer = class(TInterfacedObject, IDisplayFile)
strict private
function GetFileSize(aFilename: string): Int64;
procedure DisplayFile(const aFilename: string; const aPanel: TPanel);
end;
implementation
uses
Vcl.StdCtrls
, Vcl.Controls
, System.IOUtils
, System.SysUtils
;
Memo := TMemo.Create(aPanel);
Memo.Parent := aPanel;
Memo.Align := alClient;
Memo.ReadOnly := True;
end.
To get all these displayers and classes registered, the project contains a unit named
uRegistration.pas that has a procedure that does all the registration of the exten-
sions and displayers. Note that you can register multiple file types against the same
displayer:
procedure RegisterDisplayers;
var
TextFileDisplayer: IDisplayFile;
PictureFileDisplayer: IDisplayFile;
begin
TextFileDisplayer := TTextFileDisplayer.Create;
FileDisplayerRegistry.AddDisplayer('.txt', TextFileDisplayer);
FileDisplayerRegistry.AddDisplayer('.pas', TextFileDisplayer);
FileDisplayerRegistry.AddDisplayer('.dpr', TextFileDisplayer);
FileDisplayerRegistry.AddDisplayer('.dproj', TextFileDisplayer);
FileDisplayerRegistry.AddDisplayer('.xml', TextFileDisplayer);
PictureFileDisplayer := TPictureDisplayer.Create;
FileDisplayerRegistry.AddDisplayer('.jpg', PictureFileDisplayer);
FileDisplayerRegistry.AddDisplayer('.bmp', PictureFileDisplayer);
FileDisplayerRegistry.AddDisplayer('.png', PictureFileDisplayer);
FileDisplayerRegistry.AddDisplayer('.gif', PictureFileDisplayer);
end;
I’ve registered a number of different text files that can be viewed using TTextFileDis-
player.
Once this is done, you don’t need to create a thing – the container will do all the work
for you. Did you notice that the application doesn’t create any of its business objects
(save the singleton TFileDisplayerRegistry)? That’s because the Container does all
the creating and wiring together of all the interface references. (Remember when we
talked about injectables? Well, most of the things in the application are injectables.
About the only creatable is the IDictionary in the file extension registry.)
The code to make it all work is pathetically simple. We’ve done all the heavy lifting
already; now it is just a matter of taking advantage of our nice architecture to show
a file in the application.
First thing, we add a private variable to the form of type IDisplayOnPanel:
A Simple, Useful, and Complete Example 105
type
TFileDisplayerForm = class(TForm)
Button1: TButton;
Panel1: TPanel;
Button2: TButton;
ListBox1: TListBox;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
FContainer: TContainer;
FileDisplayer: IDisplayOnPanel;
end;
Here we make our single call to Resolve (remember, we only get the one call) and
fill up the list box with eligible extensions. Not much going on there.
The meat of things – and it’s thinly sliced meat indeed – is behind the “Get and
Display File” button:
That’s it for the UI. Seriously. There’s quite a bit of functionality in our application,
but not a lot of it is tied to the user interface. And that’s the way it should be.
A Simple, Useful, and Complete Example 106
And now here’s the application displaying an image from my favorite album of all
time:
• Most obviously, you could write additional displayers for different kinds of
A Simple, Useful, and Complete Example 107
files. Every file can be displayed in some way, and as long as you can place the
results onto a TPanel, there is no limit to what you could add.
• Adding new file types currently requires a recompile. You could easily devise
a scheme that allowed for plugging in file viewers dynamically.
• There is little to no error handling in the application, though it’s likely that
little needs to be added. The application does ensure that nothing is ever nil,
so there’s no need to check for that. However, the application doesn’t, for
instance, check to see that the file actually exists before it tries to display it.
• You could improve the file-getting implementation by allowing the user to
select the extension she is interested in and filtering the search dialog box for
those file types.
• You might consider replacing the file extension registry with an implementa-
tion that doesn’t use a Singleton.
Conclusions
Here are some thoughts as we wrap up:
• All of the interfaces are in a single unit, so you can depend on that unit and not
the units that implement the interfaces. Sure, you may end up with a bunch of
units, but since it is so easy to look at each one and figure out what it does, it
becomes a small price to pay.
• Each of those single units contains one class that does one thing. Following
the Single Responsibility Principle keeps things clean and simple. The end
result is that each class has a clear, understandable purpose. And even more
importantly, each class is testable.
• All of the registration, both for the displayer registry and the Dependency
Injection Container registration, is in its own unit. This is where things are
coupled – the uRegistration unit is where the implementation units are used.
That’s the only “touch point” for interfaces and the implementations. That’s
about as loosely coupled as you can get.
• In the end, we have a clean, extensible, loosely coupled application. Maintain-
ing, updating, and enhancing this application would be very simple. Adding
the things described above shouldn’t be hard at all.
A Simple, Useful, and Complete Example 108
I hope by now you can see the benefits of building your application with interfaces
and Dependency Injection.
Final Thoughts
There you have it. I hope you have found this book useful. I hope that it has
changed the way you think about building your applications. I hope you see the
wisdom of coding against abstractions and injecting your dependencies, and I hope
I accomplished what I set out to do in the preface.
Writing and maintaining code is hard enough without tangling everything together
in a messy ball of yarn. If you could write your code in a way that makes things
stand-alone, loosely couples those things together, and results in a powerful but easy
to test and maintain application, you’d do it, wouldn’t you? I hope that I’ve convinced
you that Dependency Injection can do just that.
As I said in the preface – Dependency Injection is a very simple idea: Just hand your
classes their dependencies, normally via the constructor. If that one basic idea is all
that you’ve gleaned from this book, I’m happy. That simple idea can transform your
code from the mundane to the sublime.
Dependency Injection has made a world of difference – all for the better – in how I
write code. I implore you – let it do the same for you.