Einzelnen Beitrag anzeigen

neo4a

Registriert seit: 22. Jan 2007
Ort: Ingolstadt
362 Beiträge
 
Delphi XE2 Architect
 
#28

AW: Spring-DI / DelegatedConstructor / Factory für Dummies

  Alt 13. Feb 2012, 15:02
Verstehe ich das richtig?
Hier ein kleines Kochbuch:

Das Prinzip des Spring-DI-Containers ist - mit etwas Phantasie - vergleichbar mit den Abläufen bei der Automontage. Dort ist es ja eher unüblich, beim Montieren (Create) des Autos die Bestandteile (Motor, Blinker etc.) ebenfalls mit herzustellen: sie werden stattdessen aus dem Warenlager (DI-Container) so beigestellt, wie sie in der Produktion beim Create() des Autos benötigt werden.

In Spring meldest Du die Klassen, die Du im Warenlager verwalten willst, im einfachsten Fall so an:
Delphi-Quellcode:
unit uMotor;

interface

uses
  classes;

type
  IMotor = interface
    ['{F23C0AAF-0D7F-4CFF-A4A6-FDEB4EC5FDF0}']
    procedure Brumm;
  end;

implementation

uses
  Spring.Container,
  Spring.Services;

type
  TMotor = class(TInterfacedObject, IMotor)
  private
    procedure Brumm;
  end;

initialization
  GlobalContainer.RegisterComponent<TMotor>.Implements<IMotor>.AsSingleton;
Das passiert analog auch in den Units uBlinker etc.

Ich rufe i.d.R. in der dpr-Datei auf:
GlobalContainer.Build; Dieser einmalige Aufruf ist erforderlich, damit der Container weiß, dass alles beieinander ist. Vergisst man das, so gibt's später eine Fehlermeldung.

Dort, wo der Motor benötigt wird, macht man nicht mehr FMotor := TMotor.Create(); , sondern:
Delphi-Quellcode:
var
  aMotor : IMotor;
begin
  aMotor := ServiceLocator.GetService<IMotor>;
  aMotor.Brumm;
Das ist erst einmal schon alles. Damit man nicht die Unit uMotor einbinden muss, sollte die Interface-Deklaration von IMotor in eine eigene Unit ausgelagert werden. Bei mir sieht das z.B. so aus:
Delphi-Quellcode:
unit uInterfaces;

interface

uses
  Classes, SysUtils,

  uMotor, uBlinker
  ;

  procedure DI_Build;

type
  IMotor = uMotor.IMotor;
  function DI_Motor : IMotor;
type
  IBlinker = uBlinker.IBlinker;
  function DI_Blinker : IBlinker;

implementation

uses
  Spring.Container,
  Spring.Services;

procedure DI_Build; begin GlobalContainer.Build; end;

function DI_Motor : IMotor;
begin
  Result := ServiceLocator.GetService<IMotor>;
end;

function DI_Blinkr : IBlinker;
begin
  Result := ServiceLocator.GetService<IBlinker>;
end;
Mit Einbindung von uInterface braucht man in der aufrufenden Unit keine speziellen Units von Spring oder Motor einzubinden. Damit sind die Klassen vollständig entkoppelt. Das einzige Bindglied ist der DI-Container und der löst alles über Interfaces auf.

Mein typischer Aufruf sieht dann bspw. so aus:
Delphi-Quellcode:
var
  aMotor : IMotor;
begin
  aMotor := DI_Motor;
  aMotor.Brumm;
Der Spring-Container erfüllt dabei mehrere Funktionen: Er erzeugt die angeforderte Klassen, verwaltet deren Instanzen und gibt sie damit auch wieder frei. Die mitgelieferten Spring-Beispiele zeigen die weiteren Möglichkeiten. Mein Beispiel sollte nur zeigen, wie einfach der Einstieg sein kann und dass die Spring-Funktionalität sich nicht zu sehr aufdrängt.

Wofür das alles nun? Dadurch, dass an der Stelle, wo IMotor eingesetzt wird, nichts über TMotor und deren Unit bekannt sein muss, kann mann natürlich alles mögliche bereitstellen, solange es IMotor implementiert und damit die Prozedur Brumm() ausführen kann. Ganz klar auch, dass das Brummen im Motor-Teil erfolgen muss. An der konsumierenden Stelle hat man nichts über das Motor-Management zu wissen.

Nun ist der Motorwechsel einfach(er) und auch der Test eines Autos mit einem anderen oder Hilfs-Motor sollte kein Problem darstellen.

HTH.
Andreas

Geändert von neo4a (13. Feb 2012 um 17:30 Uhr) Grund: Interface bei Spring unbedingt mit GUID ausführen!
  Mit Zitat antworten Zitat