So, I love the concept of LightWire - a lightweight DI engine to provide the basic functionality of ColdSpring for those who prefer programmatic config files or like to put more responsibilities on their business objects and less on the service layer.
I love the fact that I solved the basic problem in under 200 lines of code, but the first cut required you to add two methods to a base class and to tweak your init() methods for all of your objects. It worked as a proof of concept but honestly it was a little lame.
Base classes have their place, but not as a requirement for a DI framework as it makes your application way too knowledgeable about and dependent on the framework, and having to tweak your init() methods completely breaks the benefit of a DI engine that the objects it instantiates don’t need to know anything about it.
So, I’ve fixed these dubious design decisions and I’m liking where it is going.
Mixing in the Base
I need every object I create to have a method that allows objects to be injected into its variables scope (as I don’t want to have to stick them into the THIS scope which would not be very shy). Instead of having an AddObject method in a base class, I have added an AddObjectMixin class in LightWire.cfc. When I create an object I just set:
Local.ReturnObject.AddObject = variables.AddObjectMixin;
to pass the method in. Simple, effective, and works like a charm. It is a very simple method.
<cfargument name="ObjectName" type="string" required="yes" hint="I am the name of the object to add.">
<cfargument name="Object" type="any" required="yes" hint="I am the object to add.">
<cfset variables[ObjectName] = Object>
Dumping Dynamic Init()s
In my first implementation of LightWire I just used a base init class that would dynamically set all of the properties provided by LightWire. It is actually an interesting approach as it means you don’t need to put cfarguments in your init() methods. Want to add an extra parameter to your object? Just add it to your LightWire config file and you don’t need to repeat yourself by adding a cfargument to the init file.
However, the nominal benefits are completely outweighed by the fact that without a list of cfarguments in a methods init file you have no idea of the “contract” required to initialize the class making it much more likely you’ll pass only a subset of the required properties and get quirky runtime errors rather than a nice clean “argument missing” kind of error. It would also require the changing of all init() methods for existing class files just to make use of LightWire which is not a very practical design choice as it would also be making every class dependent on a DI engine with a dynamic init() method which doesn’t make a lot of sense.
So, I’ve simply removed that feature which means I need to go back in and add init methods and cfarguments into each of my class files, but I know it’s the right thing to do. Tested and working.
For anyone who is interested in the (really quite bad) idea of a “generic” base init method that will just take an argument collection and drop all of the elements into the variables scope so you don’t need to write all of your cfarguments in your init files, here is the (trivial) code to do it. It relies on a single struct being passed in, although with a little thought it could probably be rewritten to work directly with the arguments scope as a struct. Let me clarify this is code I have just REMOVED from LightWire. Just wanted a record of it somewhere in case I ever needed to do something funky with it.
<cfargument name="ArgumentStruct" type="struct" required="no">
Well, I’m going to need AOP, so I’m going to create a simple AOP implementation some time soon. I really don’t like XML config files (personal preference – I get why they are popular). That said, I WILL add an XML converter that will allow LightWire to speak a subset of ColdSpring so you’ll be able to port a project more easily between the two engines. In practice it may be a while before I add this as I have no personal use for it. If anyone wants a lightweight DI engine WITH XML config files, feel free to add the code and I’ll drop it into the project. If nobody else does this, I WILL add the feature as I need to learn how to work with XML files in CF – it seems pretty straightforward.