Home > front end >  How to configure dependencies in Spring4D in Delphi?
How to configure dependencies in Spring4D in Delphi?

Time:12-05

I'm reading Nick Hodges' book "Dependency Injection in DELPHI". The chapter "Registering Factories" considers this example: There is an interface:

ICoffeeMaker = interface
['{73436E03-EF65-44F5-9606-F706156CBEB5}']
  procedure MakeCoffee;
end;

...and the class that implements it:

type
  TCoffeeMaker = class(TInterfacedObject, ICoffeeMaker)
    private
      FCoffeeBrand: string;
      FBrewingMinutes: integer;
    public
      constructor Create(const aCoffeeBrand: string; const aBrewingMinutes: integer);
      procedure MakeCoffee;
    end;

constructor TCoffeeMaker.Create(const aCoffeeBrand: string; const aBrewingMinutes: integer);
begin
  inherited Create;
  FCoffeeBrand := aCoffeeBrand;
  FBrewingMinutes := aBrewingMinutes;
end;

procedure TCoffeeMaker.MakeCoffee;
begin
  WriteLn('Pour hot water over the ', FCoffeeBrand, ' so that it brews for ', FBrewingMinutes, ' minutes.');
end;

Next, a factory function is declared to create an instance of ICoffeeMaker:

  {$M }
  TCoffeeMakerFactory = reference to function(const aCoffeeBrand: string; const aBrewingMinutes: integer): ICoffeeMaker;
  {$M-}

...and the whole thing is registered in the container:

procedure RegisterStuff(aContainer: TContainer);
begin
  aContainer.RegisterType<ICoffeeMaker, TCoffeeMaker>.AsDefault;
  aContainer.RegisterFactory<TCoffeeMakerFactory>;
  aContainer.Build;
end;

And then the code of the main program is given:

var
  CoffeeName: string;
  BrewingMinutes: integer;
  CoffeeMakerFactory: TCoffeeMakerFactory;
  CoffeeMaker: ICoffeeMaker;
begin
  Write('What kind of coffee do you want to make? ');
  ReadLn(CoffeeName);

  Write('How many minutes? ');
  ReadLn(BrewingMinutes);

  CoffeeMakerFactory := Container.Resolve<TCoffeeMakerFactory>();
  CoffeeMaker := CoffeeMakerFactory(CoffeeName, BrewingMinutes);
  CoffeeMaker.MakeCoffee;
end;

The author writes that it is not necessary to register TCoffeeMaker in the container. But suddenly we will have some TKitchen class, which will have ICoffeeMaker as a dependency. The question is: What if there really is a TKitchen class that implements the IKitchen interface, which in the constructor takes ICOfeeMaker as a parameter? It turns out I will register it in the container:

begin
  //...
  aContainer.RegisterType<IKitchen, TKitchen>;
  //...
end;

...and then create in the main program:

begin
  //...
  Kitchen := Container.Resolve<IKitchen>();
  //...
end;

How, in this case, to choose the desired implementation and configure the created instance of ICoffeeMaker?

CodePudding user response:

To be honest that code example is a kinda bad one.

You don't create new coffeemakers for different brands or brewing times. These are different things. The brand and brewing time should be parameters on the MakeCoffee method. If a coffeemaker has some internal parts like a grinder for the beans or something like that that could be a ctor parameter.

And if you change the code like this your question disappears because you don't need to create new coffeemakers just to brew different brands of coffee with different brewing times.

Of course you could have a different model where these things are indeed passed into the ctor but then you have a coffeemaker with a fixed brand and brewing time and you simple "press the button" to get a coffee and it does it for you.

Now if you have a kitchen that has a coffeemaker you don't create a new kitchen with a new coffeemaker just to get a different brand or brewing time, do you? Yes, I am comparing the real world with software here but it shows that something is not right with this software model.

  • Related