views:

610

answers:

6

I'm writing a mid-sized analysis program (5-10kloc) in MATLAB (not my decision), and I'm trying to use dependency injection to make my code more testable. I think I understand the basic object/constructor injection model, but I'm confused as to how this scales up the dependency graph.

For instance, if I have object A, which has-a object B, which has-a object C, and object C has a dependency that needs to be injected, do I need to pass it through the entire chain? Eventually, since this is a data analysis program, everything basically comes back to one AnalyzeData object/method, does this mean that that object has to have all the dependencies of the entire program injected?

Perhaps the answer is simply to use a ServiceFactory/ServiceProvider in this case, but I'd like to know if it's possible to scale many dependencies up to a large object graph without a framework.

Also, corrections on my thinking/wording/fundamentals are encouraged - I've basically been learning most of this through Google/HN/SO.

+1  A: 

Roll your own IoC container where you can just ask for your dependencies without needing to inject them, or use the static gateway pattern to get a singleton to create classes dynamically using a factory.

I found this video a really useful intro to these topics - http://www.dnrtv.com/default.aspx?showNum=126

Duncan
+1  A: 

If you do dependency injection by hand you're better off not passing dependencies through. You just create C with its dependency, create B with C, create A with B. There's no need for A to know anything about C or its dependency. It just knows about the interface of B and usually dependencies aren't part of the interface for an object.

Mendelt
+5  A: 

Matlab-agnostic answer:

If (A needs B) and (B needs C) then you first create C, then create B and pass C to B. Then you create A and pass (B has C) to A.

Now, if C needs a dependency to be injected, call it D, you can still do this after this sequence of events by using setter injection. This would be the case if the dependency is optional for C. Otherwise it is likely a flaw in your program initialization that C wasn't injected with D before it was passed to B.

If you want to use constructor injection for injecting D into C after you already have (A has (B has C)), then you'll have to use setter injection for passing a new (C has D) to B, but this sequence of events is usually not a valid scenario for DI.

eljenso
A: 

Don't know a thing about Matlab, but I assume (from your words) that there are objects. If so, go for a Service Locator instead of Dependency Injection. Service Locators are very simple to implement, so no frameworks required.

Anton Gogolev
+2  A: 

Disclaimer: OO language agnostic answer

No, you don’t need to pass dependencies through whole object graph. Not having to instantiate concrete types or to pass them as parameters is the point of DI. Generally, you will have some other entity, called Assembler for example, that will inject these dependencies. Assembler can be ad-hock, hand-written or can be some DI framework.

For example:

  • Class ClsA has a property of type interface IB.
  • Class ClsB implements IB and has a property of type interface IC.
  • Class ClsC implements IC.

Your Assembler will bootstrap the app and:

  1. create instance named oC of class ClsC
  2. create instance named oB of ClsB and inject it with oC
  3. create instance named oA and inject it with oB

All your domain objects know only interfaces. Assembler knows all the object, but its purpose is just to create object graph and put everything in motion. Hand-written assembler is perfectly appropriate; some folks prefer writing wiring code than using config files. Don’t think it is worth the trouble writing assembler (DI framework) if you don’t plan to use it more than once. The point is that your classes are written in DI fashion.

Take a look at this article: http://books.google.com/books?id=vRxAnRb3mb4C&pg=PP1&dq=Danijel+Arsenovski#PPA428,M1

Dan
+3  A: 

If I understand your question correctly, the answer may depend on how you are creating the classes from which you instantiate the objects. Within the newest versions of MATLAB, classes can be defined in two ways: a "value" class or a "handle" class (MATLAB documentation here). Quoting from the documentation:

  • Value class: "Objects of value classes are permanently associated with the variables to which they are assigned. When a value object is copied, the object's data is also copied and the new object is independent of changes to the original object. Instances behave like standard MATLAB numeric and struct classes."

  • Handle class: "Objects of handle classes use a handle to reference objects of the class. A handle is a variable that identifies a particular instance of a class. When a handle object is copied, the handle is copied, but not the data stored in the object's properties. The copy refers to the same data as the original—if you change a property value on the original object, the copied object reflects the same change."

The sample code below gives some examples of how to interact with "nested" objects like you described above, both for value-class nested objects and handle-class nested objects:

% For value classes:

objC = C(...);  % Make an object of class C, where "..." stands
                %   for any input arguments
objB = B(...,objC);  % Make an object of class B, passing it objC
                     %   and placing objC in field 'objC'
objA = A(...,objB);  % Make an object of class A, passing it objB
                     %   and placing objB in field 'objB'

% If the '.' operator (field access) is defined for the objects:

objA.objB.objC.D = 1;  % Set field 'D' in objC to 1
objA.objB.objC = foo(objA.objB.objC,...);  % Apply a method that
                                           %   modifies objC and
                                           %   returns the new
                                           %   object


% For handle classes:

hC = C(...);  % Get a handle (reference) for a new object of class C
hB = B(...,hC);  % Get a handle for a new object of class B,
                 %   passing it handle hC and placing it in field 'hC'
hA = A(...,hB);  % Get a handle for a new object of class A,
                 %   passing it handle hB and placing it in field 'hB'

% If the '.' operator (field access) is defined for the objects:

hC.D = 1;  % Set field 'D' to 1 for object referenced by hC; Note
           %   that hC and hA.hB.hC both point to same object, and
           %   can thus be used interchangably
foo(hC);  % Apply a method that modifies the object referenced by hC

% If instead using get/set methods for the handle object:

set(hC,'D',1);
set(get(get(hA,'hB'),'hC'),'D',1);  % If variable hC wasn't made, get
                                    %   reference from nested objects
foo(hC);
foo(get(get(hA,'hB'),'hC'));

As you can see, using a handle class can help you avoid having to chain function calls and field references by storing a copy of the handle (essentially a pointer) in another variable. Handle classes also take away the need to overwrite old copies of objects with new ones returned by methods that operate on those objects.

Hope this helps with what you were asking.

gnovice