Mark IV Coffee Maker on Dissident
Jim Weirich uses an excellent example for introducing Dependency
Injection in his OSCON 2005
talk, the Mark IV Coffee
Maker.
I’m not going to introduce it here except for the API, if you did
not read his slides yet (or even attended?), you are better off if you do
that now.
Basically, you have three classes that need to know of each other:
PotSensor, Heater and Warmer. You want to instantiate a Warmer.
class PotSensor
def coffee_present?; ...; end
end
class Heater
def on; ...; end
def off; ...; end
end
class Warmer
def trigger
if pot_sensor.coffee_present?
heater.on
else
heater.off
end
end
end
Jim uses his own container, Dependency
Injection/Minimal
in the talk, and makes use of Constructor Injection. It looks like
this when using DIM (example sightly simplified for blogging
purposes):
dim = DIM::Container.new
dim.register(:pot_sensor_io_port) { 0x08F0 }
dim.register(:warmer_heater_io_port) { 0x08F1 }
dim.register(:pot_sensor) { |c| PotSensor.new(c.pot_sensor_io_port) }
dim.register(:warmer_heater) { |c| Heater.new(c.warmer_heater_io_port) }
dim.register(:warmer) { |c|
Warmer.new(c.pot_sensor, c.warmer_heater)
}
dim.warmer
If we wanted to use Constructor Injection too in Dissident, we could
go like this:
class MarkIVConfiguration < Dissident::Container
def pot_sensor_io_port; 0x08F0; end
def warmer_heater_io_port; 0x08F1; end
def pot_sensor
PotSensor.new pot_sensor_io_port
end
def warmer_heater
Heater.new warmer_heater_io_port
end
def warmer
Warmer.new(container.pot_sensor, container.warmer_heater)
end
end
(Assuming container is self, this is even valid and working Ruby
code without using Dissident.) At least you would write it that way
some iterations ago. Recently, Dissident learnt of some useful
constructs, however, that simplify above greatly. We now can write:
class MarkIVConfiguration < Dissident::Container
constant :pot_sensor_io_port, 0x08F0
constant :warmer_heater_io_port, 0x08F1
provide :pot_sensor, PotSensor, :pot_sensor_io_port
provide :warmer_heater, Heater, :warmer_heater_io_port
provide :warmer, Warmer, :pot_sensor, :warmer_heater
end
I don’t think Constructor Injection can be more straight-forward in a
language that doesn’t allow for argument name (or “type”) reflection.
We instantiate the warmer by setting up the container and calling:
Dissident.with MarkIVConfiguration do |c|
c.warmer
end
Now, let’s do the same with Setter Injection in Dissident.
class PotSensor
inject :pot_sensor_io_port
end
class Warmer
inject :pot_sensor
inject :warmer_heater
end
class MarkIVConfiguration < Dissident::Container
constant :pot_sensor_io_port, 0x08F0
constant :warmer_heater_io_port, 0x08F1
provide :pot_sensor, PotSensor
provide :warmer_heater, Heater, :warmer_heater_io_port
end
Note how Setter and Constructor Injection can be mixed. (PotSensor uses
Setter Injection, Heater gets its port via Constructor Injection.)
Now, watch how we instantiate that:
Dissident.with MarkIVConfiguration do
Warmer.new
end
As you can see, this looks like “ordinary” Ruby, as if DI was not used
at all. It may not make a big difference if you see the whole code at
once, but in my experience distributing the injections to the
classes that need it greatly simplifies coding because things are
where they are related to.
Also, see how easy we can mock, say, for testing:
class MarkIVTestConfiguration < MarkIVConfiguration
provide :pot_sensor, MockPotSensor
provide :warmer_heater, MockWarmerHeater
end
Dissident.with MarkIVTestConfiguration do
Warmer.new
end
Dissident is now in a state where I think I can make the general
public have a look at the code, so you are welcome to get it at the
darcs repository (or just browse the code there).
I hope to make a proper release really soon now.
NP: Alanis Morissette—You Oughta Know
Off to Lloret de Mar
Alright, I’m off to Llorret de Mar for
holiday for the next ten days, so I’ll be back August 28. As usual,
no mail, IRC or other net stuff. In fact, this time no notebook at
all. I have a hard time remembering the last time I did that for more
than three days; must have been early 2004, when I refactored Nukumi1
in my mind. (Maybe a month after, I threw it away. :-P)
Now I just wonder how you survive a 16 hour bus trip. Oh well.
chris blogs and Anarchaia resume publishing on August 29, have a lot
of fun.
NP: Funny van Dannen—Blauer Vogel
Growing up
(Trying to post bilingual.)
Today, Elvis Presley died
and Madonna was born, Jakob Bernoulli died and Charles Bukowski was
born; Pete Best was replaced with Ringo Starr, the Ramones have their
first show and Debian was founded.
But all that’s not relevant right now. ;-) Tonight, I got eighteen
years old, therefore I’m now an adult in Germany. It is a great
relief that all the things we did for years we now can do legally.
Just look at this great ASCII art cake Alexander Kellett sent me,
isn’t it fantastic:
(Mal zweisprachig ausprobieren.)
Heute, starb Elvis
Presley und Madonna wurde geboren, Jakob Bernoulli starb und Charles
Bukowski wurde geboren; Pete Best wurde durch Ringo Starr ersetzt, die
Ramones hatten ihre erste Show und Debian wurde gegründet.
Aber das spielt im Moment alles keine Rolle, ;-) Heute Nacht wurde
ich achtzehn Jahre alt, daher bin ich nun ein Erwachsener in
Deutschland. Es ist eine große Erleichterung, dass nun all die Dinge
die wir seit Jahren taten nun legal tun dürfen.
Schaut euch mal diese tolle ASCII-art Torte an, die mir Alexander
Kellett geschickt hat, ist sie nicht toll?:
_@_@_@_@_
( )
(#########)
_(_________)_
I had a hard time picking appropriate lyrics for today, and I decided
not to post “Too Late To Die Young” by Dan Bern, despite the most
appropriate beginning with the Presley reference; I choose “My Back
Pages” of “Another Side of Bob Dylan” as the time for “Every Grain of
Sand” has not yet come. ;-)
Es fiel mir schwer, einen passenden Songtext für heute auszusuchen,
und ich beschloss, nicht “Too Late To Die Young” von Dan Bern zu nehmen,
trotz dem passenden Presley-Bezug. Also wählte ich “My Back Pages” von
“Another Side of Bob Dylan”; die Zeit ist noch nicht reif für
“Every Grain of Sand”. ;-)
Crimson flames tied through my ears
Rollin’ high and mighty traps
Pounced with fire on flaming roads
Using ideas as my maps
“We’ll meet on edges, soon,” said I
Proud ‘neath heated brow.
Ah, but I was so much older then,
I’m younger than that now.
[…]
In a soldier’s stance, I aimed my hand
At the mongrel dogs who teach
Fearing not that I’d become my enemy
In the instant that I preach
My pathway led by confusion boats
Mutiny from stern to bow.
Ah, but I was so much older then,
I’m younger than that now.
Yes, my guard stood hard when abstract threats
Too noble to neglect
Deceived me into thinking
I had something to protect
Good and bad, I define these terms
Quite clear, no doubt, somehow.
Ah, but I was so much older then,
I’m younger than that now.
Finally some pictures taken all over the day:
Abschließend noch paar Bilder, die den Tag über aufgenommen wurden:
NP: Funny van Dannen—Sexualität
Design and Evolution of a Dependency Injection Framework
(Ok, I admit I’ve always wanted to write a post with a title like
“Design and Evolution of …” or “Structure and Implementation of …”. :-))
In the last two weeks, I’ve been writing (yet) another Dependency
Injection framework for Ruby dubbed Dissident. Based on my
experience with Needle (which I used for writing Nukumi2) and
quite a deal of inspiration by Java frameworks, especially
PicoContainer, I think I made one of the most “rubyish” frameworks
available.
What is the deal with Dependency Injection? DI tries to solve one of
the oldest problems of object-oriented programming: Decoupling
objects. Probably every half-serious OO programmer got to know that
often you need to pass a fair amount of objects to the classes you
instantiate, just because they need them; the class that
instantiates doesn’t. All this passing-around is, in the end,
unneeded code that wastes time and attracts bugs. Also, what happens
if you suddenly need to replace the actual implementations?
(Admittedly, it’s not that bad with dynamic languages and duck-typing,
but in a static language, I hope you have your interfaces handy.
Nevertheless, concentrating instantiation makes your application easier to
maintain. Tomorrow your PHB tells you he wants to use that “new
logging library” everywhere. It’s really nice if you can do that
by changing a single line. [1])
Currently, there are two popular (and generally considered being good)
approaches to this problem: Setter Injection and Constructor Injection.
Setter Injection creates new instances and injects the dependencies
of them by using setters. Without a DI framework, you would use the class
like this in Ruby:
class Application
attr_writer :logger
def do_stuff
logger.log "doing stuff"
end
end
a = Application.new
a.logger = Logger.new
a.do_stuff
As you can see, this is a straight-forward way to do DI, and also
the default one used in my framework. (Well, almost; see below.)
Constructor Injection was popularized by PicoContainer, and
is considered more “clean” by many people.
class Application
def initialize(logger)
@logger = logger
end
def do_stuff
@logger.log "doing stuff"
end
end
a = Application.new(Logger.new)
a.do_stuff
Constructor Injection is available for Dissident too, but not the
default mechanism because unlike Java and (at least to an extent)
Python, Ruby does not provide mechanisms to access the parameter
names and types of methods.
Constructor Injection is actually more work to code for in a dynamic
language like Ruby, at least in comparison to Setter Injection
(count the occurrences of logger in above classes).
As mentioned, Dissident does not implement mere Setter Injection,
but an extension of it that is only possible in dynamic languages; I
call it Method Injection: Dissident extends the classes that use DI
to provide methods that return the requested services. Therefore, the
Dissident code to make use of the first example would look like this:
class Application
inject :logger
def do_stuff
logger.log "doing stuff"
end
end
class MyContainer < Dissident::Container
provide :logger, Logger
end
Dissident.with MyContainer do
a = Application.new
a.do_stuff
end
You can stop goggling now. :-) You probably expected something like
that:
container = MyContainer.new
# mumble mumble
a = container.application
Not so in Dissident! Dissident tries to completely stay out of your
code. Rubyists duck-type class instantiation on #new, and there is
no reason to change that when using a DI framework.
So, what happens now exactly? The Dissident.with block makes an
instance of MyContainer (a plain old Ruby class) the current
container. Now, all “dissidentized” classes (Application here) can
access it. The inject line in the class definition of
Application defines a “getter” for the logger, which is provided by
the container as an instance of Logger.
In fact, instead of that provide line, you could as well write
something along this:
class MyContainer < Dissident::Container
def logger
Logger.new
end
end
…which does in above case exactly the same, but provide allows for
some more subtleties.
The code as seen is not independent of Dissident, but that can be fixed in
three easy ways:
Redefine inject as attr_writer (e.g. with class Class; alias_method
:inject, :attr_writer; end), and you can use standard Setter Injection,
rescue calls to inject (possibly falling back to attr_writer), or
use Constructor Injection.
To make use of Constructor Injection in Dissident, just tell provide
the services you want to pass; in above case you now must let
Dissident actually construct the application itself, therefore we need
to register it too:
class MyContainer < Dissident::Container
provide :logger, Logger
provide :application, Application, :logger
end
Dissident.with MyContainer do |container|
a = container.application
a.do_stuff
end
As you can see, Constructor Injection is the more “pure” approach,
but a bit more work and not as transparent as Method Injection.
You can mix both freely when using Dissident, though.
Another nice thing Dissident provides are parametrized services,
simply define a method in your container, and it will get a multiton
with a life-time of the container. This only works with Method
Injection.
class MyContainer < Dissident::Container
def logger(level=:debug)
SuperFancyLogger.new(:level => level)
end
end
class Application
inject :logger
def do_stuff
logger.log "This is just for debugging."
logger(:alert).log "Core meltdown."
end
end
What happens when you need more than one container? You may, for
example, want to use a library that makes use of Dissident while
independently using it in your own application too. Dissident solves
this by having the classes declare their association with library:
class AGreatLibrary
library AGreatLibrary
end
class AGreatLibraryHelper
library AGreatLibrary
end
Dissident.with MyContainer, AGreatLibrary => AGreatContainer do
Application.new # uses MyContainer
AGreatLibrary.new # uses AGreatContainer
AGreatLibraryHelper.new # uses AGreatContainer, too
end
If it is likely that AGreatLibrary always uses AGreatContainer,
you can declare this too, then the user doesn’t need to care about it
(but still can override manually, of course):
class AGreatLibrary
library AGreatLibrary
default_container AGreatContainer
end
Now, I showed you the most of the things Dissident can do. And these
are probably 90% of the things you’ll ever need when using Dissident.
Additional features are included as separate files, I wrote a basic
lifecycle management and support for multi-methods, that allow even
easier parametrization of services; more about that will follow in a
later post.
So much about the design of Dissident, now a bit more about the
evolution. I don’t think any library or application I ever wrote
changed so much without a rewrite. When I started to write Dissident,
it was a tiny library that would only do Setter Injection with
instance variables. Then, I noticed this approach was too inflexible
as it didn’t support parametrized services. First, I used
define_method on the singletons the container instantiated, but
that’s inefficient, and far too invasive. The next step was to
extend them with Modules, first dynamically generated, then named
for marshaling purposes. I have to admit it took me a fair time to
recognize that I could define the getters directly on the classes.
After some more playing and reading about PicoContainer, I decided to
add Constructor Injection too; that was fortunately rather easy.
But why write a new DI framework at all? There are some prejudices in
the Ruby community with respect to that. People say “they make things
complicated” and “there are more frameworks than users”. Of course,
that may be true—but it shouldn’t be for all of them.
Therefore, I decided to make
one that’s not complicated,
because you barely notice using it (It’s
true that use of DI frameworks often significantly changes the code),
one that’s easy to pickup,
because you can learn it in an afternoon and
only need to write a few additional
lines of Ruby—no XML or YAML needed,
one that actually helps coding,
because else it’s a hobble and therefore no fun,
one that eases testing,
because you can mock the services easily
(don’t use a container at all, or simply inject mocks),
one that feels like Ruby,
because you should never tangle in bad ports of Java libraries;
in the end, I decided to make
one that I personally like and want to use,
because there is no point in making libraries you don’t use on your own.
Still, Dissident is no silver bullet—there is no panacea. If your
design is broken, the best libraries can’t change that. But I think
that when you use Dissident, and use it as it was meant to be, it can
help you show the rough edges of your design earlier than when you sit
over half a dozen napkins, desperately trying to untangle your class
relationships. (You’ll quickly notice when your container definitions
get ugly.)
Another thing among the reasons I wrote my own Dependency Injection
framework was the size of the existing ones. Needle with its 1200 LOC
doesn’t count as “lightweight” in my opinion anymore—it already is
in the mid-size non-invasive team. (It is very good, though. Use it
if you think Dissident is not enough or too
extreme/weird/fancy/magic/inflexible for you.) Dissident on the other
side is one 200 LOC file for the core right now, and maybe 100 LOC for
the additional features. The core is unlikely to grow much in future
(one thing that probably needs a bunch of code is makeing everything
thread-safe), it does basically everything that is needed. Therefore,
it does no harm to just include Dissident in your package, that’s one
dependency less and you have everything you need.
[1] And that’s the crux with DI, you only know you would have needed it
when it’s too late. It’s good if you have a very easy framework then;
especially if it’s one that you actually want to use.
NP: Dan Bern—Crossroads
Off to Lake Constance
Alright, I’ll go to Lake
Constance (also
at Living Lakes) tomorrow and
stay there for the rest of the week; that is, till Sunday. As usual,
I think there are not too many open hotspots in that area to make any
guarantees to blog, read mail or waste time on IRC.
However, I’ll take my iBook and hope to get some hacking done, either
for Package—which I have been neglecting far to long—or Dissident,
a new dependency injection mechanism I started to write this week.
We’ll see.
chris blogs and Anarchaia are not going to be updated until August 15,
have fun even though.
NP: Bob Dylan—Dark Groove (Rough-Mix)
100 Days Uptime
I just found out I haven’t rebooted my iBook for 100 days now. :-)
$ uptime
16:48 up 100 days, 3:13, 7 users, load averages: 2.60 2.91 2.96
(But I wonder how often I had to restart Firefox. :-P)
“Sleep” is a wonderful thing for notebooks… I don’t want to boot my
real box anymore. Instant hacking just rocks. You gotta love
the ubiquity. BTW, did you notice OS X wraps the Network statistics
after transferring more than 4 gigabytes? It doesn’t for the Disk, though.
At least I see three-digit gigabyte numbers there.
Anyway, I’m really confident with the system as it runs now… I
wonder if I should reboot for the updates?
NP: Bob Dylan—You’re A Big Girl Now
An interesting memorization technique
Did it ever happen to you that you needed to remember a few (sometimes
unrelated) things, and far too quickly forgot them? Say you wanted to
go shopping, or you recalled some things to google for, and there was
just no piece of paper available.
If not, you can as well skip this post. (And, even better, tell me
what you did to make it never happen to you?)
Or maybe you can even remember the things, but you have trouble
recalling all of them. Try that on your own: Do you remember all
the Beatles’ names? I always have trouble finding the last one.
I therefore decided to come up with an memorization technique on my
own. I did not look how other people do it (and you know, there are
people that are very good at remembering huge amounts of data in
short time), just to avoid copying other methods (it is said that
Feynman did his research that way too, something along “tell me about
their problems, but not their solutions”).
I think I have a fairly good mind for visuals and words, therefore my
technique uses those heavily. Also, I like weird things. Now, let me
explain how it works:
Reduce the thing you need to remember to a single word or a very
short sentence. I’ll call this the subject.
Think of two things that are very closely related to the subject.
Be sure you can visualize them. (This can be problematic
with very abstract ideas. It helps to do wordplays then or just
use very weird pictures.)
Choose any animal that starts with the same letter in your native
language as your subject.
Think of a scene where the animal and the two related things do
something.
Imagine wrapping that scene in a bubble and let it fly to the sky.
Ok, if that now sounds weird or childish to you, that’s fine. To me
too. But it really works. You need to use the creative side of your
brain too to get effective.
Now, it should be pretty easy to remember the things. Most often you
will simply recall the scene and get to the subject that way.
Sometimes you forgot the scene, then you’ll most likely remember the
animal and can zen it from the letters (hopefully).
If you don’t need to remember the subject anymore, be sure to
“garbage-collect” your mind. I imagine the bubble popping and the
scenery falls into the ocean. You don’t need to remember things on
the bottom, just the bubbles flying on top.
NP: Bob Dylan—Coming From The Heart [Rehearsal 2]