AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Dependency Injection vs Service Locator -- dynamisches Erzeugung von Objekten
Thema durchsuchen
Ansicht
Themen-Optionen

Dependency Injection vs Service Locator -- dynamisches Erzeugung von Objekten

Ein Thema von s.h.a.r.k · begonnen am 1. Aug 2012 · letzter Beitrag vom 5. Aug 2012
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von s.h.a.r.k
s.h.a.r.k

Registriert seit: 26. Mai 2004
3.159 Beiträge
 
#1

Dependency Injection vs Service Locator -- dynamisches Erzeugung von Objekten

  Alt 1. Aug 2012, 15:47
Nun muss ich doch nochmals mein Thema aufgreifen und um Rat fragen. Ich sitze glaub irgendwie auf dem Schlauch und habe echt keine Ahnung, wie ich das Problem mittel Dependecy Injection lösen kann. Ich habe mal zwei Beispiele:
  • Ich habe eine Logger-Instanz, die mehrere Log-Writer bedienen kann. Log-Writer können z.B. in die Console Logs schreiben, in eine Datei oder in eine Datenbank. Wenn meine Applikation startet werden, je nach Konfiguration verschiedene Log-Writer erzeugt und dem Logger (Logger.AddWriter) übergeben. Evtl. kommen weitere Log-Writer noch während der Laufzeit hinzu -- ein Datenbank-Log-Writer kann z.B. erst dann hinzugefügt werden, wenn eine DB-Verbindung besteht.
  • Es wird eine Datenbank-Abfrage gestartet und daraus Objekte generiert. Es ist erst zur Laufzeit bekannt, wie viele Objekte es werden. Beispiel: Laden von Produkten.
So, nun habe ich einen Dependency-Container à la Spring für Delphi. Dependency Injection habe ich soweit nun auch verstanden, ebenso warum ich keinen Service Locator nutzen sollte.

Aber ich finde einfach keine sinnvolle Lösung, wie ich die obigen Probleme lösen kann. Ich komme leider immer wieder zu dem Punkt, dass ich auf den Service Locator zurückgreifen muss, um dynamisch eine beliebige Anzahl an Objekten erzeugen zu können.

Hier noch ein wenig Code, dass wir nicht nur über dieses total abstrakte Gelaber diskutieren müssen
Delphi-Quellcode:
function TUserFinder.Find(const Username: string): IList<IUser>;
var
  Query : IDbQuery;
begin
  Query := FDatabase.GetQuery();
  Query.SQL := 'SELECT * FROM Users WHERE username = :username';
  Query.Parameters.Add(':username', Username);
  Query.Open();
  try
    Result := TList<IUser>.Create();
    for each Dataset in Query.Results do
    begin
      //
      // User erzeugen ???
      //
      Result.Add(User);
    end;
  finally
    Query.Close();
  end;
end;
Oder habe ich überhaupt etwas falsch verstanden? Ich dreh mich gerade gedankelich leider nur noch im Kreis...
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)
  Mit Zitat antworten Zitat
Benutzerbild von sx2008
sx2008

Registriert seit: 16. Feb 2008
Ort: Baden-Württemberg
2.332 Beiträge
 
Delphi 2007 Professional
 
#2

AW: Dependency Injection vs Service Locator -- dynamisches Erzeugung von Objekten

  Alt 1. Aug 2012, 17:14
Du brauchst hier eine Fabrikmethode oder eine Factory.
Delphi-Quellcode:
// Fabrikmethode
// darf auch virtuell sein, damit man die implementierende Klasse von IUser
// ändern kann
function TUserFinder.CreateUser(UserID:Integer):IUser;
begin
  Result := TUser.Create(UserID);
end;

function TUserFinder.Find(const Username: string): IList<IUser>;
var
  Query : IDbQuery;
begin
  Query := FDatabase.GetQuery();
  Query.SQL := 'SELECT * FROM Users WHERE username = :username';
  Query.Parameters.Add(':username', Username);
  Query.Open();
  try
    Result := TList<IUser>.Create();
    for each Dataset in Query.Results do
    begin
      // User erzeugen mit Fabrikmethode
      User := CreateUser(Query['IdUser']);
      User.Username := Username;
      User.LastLogon := Query['LastLogon'];
      ...
      Result.Add(User);
    end;
  finally
    Query.Close();
  end;
end;
Mit einer Factory sieht das so aus:
Delphi-Quellcode:
TUserFactory = class(TObject)
public
  class function CreateUser(UserID:integer):IUser;virtual;
end

//===============
// Jenachdem ob nur in der Find-Methode neue IUser-Objekte erzeugt werden müssen,
// oder ob noch andere Methoden die Factory benötigen
// übergibt man die Factory als Parameter oder als Property der Klasse TUserFinder
function TUserFinder.Find(const Username: string; UF:TUserFactory): IList<IUser>;
var
  Query : IDbQuery;
begin
  Query := FDatabase.GetQuery();
  Query.SQL := 'SELECT * FROM Users WHERE username = :username';
  Query.Parameters.Add(':username', Username);
  Query.Open();
  try
    Result := TList<IUser>.Create();
    for each Dataset in Query.Results do
    begin
      // User erzeugen mit Factory
      User := UF.CreateUser(Query['IdUser']);
      User.Username := Username;
      User.LastLogon := Query['LastLogon'];
      ...
      Result.Add(User);
    end;
  finally
    Query.Close();
  end;
end;
Ein Service Locator ist ja auch eine Art Factory.
Allerdings kann ein Service Locator ALLE Arten von Objekten erzeugen,
während eine Factory auf eine bestimmte Gruppe von Objekten begrenzt ist.

Wenn man eine Factory wie oben im Beispiel übergibt ("injected"), dann bleibt der Code
weiterhin testbar und sauber.

Geändert von sx2008 ( 1. Aug 2012 um 17:17 Uhr)
  Mit Zitat antworten Zitat
Elvis

Registriert seit: 25. Nov 2005
Ort: München
1.909 Beiträge
 
Delphi 2010 Professional
 
#3

AW: Dependency Injection vs Service Locator -- dynamisches Erzeugung von Objekten

  Alt 1. Aug 2012, 17:21
Gib deinem TUserFinder (oder einem seiner Vorfahren) einfach eine Referenz auf den passen DI-Container mit.
Wenn du den TUserFinder per DI erzeugst, wäre sogar kaum eine Änderung an Code nötig. Da du diese Eiegenschaft ja als Dependency markieren kannst.
Wenn du ganz faul bist, kannst du ja gleich die IUserFactory als Depency-Property deklarieren.

Delphi-Quellcode:
function TUserFinder.Find(const Username: string): IList<IUser>;
var
  Query : IDbQuery;
  userFactory : IUserFactory;
begin
  Query := FDatabase.GetQuery();
  Query.SQL := 'SELECT * FROM Users WHERE username = :username';
  Query.Parameters.Add(':username', Username);
  Query.Open();
  try
    userFactory := Container.Resolve<IUserFactory>();
    Result := TList<IUser>.Create();
    for each Dataset in Query.Results do
    begin
      // nimmsu halt die Factory aus'm Container
      User := userFactory.CreateUser(Query['IdUser']);
      User.LastLogon := Query['LastLogon'];
      ...
      Result.Add(User);
    end;
  finally
    Query.Close();
  end;
end;
Das problem mit Delphi und DI ist natürlich, dass Delphi keinen Garbage Collector hat. Ohne den ist das Ganze immer sau frickelig.
Eigentlich kann man in Delphi nur Interfaces in so einen DI-Container werfen. Da du ja nie weißt, ob du eine neue Instanz bekommst, oder ob du eine nereits registrierte Instanz kriegst.
Robert Giesecke
I’m a great believer in “Occam’s Razor,” the principle which says:
“If you say something complicated, I’ll slit your throat.”

Geändert von Elvis ( 1. Aug 2012 um 17:29 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von s.h.a.r.k
s.h.a.r.k

Registriert seit: 26. Mai 2004
3.159 Beiträge
 
#4

AW: Dependency Injection vs Service Locator -- dynamisches Erzeugung von Objekten

  Alt 1. Aug 2012, 17:31
Danke schon mal für die Tipps. Jo, das dachte ich mir schon, aber ich habe damit noch ein mehr oder weniger gravierendes Problem!

@sx2008:
Delphi-Quellcode:
// Fabrikmethode
// darf auch virtuell sein, damit man die implementierende Klasse von IUser
// ändern kann
function TUserFinder.CreateUser(UserID:Integer):IUser;
begin
  Result := TUser.Create(UserID);
end;
Hier wird das TUser-Objekt leider nicht vom DI-Container erzeugt, d.h. es werden auch keinerlei Abhängigkeiten aufgelöst. Das ist schlecht. Daher ist die Lösung von Elvis mehr oder weniger besser.

@Elvis:
Bei deiner Lösung habe ich das Problem, dass in jeder Unit, in der ich Objekte erzeuge, der ServiceLocator bekannt sein muss. Ich dachte, dass diese Abhängigkeit eben nicht entstehen sollte. Aber wie schon gesagt, ich bin bisher auf noch keinen grünen Zweig gekommen, der diese Abhängigkeit eleminiert. Ich zweifle auch stark daran, dass ich diese Abhängikeit entfernen kann.

Ich würde je gewünschten Typen eine entsprechende Factory basteln. Den ServiceLocator überall einzubauen wäre eher schlecht für die Lesbarkeit.
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)
  Mit Zitat antworten Zitat
Elvis

Registriert seit: 25. Nov 2005
Ort: München
1.909 Beiträge
 
Delphi 2010 Professional
 
#5

AW: Dependency Injection vs Service Locator -- dynamisches Erzeugung von Objekten

  Alt 1. Aug 2012, 17:33
@Elvis:
Bei deiner Lösung habe ich das Problem, dass in jeder Unit, in der ich Objekte erzeuge, der ServiceLocator bekannt sein muss. Ich dachte, dass diese Abhängigkeit eben nicht entstehen sollte. Aber wie schon gesagt, ich bin bisher auf noch keinen grünen Zweig gekommen, der diese Abhängigkeit eleminiert. Ich zweifle auch stark daran, dass ich diese Abhängikeit entfernen kann.

Ich würde je gewünschten Typen eine entsprechende Factory basteln. Den ServiceLocator überall einzubauen wäre eher schlecht für die Lesbarkeit.
Dann packe die IUserFactory als Property in dein UserDings und markiere sie als Dependency.
Wenn du jetzt den TUserFinder durch den DI-Container hochziehst, wird er dort eine passende Instanz von der Factory reinstecken.

Ich selbst habe oft neben den ctor parametern und Properties, die vom DI autom. gefüllt werden, außerdem noch oft den Container in einem Vorfahren deklariert.
Je nach use-case hat man sonst zu viele unnütze Dependencies, die aufgelöst werden müssen, aber in der Lebenszeit des Objektes gar nicht benutzt werden.
Muss man halt abwägen...
Robert Giesecke
I’m a great believer in “Occam’s Razor,” the principle which says:
“If you say something complicated, I’ll slit your throat.”

Geändert von Elvis ( 1. Aug 2012 um 17:35 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von s.h.a.r.k
s.h.a.r.k

Registriert seit: 26. Mai 2004
3.159 Beiträge
 
#6

AW: Dependency Injection vs Service Locator -- dynamisches Erzeugung von Objekten

  Alt 1. Aug 2012, 17:50
Dann packe die IUserFactory als Property in dein UserDings und markiere sie als Dependency.
Wenn du jetzt den TUserFinder durch den DI-Container hochziehst, wird er dort eine passende Instanz von der Factory reinstecken.
Ich hatte die Hoffnung, dass der DI-Container selbst das erstellen beliebig vieler Objekte automatisch kann und ich gar nicht erst auf einen ServiceLocator zurückgreifen muss.

Ich selbst habe oft neben den ctor parametern und Properties, die vom DI autom. gefüllt werden, außerdem noch oft den Container in einem Vorfahren deklariert.
Je nach use-case hat man sonst zu viele unnütze Dependencies, die aufgelöst werden müssen, aber in der Lebenszeit des Objektes gar nicht benutzt werden.
Muss man halt abwägen...
Jo, das ist mir auch schon durch den Kopf gegangen. AOP ist hier ein Stichwort, zumindest ist mir das hin und wieder im Bezug auf Logging über den Weg gelaufen. Auf der anderen Seite muss ich einfach nur beliebig viele Basis-Objekte definieren, dann kann ich immer vom passenden ableiten
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)
  Mit Zitat antworten Zitat
Elvis

Registriert seit: 25. Nov 2005
Ort: München
1.909 Beiträge
 
Delphi 2010 Professional
 
#7

AW: Dependency Injection vs Service Locator -- dynamisches Erzeugung von Objekten

  Alt 1. Aug 2012, 18:21
Dann packe die IUserFactory als Property in dein UserDings und markiere sie als Dependency.
Wenn du jetzt den TUserFinder durch den DI-Container hochziehst, wird er dort eine passende Instanz von der Factory reinstecken.
Ich hatte die Hoffnung, dass der DI-Container selbst das erstellen beliebig vieler Objekte automatisch kann und ich gar nicht erst auf einen ServiceLocator zurückgreifen muss.
Woher kommt dein TUserFinder?
Hast du ihn direkt aus einem DIC gezogen, oder ist er eine Dependency einer anderen Klasse?
Ist er eine Dependency, dann würde der DIC ja autom. alles machen. Wenn nicht, dann mussu den selbst aus dem Container hochziehen.


Hier mal ein Bleistift, damit wir von der gleichen Sache reden. (Ich nehme einfach an, dass dein DI-Framework [Dependency] als Markierung für Abhängigkeiten verwendet)

Delphi-Quellcode:
type
   TSomeClass = class
   protected
      [Dependency]
      property UserFinder : IUserFinder...;
   public
      function FindUsers(...) : IList<TUser>;
   ...
   TUserFinder = class(IUserFinder)
   protected
      [Dependency]
      property UserFactory : IUserFactory...;
Du hast also irgendeine Klasse, die einen IUserFinder als Property nutzt.
Der wiederum hat eine Property UserFactory.

Würdest du jetzt zum Beispiel das hier machen...
Delphi-Quellcode:
var miep := container.Resolve<TSomeClass>();
miep.FindUsers(...)
...würde der DIC:
  • TSomeClass erzeugen
  • sieht die Dependency-Property
  • packt dir eine IUserFactory rein, die er vllt erst erzeugen muss
  • sieht darin die DependencyProperty
  • packt da die TUserFactory rein
  • sieht deren Dependencies
  • ...

Ich habe da schon ganz schlimme Dinge gesehen, bei denen nicht bedacht wurde was für einen Rattenschwanz das nach sich ziehen kann.
In .Net ist das einfach. Da nimmt man Post# und kann sich (wie du erwähntest) per AOP piepe-einfach die Depencies zu Lazy-Loaded ändern.
In Delphi hassu da keinen, der dir da hilft...

Der Sinn von DI ist ja nicht, dass überall alles von alleine geht (was ja schlecht möglich ist ), man soll die Möglichkeit haben, Implementierungen von außen ändern zu können. (Testbarkeit, Mocks!)
Das geht genauso, wenn man einen Container durchreicht. (Wobei der Container das durchreichen erledigt)
Robert Giesecke
I’m a great believer in “Occam’s Razor,” the principle which says:
“If you say something complicated, I’ll slit your throat.”

Geändert von Elvis ( 2. Aug 2012 um 09:34 Uhr) Grund: Boah, ich glaube ich habe mich in der Disziplin Typos und Edits pro Post ins Olympische Finale editiert...
  Mit Zitat antworten Zitat
Benutzerbild von s.h.a.r.k
s.h.a.r.k

Registriert seit: 26. Mai 2004
3.159 Beiträge
 
#8

AW: Dependency Injection vs Service Locator -- dynamisches Erzeugung von Objekten

  Alt 2. Aug 2012, 09:28
Ich habe mir die Sache nun weiter gründlich durch den Kopf gehen lassen und glaube, dass ich nun mein Problem besser formulieren kann. Dein Beispiel ist gut gewählt und ich würde vorschlagen, dass wir dabei bleiben, wenn wir weiterhin über das Thema diskutieren. Dass die Dependencies entsprechend aufgelöst werden ist ja mein Wunsch, aber scheinbar ist es so, dass nur "1:1-Abhängigkeiten" aufgelöst werden. Es gehen auch 1:N-Beziehungen, aber nur mit einem vorher fest gewählten N -- ich meine damit, dass es fixe N Properties geben muss, die dann entsprechend gefüllt werden. Folgendes klappt scheinbar nicht:
Delphi-Quellcode:
TUserFactory = class
private
  [Dependency(10)]
  FUserList : IList<IUser>;
end;
Ich will mit der 10 andeuten, dass 10 User-Objekte beim Erzeugen des Objekts injiziert werden sollen -- wobei ich doch ganz gerne die Zahl parametrisiert haben wollen würde, sodass ich eigentlich NIE auf den Service Locator zurückgreifen muss. Aber ich wüsste selbst nicht, wie ich diese Problem lösen kann, daher genau dieser Thread hier

Daraus kommt für mich eigentlich nur ein Schluss bei raus: ich *muss* auf den Service Locator immer zugreifen, da ich nur sehr selten eine Anwendung habe, die keine Daten à la User oder Produkte aus einer DB lädt. Nutze ich für die Erstellung einer vorher nicht bekannten, beliebig großen Menge an Objekten (User, Produkte etc.) das Factory-Pattern, so muss jede Factory auf den Service Locator zurückgreifen und ist abhängig davon.

Klar, die Abhängigkeit ist überschaubar und der Gewinn durch den DI-Container, der die Abhängigkeiten der erstellen Objekte entsprechend auflöst, ist echt groß, aber hier könnte man fast noch ein Stückchen mehr nachbessern und den DI-Container optimieren. Aber das ist alles nur Wunschdenken, ohne eine Idee eines eigenen Ansatzes
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)
  Mit Zitat antworten Zitat
Elvis

Registriert seit: 25. Nov 2005
Ort: München
1.909 Beiträge
 
Delphi 2010 Professional
 
#9

AW: Dependency Injection vs Service Locator -- dynamisches Erzeugung von Objekten

  Alt 2. Aug 2012, 09:40
Man muss beim Programmieren aufpassen, dass man nicht dogmatisch wird.
Sicher muss eine gewisse Eleganz gewährleistet sein, damit man an dem Code arbeiten will. Keine Frage!
DI und property/ctor- Injection ermöglicht es dir für viele Business-Klassen fast ohne direkte Berührung mit dem DIC auszukommen. Aber wie mit allem im Leben: Wenn man es übertreibt, wird's frickelig und kompliziert.

Dogma und Scheuklappen haben in der Software-Entwicklung nix zu suchen, das ist das Betätigungsfeld für Pfarrer, Astrologen und Homöopathen...
Robert Giesecke
I’m a great believer in “Occam’s Razor,” the principle which says:
“If you say something complicated, I’ll slit your throat.”
  Mit Zitat antworten Zitat
Benutzerbild von s.h.a.r.k
s.h.a.r.k

Registriert seit: 26. Mai 2004
3.159 Beiträge
 
#10

AW: Dependency Injection vs Service Locator -- dynamisches Erzeugung von Objekten

  Alt 2. Aug 2012, 09:43
Wieso findet dann Philosophie seinen Platz in der Programmierung?!

Ne, Spass bei Seite... Habe glaub komplett verstanden, wie der Hase läuft. Danke für die (Er)Klärung
»Remember, the future maintainer is the person you should be writing code for, not the compiler.« (Nick Hodges)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:02 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz