4

I am faced with the problem of properly object instantiating from the type resolved by a Spring4D framework container.

I have a class:

type
  TSurvey = class ( TInterfacedObject, ISurvey )

  private
        _id : Integer;
        _organization : IOrganization;

        function GetId () : Integer;
        procedure SetId ( const value : Integer );

        function GetOrganization () : IOrganization;
        procedure SetOrganization ( const value : IOrganization);

  public
        property Id : Integer read GetId write SetId;
        property Organization: IOrganization read GetOrganization write SetOrganization;
end;

...

initialization

  GlobalContainer.RegisterType<TSurvey>.Implements<ISurvey>.InjectField ( '_organization' );

...

I use the GlobalContainer to instantiate an object:

survey := GlobalContainer.Resolve<ISurvey>;
survey.Organization.Id := 5;

and everything is alright and works perfectly.

Now I want to create a descendant class for TSurvey:

type
  TFieldSurvey = class ( TSurvey )
  ...
end;

And the question is how to correct instantiate an object for TFieldSurvey class?

If I use Create (), then I get an exception:

 fieldSurvey := TFieldSurvey.Create ();
 fieldSurvey.Organization.Id := 5    <- exception is here

Do I have to explicitly call the constructor for Organization field in TFieldSurvey constructor, or there is another way? For example, using GlobalContainer?

Thanks in advance.

Aptem
  • 169
  • 5

2 Answers2

4

The Injection will only work when you create your object through the Container, not by directly calling the constructor on your object. So you would need to register TFieldSurvey with GlobalContainer and then call Resolve to get your object.

Register:

GlobalContainer.RegisterType<TSurvey>.Implements<ISurvey>('SPRING_SURVEY').InjectField ( '_organization' );
GlobalContainer.RegisterType<TFieldSurvey>.Implements<ISurvey>('SPRING_FIELD_SURVEY').InjectField ( '_organization' );

Then to obtain an instance:

GlobalContainer.Resolve<ISurvey>('SPRING_FIELD_SURVEY')

I added the names of 'SPRING_SURVEY' and 'SPRING_FIELD_SURVEY' since they both implement ISurvey and this lets you choose which class instance you want otherwise you end up with the last implementation registered for that interface. If TFieldSurvey is going to implement its own interface (eg IFieldSurvey) you could do away with the names and then typecast back to ISurvey if needed.

You could always use the [Inject] attribute too on _organization field rather than using .InjectField (after adding Global.Container.Common to your uses):

  TSurvey = class ( TInterfacedObject, ISurvey )
  private
    _id : Integer;
    [Inject]
    _organization : IOrganization;

    function GetId () : Integer;
    procedure SetId ( const value : Integer );

    function GetOrganization () : IOrganization;
    procedure SetOrganization ( const value : IOrganization);

  public
    property Id : Integer read GetId write SetId;
    property Organization: IOrganization read GetOrganization write SetOrganization;
  end;

and your registration would be:

  GlobalContainer.RegisterType<TSurvey>.Implements<ISurvey>('SPRING_SURVEY');
  GlobalContainer.RegisterType<TFieldSurvey>.Implements<ISurvey>('SPRING_FIELD_SURVEY');
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Jason
  • 2,572
  • 3
  • 34
  • 41
2

You should not write your code in a way that it only works with the DI container.

The DI container is a tool and you should avoid dependencies (directly or indirectly) to it.

That means you should avoid using field injection because such code cannot work with pure DI - use constructor or property injection instead.

Also from the code snippet you posted I can smell the service locator anti pattern.

If you want to create surveys, then use a survey factory and inject that into the class where you are using it. DI containers are usually not for creating value objects and your survey class (despite unnecessarily having an interface) looks like one.

Before diving into using a DI container I really suggest learning more about how DI works and what techniques enable you to write clean code using DI. And only then start working with a DI container. Doing it the other way around will only lead to mistakes that make the code harder to maintain in the end.

Stefan Glienke
  • 20,860
  • 2
  • 48
  • 102
  • Dear Stefan, thank you for your comment. Can you give some examples why I should not use DI container in that way? What field injection need for, from this point of view? – Aptem Aug 03 '15 at 04:58