Autor-Archiv

Assisted Inject

Veröffentlicht: 9. November 2011 von pmischke in Dependency Injection

Es geht weiter mit einem Dependency Injection Thema, das ich hier „Asssited Inject“ nenne. Ich kann nicht ausschließen, dass es noch weitere Namen für diese Technik gibt. Mir ist sie zuerst bei dem Guice Framework und eben diesem Namen über den Weg gelaufen:

http://code.google.com/p/google-guice/wiki/AssistedInject

Es geht um folgendes Problem:

  • Ich möchte mehrere Objekte eines Typs erzeugen.
  • Mein Typ hat Abhängigkeiten zu anderen Objekte, die über den Konstruktor vom DI Container gesetzt werden sollen. Diese Abhängigkeiten sind im einfachsten Fall für alle Objekte identisch (also Singleton Scope). Das muss nicht so sein, ist aber für das Assisted Inject irrelevant, also nehme ich den einfachsten Fall an.
  • Mein Typ hat daneben weiter Konstrutor-Argumente, die der Client des Objekts im Moment der Erzeugung spezifizieren soll. Diese Argumente (im folgenden Parameter genannt) sind wahrscheinlich für jedes Objekt verschieden. Dadurch entsteht hier der Bedarf, mehrere Objekte zu erzeugen, selbst wenn die Objekte keinen modifizierbaren Zustand besitzen.

Worin genau besteht das Problem? Nun ja, zum einen möchte ich natürlich, dass der DI Container die Objekte erzeugt. So kann er sich um die notwendigen Abhängigkeiten kümmern. Zum anderen sollen aber die Clients die Objekte erzeugen, um ihnen Parameter zum Zeitpunkte der Erzeugung mitgeben zu können. Wer soll also jetzt die Objekte erzeugen?

Das Pattern „Assisted Inject“ zur Lösung des Konflikts sieht wie folgt aus: Der DI Container erzeugt zunächst eine Factory für die Objekte. Diese Factory wird mit den Abhängigkeiten versehen, die später die Objekte erhalten sollen. Die Clients bekommen nun vom DI Container die Factory zur Verfügung gestellt. Zum gewünschten Zeitpunkt erzeugen die Clients dann die Objekte durch Aufruf einer Factory Methode. Die Factory Methode erhält die gewünschten Parameter und reicht diese zusammen mit den Abhängigkeiten – die der Factory bekannt sind – an den Konstruktor weiter.

Diese Lösung lässt sich leicht „von Hand“ programmieren und ist nicht von der Verwendung eines bestimmten DI Containers abhängig.

Es mag jedoch lästig erscheinen, die zusätzliche Factory zwischen Client und dem benötigtem Typ zu schreiben. Sie enthält im Prinzip nur „Boilerplate Code“ zum Durchreichen der Parameter und Abhängigkeiten. Wer Guice verwendet, bekommt dazu Hilfe angeboten. Eine Erweiterung zu Guice kann die Factory zur Laufzeit dynamisch generieren. Es genügt, ein Interface für die Factory Methode zu schreiben (siehe Link oben).

Auch für CDI existiert solch eine Erweiterung.

http://www.warski.org/blog/2010/12/improving-autofactoriesassisted-inject/

Der Autor dieser Erweiterung moniert in seinem Blog einen Mangel des vorgestellten Patterns. Im Konstruktor des verwendeten Typs sind Parameter und die echten Abhängigkeiten vermischt. Es ist nicht explizit mit OO Mitteln modelliert, was Abhängigkeiten und was Parameter sind. Daher muss man auch bei Verwendung einer der Erweiterung die Konstruktor-Parameter annotieren, die keine Abhängigkeiten sind. Ansonsten kann der DI-Container bzw. die Erweiterung diese nicht unterscheiden. Der Autor der CDI Erweiterung versucht diese Unschönheit durch kombinierte Verwendung von Kontruktor- und Feld-Injektion zu modellieren. Konstruktor-Injektion für die Paramter und Feld-Injektion für die Abhängigkeiten. Das ist in meinen Augen nicht unbedingt eine Verbesserung. Ich bin an sich kein Freund von Feld-Injektion. Und dann auch noch gemischt mit Konstrutor-Injektion scheint mir das ziemlich gekünstelt. Der Code wird dadurch nicht unbedingt verständlicher.

Ich möchte hier eine alternative Java Implementierung des Assited Inject Patterns vorstellen. Mir gefällt diese Implementierung aus zwei Gründen. Erstens ist der Unterschied zwischen den Abhängigkeiten und Parameter explizit mit OO Mitteln modelliert. Zweitens ist der Boilerplate Code etwas reduziert, dass vielleicht die Notwendigkeit für den Einsatz von Erweiterungen entfällt. Der Trick der Implementierung besteht in der Verwendung einer inneren Klasse.

public class PaymentFactory {

    private final CreditService creditService;

    @Inject
    public PaymentFactory(CreditService creditService) {
        this.creditService = creditService;
    }

    public class Instance implements Payment {

        public Instance(Date startDate, Money amount) {
            // ...
        }

        public void doSomething() {
            creditService.serviceCall();
        }

    }

}

public class Client {

    private final PaymentFactory paymentFactory;

    @Inject
    public Client(PaymentFactory paymentFactory) {
        this.paymentFactory = paymentFactory;
    }

    public void createPayment() {
        Money amount = null;
        Date startDate = null;

        Payment myPayment = paymentFactory.new Instance(startDate, amount);
    }

}

Da diese Lösung mit einer konkreten Factory ohne Interface arbeitet, kann man sich die Factory Mehtode sparen und die Java Syntax für Konstruktion von inneren Klassen verwenden. Unschön ist, dass sich die Factory nicht austauschen lässt (z.B. durch einen Mock für Tests) und dass alle Clients abhängig von der konkreten inneren Klasse sind. Wem das nicht gefällt kann auch ein Interface einziehen, muss dafür aber die Factory Methode ergänzen:

public class InnerClassPaymentFactory implements PaymentFactory {

    private final CreditService creditService;

    @Inject
    public InnerClassPaymentFactory(CreditService creditService) {
        this.creditService = creditService;
    }

    @Override
    public Payment create(Date startDate, Money amount) {
        return new Instance(startDate, amount);
    }

    public class Instance implements Payment {

        public Instance(Date startDate, Money amount) {
            // ...
        }

        public void doSomething() {
            creditService.serviceCall();
        }

    }

}

public class Client {

    private final PaymentFactory paymentFactory;

    @Inject
    public Client(PaymentFactory paymentFactory) {
        this.paymentFactory = paymentFactory;
    }

    public void createPayment() {
        Money amount = null;
        Date startDate = null;

        Payment myPayment = paymentFactory.create(startDate, amount);
    }

}

Beiden Lösungen ist gemein, dass der Unterschied zwischen Parametern und Abhängigkeiten explizit modelliert ist. Die Parameter sind die Konstruktor Argumente des betrachteten Typs (hier Payment). Die Abhängigkeiten tauchen nun als Abhängigkeiten der Factory auf. Das Pattern besteht ja genau in dem Trick, die Abhängigkeiten des Typs zu Abhängigkeiten der Factory zu machen. Die Regeln für Sichtbarkeit bei innere Klassen in Java erlauben es, dass die Instanzen des Typs Zugriff auf die Abhängigkeiten der Factory haben. Das Durchreichen der Abhängigkeiten entfällt.

Dependency Injection considered useful

Veröffentlicht: 12. August 2011 von pmischke in Dependency Injection

Im folgenden möchte ich gerne den Übergang vom letzten Eintrag Dependency Inversion Principal (DIP) zum Dependency Injection (DI) Container schaffen. Mit dem Titel meines Posts beziehe ich mich auf

Der Post von Nat Pryce ist ein wenig provokant aufgezogen und führte daher zu viel Diskussion in der Kommentar Sektion. Das abschließende Fazit von Nat lautet

Let me clarify… the title — „Dependency Injection“ considered harmful — refers to the fact that I consider the term „Dependency Injection“ harmful — it is misleading and as a result DI frameworks often do not actually do DI at all because they focus on „injection“ and not the clear, explicit management of dependencies between objects.

Zum einen geht es Nat also um die Wahl des Namens. Insbesondere um den Ausdruck „Injection“ für die Technologie, sprich die DI Container. Aber noch wichtiger kritisiert er, dass in Folge dessen die DI Container nicht dazu genutzt werden, die OO Konzepte hinter DI umzusetzten. Das sind Konzepte wie eben das DIP und die im Post genannten Verweise auf das GOOS Buch. Stattdessen wird die Technologie ohne Rücksicht auf die Konzepte verwendet. Im Gegenteil werden durch die Technologie sogar OO Prinzipien wie Kapselung ausgehebelt.

In das gleiche Horn stößt

Die wichtige Aussage von Mark Seemann in diesem Zusammenhang ist

What’s really lacking, at least in the .NET world, is an understanding that DI is really about principles and patterns, and not about technology.

As .NET developers we have been raised by Microsoft to believe that every problem can be addressed with a tool. When it comes to loose coupling, DI Containers look like enabling technology, but it’s really, really important to realize that the solution doesn’t lie with the tool. It lies in the understanding that DI (or Inversion of Control) is a fundamentally different way to reason about and design software.

This relates very closely to the SOLID principles – in fact, DI is just an elaboration of the ‚D‘ in SOLID, so it’s far from new knowledge. In my opinion, SOLID very nicely captures the essence of OOP, and if you write SOLID code, you also utilize DI. Technology in the shape of a container is totally optional at this point.

Auch Mark sagt, dass wichtige an DI sind die Konzepte, insbesondere das D aus den SOLID Prinzipien, also das DIP. Im übrigen ist der Irrglaube der heilbringenden Technologie in der Java Welt genauso verbreitet.

Fassen wir also zusammen: OO Konzepte wie das DIP sind notwendige Voraussetzung für erfolgreiche Anwendung von Dependency Injection. Bis zu dieser Stelle sind Nat, Mark, wahrscheinlich viele andere auch und ich alle der selben Meinung. Bei der Rolle der Technologie in Form des DI Containers gehen die Meinungen jedoch auseinander.

Nat hält den Container für eine Behinderung. Darum verzichtet er ganz auf ihn und schreibt die separierten Kompositionslogik lieber selbst. Die Kompositionslogik ist dadurch klar in Code ausgedrückt, gut sichtbar und unterliegt dem normalen Refactoring. Die Argumente sind durchaus nachvollziehbar. Ja, es ist wichtig, die Abhängigkeiten explizit im Code auszudrücken. Daher befürworte ich, dass die JSR-330 DI Container die @Inject Annotation am Konstrutor erzwingen, auch wenn man das per Konvention regeln könnte, wie es einige .NET DI Container machen. Auf die Idee, die Kompositionslogik selber zu schreiben, würde ich nicht kommen. In der Kompositionslogik finden sich wiederkehrende Muster. Zum Teil sind diese Muster bereits hier und da dokumentiert, zum Teil auch nicht (Vielleicht ist das ein oder andere Muster demnächst in diesem Blog zu finden 🙂 ). Wie dem auch sei, sollte man die wiederkehrende Logik doch allgemein und wiederverwendbar lösen und nicht in jeder Anwendung neu. Das ist doch genau die Leistung des DI Containers, oder?

Mark beschäftigt sich sehr intensiv mit Containers, hat ein Buch dazu in Vorbereitung. Das ist alles andere als Ablehnung. Dennoch hält er den DI Container für optional (siehe Zitat oben).

Und wie stehe ich zu DI Containern? Natürlich kann der DI Container missbraucht werden, wie jede Technologie. Aber die Möglichkeit zum Missbrauch ist für mich noch kein Grund zur Ablehnung. Sauberes OO Design ist nach wie vor die notwendige Voraussetzung für ein nachhaltiges System. Der DI Container kann meiner Erfahrung nach zum Katalysator für gutes OO Design werden und macht sich damit zum Pflichtbestandteil. Gutes Design kommt schließlich nie zum Null-Tarif. Der Aufwand, entkoppelte Module zu komponieren sinkt aber deutlich beim Einsatz eines DI Containers. Mit dem sinkenden Aufwand erhöht sich das Kosten-Nutzen-Verhältnis und damit meine Bereitschaft, diesen Aufwand zu treiben. Der DI Container ist also kein Selbstzweck, sondern Mittel zum Zweck. Der Theorie nach ist er optional, in der Praxis nicht!

Dependency Inversion – ganz neu entdeckt

Veröffentlicht: 7. Juli 2011 von pmischke in Dependency Injection

So, jetzt geht’s los! Dies ist der erste Post im Themenfeld „Dependency Injection“. Ich hoffe, es gelingt mir, dieses hoch interessante Thema weiter zu verfolgen.

Am Anfang steht das „Dependency Inversion Principle“ (DIP) von Robert Martin. Genauer gesagt war es 1995, was aus meiner beruflichen Perspektive schon sehr weit am Anfang ist. Trotzdem sieht es nicht so aus, als ob dieses Prinzip in der Software-Entwicklung allgemein verinnerlicht ist. Mir geht es nicht anders. Lange Zeit hatte ich nur ein sehr oberflächliches Verständnis des DIP. Seit kurzem bin ich einen Schritt weiter.

Aber was ist das DIP eigentlich? Es gibt viel Material dazu im Netz. Eine umfangreiche und verständliche Erklärung findet Ihr zum Beispiel hier:

Wir lernen also,

A. High-level modules should not depend upon low-level modules. Both should depend upon abstractions.

Ich finde es ganz interessant, sich zunächst einmal zu vergegenwärtigen, warum wir diese Abhängigkeiten von oben nach unten überhaupt haben. Martin weist im Original Artikel darauf hin. Die traditionelle strukturierte Programmierung lehrt uns, große Probleme in mehrere kleine Probleme aufzuteilen und dann die große Lösung aus den kleinen Lösungen zusammen zu setzen. So ergeben sich die Hierarchien, in denen das oberste Modul letzendlich von allen anderen abhängt. Jede Änderung wirkt sich die Hierarchie hoch durch das ganze System. Eigentlich ziemlich unschön, aber dieses Prinzip sitzt sehr fest in unseren Köpfen. Die objektorientierte Programmierung hat bisher nur wenig daran geändert.

Obwohl der objektorientierte Werkzeugkasten mehr zu bieten hat. Das DIP zeigt, dass die Abhängigkeiten von oben nach unten durch Abstraktionen umgekehrt werden können. Für die Abstraktionen nehmen wir Schnittstellen, also Interfaces in Java und C#. Weiter heißt es im DIP

B. Abstractions should not depend upon details. Details should depend upon abstractions.

Was bedeute das? Dieser seltsam wirkende zweite Teil hat etwas mit C++ zu tun. Ja genau, um 1995 hab auch ich mit C++ entwickelt, aber das ist Geschichte. Mit den Interfaces aus Java kann ich meine Module perfekt entkoppeln, ohne mir darüber Gedanken zu machen.

Mit diesem Erkenntnisstand bin ich lange gut gefahren. Zumindest dachte ich das. Bei dem Versuch, meine Kollegen vom DIP und dem guten Design zu überzeugen, welches sich damit erreichen lässt, kam dann folgender Einwand. Die Schnittstelle macht nur mehr Arbeit. Änderungen fanden vorher nur in den beiden Module statt. Und nun muss ich immer drei Stellen ändern. Die Schnittstelle, das implementierende Modul und das nutzende Modul. Ja das stimmt zwar… aber nur wenn sich die Schnittstelle ändert. Ich kann also leicht meine Bugs in der Implementierung fixen. Bei wirklichen Änderungen habe ich zugegebenermaßen nicht viel gewonnen. Tja, dagegen kann man nichts machen.

Dumm nur, das unser Geschäft quasi ausschließlich aus Änderungen besteht. Und eigentlich kann ich Bugs auch gut ohne separierte Schnittstelle in dem Modul fixen, wenn die Signaturen der Methoden nicht betroffen sind. Was bringt mir das DIP also?

Der Schlüssel liegt in dem zweiten Teil des DIP, den ich so schnell beiseite gelegt hatte. Abstraktionen dürfen nicht von Details abhängen! Anders ausgedrückt, die Schnittstelle muss überhaupt eine Abstraktion sein. In der IDE schnell mal den „Extract Interface“ Wizzard starten, erzeugt zwar eine Schnittstelle, aber keine Abstraktion. Diese Schnittstellen unterscheiden sich nicht einen Deut von den öffentlichen Methoden eines Moduls, das ohne Schnittstelle genutzt wird. Es stellt sich genau der Effekt ein, dass alle Änderungen trotz Schnittstelle nach oben durchschlagen.

Ich möchte einfaches Beispiel geben.

public Customer findCustomerById(long id);

Diese Abstraktion hängt von Details ab. Nämlich von dem Detail, dass ich den long Typ für meine IDs verwende. Bei der Umstellung auf UUID wird wahrscheinlich einiges an Code geändert werden.

public Customer findCustomerById(ID id);

Diese Abstraktion hängt nicht von Details ab. Wenn mein eigener Typ ID den Typ der Implementierung versteckt, ist das nutzende Modul von einer Umstellung entkoppelt. Wenn sich das Wesen der Methode nicht ändert, wird sich auch die Schnittstelle nicht ändern. Eine Methode, die einen Kunden zu einer gegebenen ID findet, wird immer eine ID als Parameter bekommen und einen Kunden zurück liefern. Egal, wie die Implementierung aussieht.

Es ist nicht einfach, die echte Abstraktion zu finden, die hinter einer Schnittstelle liegt. Die IDE kann das nicht leisten. Hier ist das Gehirn gefragt. Und nicht zu knapp. Aber es lohnt sich. Die echte Abstraktion bringt Stabilität in die Schnittstelle. Häufige Änderungen an der Schnittstelle sind ein Hinweis auf mangelnde Abstraktion. Der zweite Teil des DIP ist für mich inzwischen wichtiger geworden, als der erste. Ohne Abstraktion funktioniert die Schnittstelle nicht.

Wir lösen am liebsten die schwierigen Probleme

Veröffentlicht: 21. Dezember 2010 von pmischke in Uncategorized

Vor einiger Zeit las ich einen Artikel von Oren Eini, in dem er eine Architektur mit rich client und server back end vorstellt (siehe unten). In diesem Artikel findet sich folgendes Zitat:

The actual code is pretty boring. […] However, this is more important than you might think. An architect’s job, I’d argue, is to make sure that the developers in the project are as bored as possible. Most business problems are boring, and by removing technological complexities from the system, you get a much higher percentage of developer time spent working on boring business problems instead of interesting technological problems.

Hier sehe ich einen gewissen Interessenkonflikt. Zusammenfassend könnte man demnach auch sagen, dass gute Architektur zu langweiligem Code führt. Es hat aber niemand großen Spaß daran, langweiligen Code zu schreiben.

Wird die Rolle des Architekten nicht von einem Entwickler eingenommen, wäre dieser bei den Entwicklern bestimmt schnell „unten durch“. Wer lässt sich schon gerne die langweiligen Aufgaben zuschieben? Es ist fraglich, wie lange der Architekt seine Autorität aufrecht erhalten kann, bevor die Entwickler eigene Architekturen aufziehen.

Wird die Architektur hingegen von den Entwicklern selbst gemacht, ergibt sich der angesprochene Interessenkonflikt. Warum sollte ich dafür sorgen, dass meine Arbeit langweilig wird?

Ich glaube, die Lösung des Konflikts liegt im Wertesystem. Einfachheit wird in der Softwareentwicklung vielfach mit langweilig gleich gesetzt. Hohe Komplexität ist interessant, spannend und herausfordernd. Hohe Komplexität zu beherrschen,  ist unser Ding. Vielleicht sollten wir uns aber auf die positiven Aspekte von Einfachheit besinnen. Einfachheit ist verständlich, ästhetisch, genial. Weitere Plädoyers für Einfachheit finden sich bei Wikipedia und Wikiquote.