PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Erfahrung mit Dependency Injection Frameworks?


Monger
2015-01-02, 13:49:52
Nachdem mich auf Arbeit das Thema gerade umtreibt, wollte ich mal hier nachfragen: hat hier jemand Erfahrung mit Dependency Injection, und mit entsprechenden Frameworks?

Ich denke mir ist die Theorie einigermaßen klar, wie gut funktioniert das denn in der Praxis? Wird z.B. die Kompositionswurzel einer Anwendung irgendwann mal unüberschaubar groß? Ist es wirklich praktikabel, Cross-Cutting Concerns wie Logging o.ä. darüber zu lösen?

Ich lese mich gerade in Unity (https://unity.codeplex.com/) rein. Hat speziell damit jemand Erfahrung? Oder mit einem anderen Framework? Irgendeine Meinung dazu?

Shink
2015-01-02, 15:10:21
hat hier jemand Erfahrung mit Dependency Injection, und mit entsprechenden Frameworks?

Wird z.B. die Kompositionswurzel einer Anwendung irgendwann mal unüberschaubar groß?
2 Mal "aber natürlich".:freak:

Ich komme aus der Java-Ecke, da stellt sich irgendwie keiner diese Frage - DI bzw IoC ist da seit 10 Jahren "Standard" mit EJB und Spring.

HajottV
2015-01-02, 16:34:37
Erfahrung? funktioniert in der Praxis?
Wird [...] unüberschaubar groß? Ist es wirklich praktikabel, Cross-Cutting Concerns wie Logging o.ä. darüber zu lösen?

Ja, funzt prima. Ich könnte mir nicht mehr vorstellen, meine Applikationen zu 'hardwire-n'.

Nuja, ich hatte mal in Kombination mit Apache Camel einen Context, der - obwohl er in viele kleine Kontexte modularisiert war - nicht mehr leicht durchschaubar war. Das lag aber an den Camel-Routen und nicht an der DI (damals mit Spring).

Logging macht man i.d.R. nicht darüber, sondern über log4j & Co.

Aber über die AoP kann man sehr coole Dinge machen, die man sonst nur mit viel mehr Aufwand realisieren könnte.

Wenn man's richtig gemacht hat, wird das Testen auch deutlich vereinfacht und die Wiederverwendbarkeit steigt enorm.

Unity kenne ich nicht (da .net).

Marscel
2015-01-02, 18:52:08
Ich lese mich gerade in Unity (https://unity.codeplex.com/) rein. Hat speziell damit jemand Erfahrung? Oder mit einem anderen Framework? Irgendeine Meinung dazu?

Ich bin sehr dafür, dass DI angesichts des Schunds, der mit OO so betrieben wird, echt ein paar Entwickler mehr erreichen sollte. Aber das Unity sieht ja nach höllischem Overengineering aus, gepaart mit einem Haufen Reflection, resultierend in schlechter Lesbarkeit und mehr Overhead als nötig.

Trap
2015-01-02, 19:52:08
Ich verwende aktuell keins der Frameworks, aber von der Dokumentation und angebotenen API ist mir https://simpleinjector.readthedocs.org am sympathischsten - die Projekthomepage ist https://simpleinjector.codeplex.com/ .

Die haben eine klare Position was richtige Verwendung ist (mit Argumenten für ihre Position) und eine API die das möglichst einfach und zuverlässig macht.

Allgemein zum Thema DI kann ich noch das Buch http://www.manning.com/seemann/ empfehlen. Sehr anschauliche Beschreibung mit guten Beispielen.

Monger
2015-01-02, 20:40:03
Ich bin sehr dafür, dass DI angesichts des Schunds, der mit OO so betrieben wird, echt ein paar Entwickler mehr erreichen sollte. Aber das Unity sieht ja nach höllischem Overengineering aus, gepaart mit einem Haufen Reflection, resultierend in schlechter Lesbarkeit und mehr Overhead als nötig.
Kannst du das an was konkretem festmachen? Wie gesagt: mir ist das Thema noch relativ neu, ich kann das noch nicht abschätzen. Würde daher gerne wissen wie denn einfachere Lösungen aussehen würden.

Den Geradeaus-Weg bei Unity finde ich eigentlich recht plausibel:

container.RegisterType<IStorage, FileStorage>();
var controller = container.Resolve<MainController>();

Unter der Annahme hier dass MainController einen IStorage verlangt.

Die Frage ist halt: was braucht man denn in der Praxis an komplexeren Konstrukten, und wie sieht das in anderen Frameworks aus? Wie lösen denn andere Frameworks das was Unity "Interception" nennt?

Monger
2015-01-02, 20:43:30
Allgemein zum Thema DI kann ich noch das Buch http://www.manning.com/seemann/ empfehlen. Sehr anschauliche Beschreibung mit guten Beispielen.
Das ist genau das Buch was mich überhaupt erst zu DI gebracht hat. Hab das quasi blind gekauft, und fand es einen sehr guten Kauf.

Allerdings auch ein harter Brocken. Der Autor sagt ja selbst: die meisten Leser berichten davon, dass erst etwa nach dem ersten drittel des Buches bei den meisten der Groschen fällt. Bis ungefähr dahin habe ich auch sehr mit der Stirn gerunzelt was denn der ganze Krams soll.

Marscel
2015-01-03, 00:17:27
Wenn ich es in C++ richtig auf die Spitze treibe, dann hab ich da solche Faxen:


class Something;
Something::Something(<Argumente>, SomethingConfig sc);
Something::some_action() {
IFactoryForSomeIOtherThing &factory = sc.factory_for_other_thing();
IOtherThing *other_thing = factory.create(<Parametrisierung>);
// ...
IObject &obj = sc.object_something_depends_on();
}


Wenn ich richtig sehe, was mit Interceptor gemeint ist: Du erstellst in deinem Setup Ketten von Objekten, die alle dasselbe Interface haben.

Samplemäßig so:

#include <iostream>

class ISomething {
public:
virtual int return_val(int val) = 0;
};

class RealSomething : public ISomething {
public:
int return_val(int val) {
return val + 1;
}
};

class LogISomething : public ISomething {
public:
LogISomething(ISomething &_sth) : sth(_sth) {}

int return_val(int val) {
std::cout << "RETURN_VAL WITH " << val << std::endl;
return sth.return_val(val);
}

private:
ISomething& sth;
};

class ForceISomething : public ISomething {
public:
ForceISomething(ISomething &_sth) : sth(_sth) {}

int return_val(int) {
return sth.return_val(3);
}
private:
ISomething& sth;
};

class NeedsSomething {
public:
NeedsSomething(ISomething &_sth) : sth(_sth) {}
int action() {
return sth.return_val(123);
}
private:
ISomething& sth;
};

int main() {
RealSomething real;
ForceISomething force(real);
LogISomething log(force);

NeedsSomething depends(log);
std::cout << "GOT " << depends.action() << std::endl;
return 0;
}

Ausgabe:

RETURN_VAL WITH 123
GOT 4

Unfug
2015-01-04, 14:12:40
Meine Erfahrungen sind so lala. Ein interessantes Features aber kein Must Have.

Pro:
Man wird zu Interfaces gezwungen. Viele Entwickler machen das nicht automatisch (warum auch immer).

Contra:
Abhängigkeiten werden in eine zentrale Stelle ausgelagert.
Man übertreibt es gerne (alles und jeder wird injected)
Eine weitere Abhängigkeit, zum Framework, existiert
Je nach Framework müssen Attribute in die Klassen geschrieben werden.


Meine Erfahrung ist: Interfaces, Interfaces, Interfaces. Verinnerlicht man dies ist die Welt automatisch schöner.

Falls man doch mal in C# was mit DI machen will. http://www.ninject.org/

Konzentriert sich auf das Wesentliche.

Monger
2015-01-04, 16:03:45
Wenn ich es in C++ richtig auf die Spitze treibe, dann hab ich da solche Faxen:


class Something;
Something::Something(<Argumente>, SomethingConfig sc);
Something::some_action() {
IFactoryForSomeIOtherThing &factory = sc.factory_for_other_thing();
IOtherThing *other_thing = factory.create(<Parametrisierung>);
// ...
IObject &obj = sc.object_something_depends_on();
}


Mir ist ehrlich gesagt nicht klar, was du mit dem Beispiel sagen willst. Ich sehe zumindest nicht was das mit irgendeinem DI Framework zu tun hat.


Wenn ich richtig sehe, was mit Interceptor gemeint ist: Du erstellst in deinem Setup Ketten von Objekten, die alle dasselbe Interface haben.

Ich muss schauen dass ich mit meinen Begriffen präziser werde. Worauf ich mich eher bezog, waren die Interception Behaviors, also Interceptors die für ALLE registrierten Interfaces anwendbar sind. Also z.B. jedesmal wenn eine Methode verlassen wird, wird der Rückgabewert ins Log geschrieben o.ä. .

Die sind in der Tat sehr Reflection-lastig, und momentan frage ich mich ob das tatsächlich von praktischem Wert ist. Damit macht man ja implizite Annahmen über die Implementierung, die auch brechen können.


Contra:
Abhängigkeiten werden in eine zentrale Stelle ausgelagert.

Inwiefern siehst du das als Nachteil? Ich mein, das ist ja nun mal das zentrale Merkmal von DI. Bessere Wartbarkeit weil keine Codeduplikation, etc. .


Man übertreibt es gerne (alles und jeder wird injected)
Eine weitere Abhängigkeit, zum Framework, existiert
Je nach Framework müssen Attribute in die Klassen geschrieben werden.

Unity hat diese Attribute teilweise auch, halte ich aber für ein Antimuster. Kann man zum Glück vollständig ignorieren. Der Vorteil von DI gegenüber Service Locators etc. ist ja nunmal, dass der Code eben KEINE Abhängigkeit zu irgendeiner spezifischen Technologie benötigt. Theoretisch kannst du an der Kompositionswurzel das Framework auch komplett weglassen und alles von Hand bauen, oder etwa nicht? Ist für kleinere Applikationen mMn auch durchaus valide.


Falls man doch mal in C# was mit DI machen will. http://www.ninject.org/

Konzentriert sich auf das Wesentliche.
Ich hab mal einen Blick reingeworfen. Sieht ganz interessant aus, vor allem die sehr "sprechende" Syntax. Aber wesentlich simpler als Unity finde ich das nicht.

Allerdings, jetzt wo ich nochmal darin rumgewühlt habe: die Dokumentation von Unity ist mehr als bescheiden. Da wird Syntax wild aus mehreren Versionen vermischt, die Beispiele sind teils absurd kompliziert, und die Klassendokumentation an sich empfinde ich als relativ dünn. Ohne das dazugehörige PDF gelesen zu haben, finde ich es schon schwer sich da auf der Webseite zu orientieren.

Marscel
2015-01-04, 16:39:55
Mir ist ehrlich gesagt nicht klar, was du mit dem Beispiel sagen willst. Ich sehe zumindest nicht was das mit irgendeinem DI Framework zu tun hat.

Hat es auch nicht. Ich hatte mich ein wenig eingelesen und emfand den späteren Teil sehr wie Overhead und Konfiguration. Und hier eben, wie man es ohne und ohne viel Aufwand sogar in C++ auf die Weise schafft.

Ich muss schauen dass ich mit meinen Begriffen präziser werde. Worauf ich mich eher bezog, waren die Interception Behaviors, also Interceptors die für ALLE registrierten Interfaces anwendbar sind. Also z.B. jedesmal wenn eine Methode verlassen wird, wird der Rückgabewert ins Log geschrieben o.ä. .

Die sind in der Tat sehr Reflection-lastig, und momentan frage ich mich ob das tatsächlich von praktischem Wert ist. Damit macht man ja implizite Annahmen über die Implementierung, die auch brechen können.

Damit bestätigt sich ein wenig meine Skepsis. In .NET ja durchaus legitim, aber ob damit irgendwem am Ende geholfen ist, wenn man den Benutzercode so aufbaut ...

Meine Erfahrung ist: Interfaces, Interfaces, Interfaces. Verinnerlicht man dies ist die Welt automatisch schöner.

Richtig, damit tut man sich schon einen großen Gefallen in meinen Augen. Nicht, dass ich es für jeden Kleinkram so handhabe, aber in Hinsicht auf Austauschbarkeit oder Abstraktion der erste Griff.

Gast
2015-01-04, 18:30:46
Hat es auch nicht. Ich hatte mich ein wenig eingelesen und emfand den späteren Teil sehr wie Overhead und Konfiguration. Und hier eben, wie man es ohne und ohne viel Aufwand sogar in C++ auf die Weise schafft.


Dein Code zeigt doch nur ein Factory Pattern. Oder wer übergibt "sc"?

Beim DI geht es ja eben genau darum, die Typen zu Interfaces konfigurierbar zu halten, damit man eine lose Kopplung hat. Ohne die Anwendung neu kompilieren zu müssen.

Marscel
2015-01-04, 19:02:23
Dein Code zeigt doch nur ein Factory Pattern. Oder wer übergibt "sc"?

Dazu hast du dann irgendwo ein Setup, das dir das konfiguriert (im Release deine Produktivfactories und Instanzen, im Test die Mocks), und das kannst du im gewissen Rahmen, bei C++, ja auch Laufzeitparametrisieren.

Beim DI geht es ja eben genau darum, die Typen zu Interfaces konfigurierbar zu halten, damit man eine lose Kopplung hat. Ohne die Anwendung neu kompilieren zu müssen.

Da seh ich jetzt keinen Widerspruch zu meinem Beispiel.

Monger
2015-01-04, 19:27:07
Hat es auch nicht. Ich hatte mich ein wenig eingelesen und emfand den späteren Teil sehr wie Overhead und Konfiguration. Und hier eben, wie man es ohne und ohne viel Aufwand sogar in C++ auf die Weise schafft.

Dann ist das aber mehr eine Kritik an Dependency Injection selbst, und nicht am Framework, oder?

Gast
2015-01-04, 19:35:21
Das Factory Pattern kannst du so gut wie in jeder Sprache implementieren. Verstehe ich nicht, was das mit C++ zu tun haben soll.

Ein DI Framework hat ein paar Funktionen mehr als eine normale Factory. Sonst wären sie nicht so beliebt und man würde sie nicht nehmen.

Gegen das Factory Pattern ist ja nichts einzuwenden. Aber das hat man auch schon seit .NET 2.0 intensiv verwendet. Und Entwickler, die es nicht kennen, werden wohl auch nie ein DI Framework verwenden.

Marscel
2015-01-04, 20:07:43
Dann ist das aber mehr eine Kritik an Dependency Injection selbst, und nicht am Framework, oder?

Nein, keineswegs. Bloß Erstaunen darüber, dass dieses Framework-Setup einen Gewinn darstellen soll. Angesichts recht überschaubarer Eigenlösungen, und auch ohne Notwendigkeit von Reflection.

Monger
2015-01-04, 21:14:25
Nein, keineswegs. Bloß Erstaunen darüber, dass dieses Framework-Setup einen Gewinn darstellen soll. Angesichts recht überschaubarer Eigenlösungen, und auch ohne Notwendigkeit von Reflection.
Dann verstehe ich dein Codebeispiel wohl immer noch nicht. Du benutzt dort zwar Constructor Injection - aber darum ging es ja nie. DI Frameworks behandeln nur die Kompositionswurzel, also üblicherweise die Implementierung von main().

Eine manuelle Komposition ist ja durchaus auch erlaubt. Kommt halt drauf an was man braucht. LifeTime Management würde man noch von Hand hinkriegen, wenn auch mit n bissl Gefummel. Aber diese Interception Behaviors (http://msdn.microsoft.com/en-us/library/dn178466(v=pandp.30).aspx) um z.B. einen allgemeinen Caching oder Logging Code für verschiedene Interfaces zu implementieren, das wäre von Hand schon enorm anstrengend.

Shink
2015-01-05, 07:10:47
Aber diese Interception Behaviors (http://msdn.microsoft.com/en-us/library/dn178466(v=pandp.30).aspx) um z.B. einen allgemeinen Caching oder Logging Code für verschiedene Interfaces zu implementieren, das wäre von Hand schon enorm anstrengend.
Sieht eher nach AOP aus und nicht nach DI, oder? Man kann es ja auch ohne den Unity-Container verwenden.