0

I'm new to Spring4D and trying to understand how to combine DIContainers with lazy loaded objects. I think i get the concept of pushing all the creation of the objects back in the callstack to the root of the application and register the types and invoke something or pass the solved objects using constructor/property/method injection. This way the project will only get dependency to interfaces in the seperate units and only get dependency to Spring in the root unit.

IMyClass = interface ['{593ABC29-B882-4B70-903F-52F381DD53BF}']
  procedure DoSomething;
end;

TMyClass = class(TInterfacedObject, IMyClass)
public
  procedure DoSomething;
end;

// In root of application 
Container.RegisterType<TMyClass>;
Container.Resolve<IMyClass>.DoSomething;

I see Spring4D has something called Lazy<T> for lazy loading, so in following example an instance of TMyClass is not created before referencing Value e.g.

  var LazyMyClassObjectFactory := TLazy<TMyClass>.Create;
  LazyMyClassObjectFactory.Value.DoSomething;

My problem is 1. how do i combine a DIContainer with lazy loading (not quite sure how to resolve the interfaced lazy objects) and 2. can it be done without requiring Lazy<T> since i dont like the idea of becoming dependant on "spring" in all my units.

2 Answers2

1

Got it working some what, though I don't use Lazy\<T\>. Instead I register a factory and pass down the line. Not sure if this is the right way to go about it, but I only get dependencies to my unit with the interfaces this way.

IMyClass = interface ['{735BA720-F1D9-4138-85E1-44EA9DCAA773}']
  procedure DoSomething;
end;

IMyClassFactory = interface ['{7B1125D7-2639-4D4E-9909-28A08543F5FF}']
  function Create: IMyClass
end;

TMyClass = class(TInterfacedObject, IMyClass)
public
  procedure DoSomething;
end;

// From top-level root
GlobalContainer.RegisterType<IMyClass>;
GlobalContainer.RegisterFactory<IMyClassFactory>;
GlobalContainer.Build;

// Instanciate MyClassFactoryObject and pass it down to other units having params of type IMyClassFactory
var MyClassFactoryObject := GlobalContainer.Resolve<IMyClassFactory>;

// inside some other class
var MyClassObject := MyClassFactoryObject.Create;
MyClassObject.DoSomething;
ouflak
  • 2,458
  • 10
  • 44
  • 49
0

The concept of DI is not to push instance creation to the root of the application but the composition of the object graph - hence the name "composition root". It is quite the opposite - given that the composition is done very close to the start of the application it is to be avoided to create all instances at that time because a) it can be really expensive, b) not all instances might be ever needed and c) some information might not be there at that point such as user input.

This is what "lazy initialization" solves and the container knows about this. It can automatically resolve Lazy<T> and Func<T>. These two differ from each other: Lazy<T> defers the instantiation to the point where .Value is actually accessed and then keeps the created instance for further use. An automatically created Func<T> from the container internally resolves each time it is being called.

The code in your answer makes me wonder if you fully understood the concept of DI because then you don't need to resolve something from the container and pass it on because that is what the container does - you only resolve the root of an object graph and everything beneath "unfolds" automatically (by being injected and lazily constructed) - nothing magic though that can exclusively be done with the container but also via pure DI and a proper architecture.

Stefan Glienke
  • 20,860
  • 2
  • 48
  • 102