Inversion of Control Container …

Inversion of Control Container …

Inversion of Control Container ...

Inversion of Control Container und Dependency Injection-Muster

In der Java-Community gibt es eine Ansturm von Lightweight-Container gegeben, die Komponenten aus verschiedenen Projekten zu einer zusammenhängenden Anwendung zu montieren helfen. diese Behälter Basiswert ist ein gemeinsames Muster, wie sie führen die Verkabelung, ein Konzept, das sie unter dem sehr Gattungsnamen beziehen von "Inversion of Control". In diesem Artikel werde ich graben, wie sich dieses Muster arbeitet, unter dem spezifischeren Namen "Abhängigkeitsspritze"Und der Kontrast mit dem alternativen Service Locator. Die Wahl zwischen ihnen ist weniger wichtig als das Prinzip der Konfiguration aus der Benutzung trennen.

23. Januar 2004

Inhalt

Einer der unterhaltsame Dinge über die Enterprise-Java-Welt ist die riesige Menge an Aktivität in Gebäude Alternativen zu den Mainstream-J2EE-Technologien, ein Großteil davon in Open-Source geschieht. Eine Menge davon ist eine Reaktion auf die Schwergewichts-Komplexität in den Mainstream-J2EE-Welt, aber viel davon ist auch Alternativen und kommen mit kreativen Ideen zu erkunden. Ein häufiges Problem zu behandeln ist, wie sie zusammen verschiedene Elemente zu verbinden: wie passen zusammen Sie diese Web-Controller-Architektur mit dem Datenbank-Schnittstelle Unterstützung, wenn sie von verschiedenen Teams mit wenig Wissen voneinander gebaut wurden. Eine Reihe von Rahmenbedingungen haben einen Stich an diesem Problem genommen, und einige sind Verzweigung eine allgemeine Fähigkeit zu liefern Komponenten aus verschiedenen Schichten zusammenzustellen. Diese werden oft als Lightweight-Container bezeichnet, Beispiele sind PicoContainer. und Frühling.

Basiswert diese Behälter eine Reihe von interessanten Design-Prinzipien sind die Dinge, die über beide diese speziellen Container und in der Tat die Java-Plattform zu gehen. Hier möchte ich zu erkunden einige dieser Prinzipien zu beginnen. Die Beispiele, die ich verwenden, sind in Java, aber wie die meisten meiner Schreiben die Prinzipien sind gleichermaßen auf andere OO-Umgebungen, insbesondere .NET.

Komponenten und Services

Das Thema der Verdrahtungselemente schleppt mich zusammen fast sofort in die knorrigen Terminologie Probleme, die die Begriffe Service und Komponente umgeben. Sie finden lange und widersprüchliche Artikel über die Definition dieser Dinge mit Leichtigkeit. Für meine Zwecke hier sind meine aktuellen Verwendungen dieser überlasteten Bedingungen.

Ich benutze Komponente eine glob von Software verstanden, die verwendet werden, ist beabsichtigt, ohne Veränderung, die von einer Anwendung, die außerhalb der Kontrolle der Autoren der Komponente ist. Mit «ohne Änderung» meine ich, dass die Verwendung von Anwendung nicht den Quellcode der Komponenten ändert sich, obwohl sie es in einer Weise, die Komponentenverhalten ändern kann durch die Komponentenentwickler erlaubt durch die Erweiterung.

Ein Dienst ist ähnlich einer Komponente, dass sie von ausländischen Anwendungen verwendet wird. Der wesentliche Unterschied ist, dass ich eine Komponente erwarten lokal zu werden (man denke JAR-Datei, die Montage, dll, oder eine Quelle Import). Ein Dienst wird remote über einige Remote-Schnittstelle verwendet werden, entweder synchron oder asynchron (zB Web-Service, Messaging-System, RPC, oder Buchse.)

Ich meistens Service in diesem Artikel verwenden, aber viel von der gleichen Logik kann auch auf lokale Komponenten angewendet werden. Tatsächlich oft müssen Sie irgendeine Art von lokalen Komponenten-Framework auf einfache Weise einen Remote-Service zugreifen. Aber Schreiben "Komponente oder eine Dienstleistung" zu lesen und zu schreiben ist anstrengend, und Dienstleistungen sind viel mehr in Mode im Moment.

Ein Naive Beispiel

Um all dies zu konkretisieren werde ich einen laufenden Beispiel nutzen, um über all das zu reden. Wie alle meine Beispiele ist es eines dieser super-einfache Beispiele; klein genug, unwirklich, aber hoffentlich genug, um für Sie zu visualisieren, was, ohne in den Sumpf eines realen Beispiel vor sich geht.

In diesem Beispiel schreibe ich eine Komponente, die eine Liste von Filmen von einem bestimmten Regisseur gerichtet zur Verfügung stellt. Diese erstaunlich nützliche Funktion wird durch eine einzelne Methode implementiert.

Die Implementierung dieser Funktion in der extremen naiv ist, es fragt ein Finder-Objekt (die wir in einem Moment bekommen) jeden Film zurückkehren es kennt. Dann jagt er gerade durch diese Liste diejenigen, die von einem bestimmten Regisseur gerichtet zurückzukehren. Dieses besondere Stück Naivität Ich werde zu beheben nicht, da es nur das Gerüst für den realen Punkt dieses Artikels ist.

Der eigentliche Punkt dieses Artikels ist das Finder-Objekt, oder vor allem, wie wir das lister Objekt mit einem bestimmten Finder Objekt verbinden. Der Grund, warum dies interessant ist, ist, dass ich meine wunderbare moviesDirectedBy Methode wollen vollständig zu sein, unabhängig davon, wie alle Filme gespeichert werden. Also das ganze Verfahren nicht auf einen Sucher beziehen, und alles, was Finder tut, ist weiß, wie man die findAll Methode zu reagieren. Ich kann dies für den Finder durch die Definition einer Schnittstelle bringen.

Nun ist sehr all dies gut entkoppelt, aber irgendwann habe ich mit einer konkreten Klasse zu kommen, um tatsächlich mit den Filmen kommen. In diesem Fall habe ich den Code für diese im Konstruktor meiner lister Klasse.

Der Name der Implementierungsklasse kommt von der Tatsache, dass ich meine Liste von einem Doppelpunkt getrennte Textdatei zu bekommen. Ich werde Ihnen die Einzelheiten ersparen, doch der Punkt, nur ist, dass es einige Implementierung ist.

Nun, wenn ich für mich nur diese Klasse bin mit, das ist alles schön und gut. Aber was passiert, wenn meine Freunde von dem Wunsch, für diese wunderbare Funktionalität sind überwältigt und möchte eine Kopie meines Programms gefallen hat? Wenn sie auch speichern ihre Kinoprogramm in einem Doppelpunkt getrennte Textdatei mit dem Namen "movies1.txt" dann ist alles wunderbar. Wenn sie einen anderen Namen für ihre Filme Datei haben, dann ist es einfach, den Namen der Datei in einer Properties-Datei zu setzen. Aber was, wenn sie haben eine ganz andere Form ihre Kinofilmbewertungs- der Speicherung: eine SQL-Datenbank, eine XML-Datei, einen Web-Service, oder einfach nur ein anderes Format der Textdatei? In diesem Fall müssen wir eine andere Klasse, die Daten zu greifen. Nun, weil ich eine MovieFinder Schnittstelle definiert haben, wird dies nicht mein moviesDirectedBy Methode ändern. Aber ich brauche noch einen weiten Weg zu haben, um eine Instanz der rechten Finder Implementierung in Platz zu bekommen.

Abbildung 1: Die Abhängigkeiten eine einfache Erstellung in der lister-Klasse

Abbildung 1 zeigt die Abhängigkeiten für diese Situation. Die MovieLister Klasse ist abhängig von sowohl der MovieFinder Schnittstelle und von der Implementierung. Wir würden es vorziehen, wenn es nur abhängig von der Schnittstelle waren, aber dann, wie machen wir eine Instanz mit zu arbeiten?

In meinem Buch P der EAA. wir beschrieben, diese Situation als Plugin. Die Implementierungsklasse für den Finder nicht in das Programm bei der Kompilierung verbunden, da ich nicht weiß, was meine Freunde benutzen werden. Stattdessen wollen wir meine Lister mit jeder Implementierung zu arbeiten, und für die Umsetzung zu einem späteren Zeitpunkt eingesteckt zu werden, aus meinen Händen. Das Problem ist, wie kann ich diesen Link, so dass meine lister Klasse unwissend der Implementierungsklasse, kann aber immer noch auf eine Instanz sprechen seine Arbeit zu tun.

Der Ausbau dieses in einem realen System, könnten wir Dutzende solcher Dienste und Komponenten haben. In jedem Fall können wir abstrakt unsere Verwendung dieser Komponenten durch über eine Schnittstelle mit ihnen zu sprechen (und über einen Adapter, wenn die Komponente nicht mit einer Schnittstelle konzipiert ist). Aber wenn wir dieses System auf unterschiedliche Weise bereitstellen wollen, müssen wir Plugins verwenden, um die Interaktion mit diesen Diensten zu handhaben, so dass wir verschiedene Implementierungen in verschiedenen Implementierungen verwenden.

So das Kernproblem ist, wie montieren wir diese Plugins in eine Anwendung? Dies ist eines der Hauptprobleme, die diese neue Generation von Lightweight-Container stehen, und universell sie alle tun es Inversion of Control verwenden.

Inversion of Control

Wenn diese Behälter sprechen darüber, wie sie sind so nützlich, weil sie implementieren "Inversion of Control" Ich am Ende sehr verwirrt. Inversion of Control ist ein gemeinsames Merkmal von Frameworks, so zu sagen, dass diese leichten Behälter sind speziell, weil sie Inversion of Control verwenden ist wie gesagt mein Auto Besonderes ist, weil es Räder hat.

Die Frage ist: "welcher Aspekt Steuerung invertiert sie?" Als ich lief in Umkehrung der Steuer Zuerst war es in der Hauptsteuerung einer Benutzeroberfläche. Frühe Benutzerschnittstellen wurden durch das Anwendungsprogramm gesteuert. Sie würden eine Reihe von Befehlen haben wie "Name eingeben". "Adresse eingeben"; Ihr Programm würde die Eingabeaufforderungen fahren und eine Antwort zu jeder abholen. Mit grafischen (oder sogar Bildschirm basiert) UIS würde die UI-Framework diese Hauptschleife enthalten und Ihr Programm statt Event-Handler für die verschiedenen Felder auf dem Bildschirm zur Verfügung gestellt. Die Hauptsteuer des Programms wurde umgedreht, zog in die Rahmen von Ihnen entfernt.

Für diese neue Generation von Containern ist die Umkehrung, wie sie Lookup eine Plugin-Implementierung. In meinem naiven Beispiel sah der lister den Finder Implementierung, indem es direkt instanziieren. Dieser stoppt den Finder von ein Plugin zu sein. Der Ansatz, den diese Behälter verwenden ist, um sicherzustellen, dass jeder Benutzer eines Plugins einige Konvention folgt, dass ein separater Assembler-Modul ermöglicht die Implementierung in die lister zu injizieren.

Als Ergebnis glaube ich, die wir für dieses Muster einen bestimmten Namen benötigen. Inversion of Control ist zu allgemein ein Begriff, und damit Menschen finden es verwirrend. Als Ergebnis mit viel Diskussion mit verschiedenen IoC Befürworter haben wir uns auf den Namen Abhängigkeitsspritze .

Ich werde, indem er über die verschiedenen Formen der Abhängigkeit Injektion zu starten, aber ich werde jetzt darauf hin, dass das ist nicht der einzige Weg, um die Abhängigkeit von der Anwendungsklasse an das Plugin Umsetzung zu entfernen. Das andere Muster können Sie tun, um diesen Service Locator ist, und das werde ich erörtern, nachdem ich mit dem Erklären Dependency Injection fertig bin.

Formen der Dependency Injection

Die Grundidee der Dependency Injection ist ein eigenes Objekt zu haben, Assembler, die für den Finder-Schnittstelle, was zu einem Abhängigkeitsdiagramm entlang der Linien von Figur 2 ein Feld in der lister Klasse mit einer entsprechenden Umsetzung auffüllt

Abbildung 2: Die Abhängigkeiten für eine Dependency Injector

Es gibt drei Hauptstile von Dependency Injection. Die Namen, die ich für sie bin mit sind Constructor Injection, Setter Injection und die Interface-Injection. Wenn Sie über diese Dinge in den aktuellen Diskussionen um Inversion of Control gelesen werden Sie diese bezeichnet als Typ-1-IoC (Interface-Injektion), Typ-2-IoC (Setter-Injektion) und Typ 3 IoC (Konstruktor Injektion) zu hören. Ich finde numerischen Namen eher schwer zu merken, weshalb ich die Namen habe ich hier benutzt habe.

Constructor Injection mit PicoContainer

Ich werde mit zeigen beginnen, wie diese Injektion durchgeführt wird einen leichten Behälter namens PicoContainer. Ich fange hier in erster Linie, weil einige meiner Kollegen bei Thoughtworks sind sehr aktiv in der Entwicklung von PicoContainer (ja, es ist eine Art Corporate Nepotismus.)

PicoContainer verwendet einen Konstruktor, um zu entscheiden, wie eine Finder Implementierung in die lister Klasse zu injizieren. Damit dies funktioniert, muss der Film lister Klasse einen Konstruktor zu deklarieren, die alles enthält es injiziert werden muss.

Der Sucher selbst wird auch durch den pico Behälter, und als solche werden die Dateinamen der Textdatei in sie durch den Behälter injiziert verwaltet werden.

Der Pico-Behälter muss dann gesagt werden, die Implementierungsklasse mit jeder Schnittstelle zugeordnet werden soll, und dem String in den Finder zu injizieren.

Diese Konfigurationscode wird in der Regel in einer anderen Klasse eingerichtet. Für unser Beispiel jeder Freund, der mein lister verwendet könnte die entsprechende Konfigurationscode in einigen Setup-Klasse für sich schreiben. Natürlich ist es üblich, diese Art von Konfigurationsinformationen in separaten Konfigurationsdateien zu halten. Sie können eine Klasse schreiben eine Config-Datei zu lesen und den Behälter entsprechend einrichten. Obwohl PicoContainer selbst nicht über diese Funktionalität enthalten, ist es ein eng verwandtes Projekt Nanocontainer genannt, die die entsprechenden Wrapper bietet Ihnen zu erlauben, XML-Konfigurationsdateien haben. Solch ein Nano-Container die XML analysieren und dann eine darunter liegende pico Container konfigurieren. Die Philosophie des Projekts ist die Config-Datei-Format von der zugrunde liegenden Mechanismus zu trennen.

Um den Behälter zu verwenden, schreiben Sie Code so etwas wie dieses.

Obwohl in diesem Beispiel I verwendeten Konstruktor Injektion haben, unterstützt PicoContainer auch Setter Injektion, obwohl seine Entwickler Konstruktor Injektion bevorzugen tun.

Setter Injection mit Feder

Das Spring-Framework ist eine weitreichende Rahmen für Enterprise-Java-Entwicklung. Es umfasst Abstraktionsschichten für Transaktionen, Persistenz-Frameworks, die Entwicklung von Webanwendungen und JDBC. Wie PicoContainer unterstützt es sowohl Konstruktor und Setter-Injektion, aber seine Entwickler neigen Setter Injektion zu bevorzugen — die es eine geeignete Wahl für dieses Beispiel macht.

Zu meinem Film lister bekommen, um die Injektion akzeptieren ich eine Einstellungsmethode für diesen Dienst definieren

Ebenso definiere ich einen Setter für den Dateinamen.

Der dritte Schritt ist die Konfiguration für die Dateien einzurichten. Frühling unterstützt die Konfiguration über XML-Dateien und auch durch Code, aber XML ist die erwartete Art und Weise, es zu tun.

Der Test sieht dann folgendermaßen aus.

Interface-Injection

Die dritte Injektionstechnik ist zu definieren und verwenden Schnittstellen für die Injektion. Avalon ist ein Beispiel für einen Rahmen, der diese Technik an Orten verwendet. Ich werde ein bisschen mehr darüber reden später, aber in diesem Fall werde ich es mit einigen einfachen Beispielcode zu verwenden.

Mit dieser Technik beginne ich durch eine Schnittstelle definiert, die ich verwenden werde die Injektion durchzuführen. Hier ist die Schnittstelle für einen Film-Finder in ein Objekt injiziert wird.

Diese Schnittstelle würde durch definiert werden, wer bietet die MovieFinder Schnittstelle. Es muss von jeder Klasse implementiert werden, die einen Sucher, wie die lister verwenden möchte.

Klasse MovieLister implementiert InjectFinder

Ich verwende einen ähnlichen Ansatz, um die Dateinamen in den Finder Implementierung zu injizieren.

Klasse ColonMovieFinder implementiert MovieFinder, InjectFinderFilename.

Dann wird, wie üblich, brauche ich einige Konfigurationscode die Implementierungen zu verkabeln. Der Einfachheit halber werde ich es im Code tun.

Diese Konfiguration hat zwei Stufen, Komponenten durch Lookup-Schlüssel Registrierung zu den anderen Beispielen ziemlich ähnlich ist.

Ein neuer Schritt ist, um die Injektoren zu registrieren, die die abhängigen Komponenten injizieren. Jede Injektion Schnittstelle benötigt einige Code, um das abhängige Objekt zu injizieren. Hier kann ich dies durch Injektor-Objekte mit dem Behälter zu registrieren. Jeder Injektor Objekt implementiert die Injektor-Schnittstelle.

Wenn die abhängige eine Klasse für diesen Container geschrieben ist, macht es Sinn für die Komponente des Injektors Schnittstelle selbst zu implementieren, da ich hier mit dem Film-Finder tun. Für generische Klassen, wie zum Beispiel die Zeichenfolge, verwende ich eine innere Klasse innerhalb der Konfigurationscode.

Klasse ColonMovieFinder implementiert Injektor.

Die Tests dann den Container verwenden.

Der Behälter verwendet die deklarierten Injektion Schnittstellen, um die Abhängigkeiten, um herauszufinden, und die Injektoren die richtigen Abhängigen zu injizieren. (Die spezifische Container Implementierung ich hier tat, ist nicht wichtig, die Technik, und ich werde es nicht zeigen, weil man nur lachen würde.)

Mit einem Service Locator

Der entscheidende Vorteil eines Dependency Injector ist, dass es die Abhängigkeit, die der MovieLister Klasse hat auf die Beton MovieFinder Umsetzung entfernt. Dies ermöglicht es mir Listers zu Freunden zu geben und für sie in einer geeigneten Implementierung für ihre eigene Umwelt zu stopfen. Injektion ist nicht der einzige Weg, diese Abhängigkeit zu brechen, eine andere ist es, einen Service Locator zu verwenden.

Die Grundidee hinter einem Service-Locator ist ein Objekt zu haben, der weiß, wie man halten, alle Dienste zu erhalten, die eine Anwendung benötigen. So ein Service-Locator für diese Anwendung wäre eine Methode, die einen Film Finder zurückgibt, wenn man gebraucht wird. Natürlich verschiebt sich dies nur die Last ein bisschen, wir immer noch den Locator in die lister bekommen müssen, in den Abhängigkeiten der Abbildung erhaltenen 3

Abbildung 3: Die Abhängigkeiten für einen Service Locator

In diesem Fall werde ich den Servicelocator als Singleton Registry verwenden. Die lister kann dann verwenden Sie den Finder zu bekommen, wenn es instanziiert wird.

Wie bei der Injektion Ansatz, müssen wir den Service Locator konfigurieren. Hier habe ich es zu tun in Code bin, aber es ist nicht schwer, einen Mechanismus zu verwenden, der die entsprechenden Daten aus einer Konfigurationsdatei lesen würde.

Hier ist der Testcode.

Ich habe oft die Beschwerde gehört, dass diese Art von Service Locators eine schlechte Sache sind, weil sie nicht prüfbar sind, weil Sie nicht Implementierungen für sie ersetzen können. Sicherlich können Sie sie entwerfen schlecht in diese Art von Schwierigkeiten zu bekommen, aber Sie müssen nicht. In diesem Fall ist der Service Locator Instanz nur eine einfache Dateninhaber. Ich kann leicht den Locator mit Testimplementierungen meiner Dienste erstellen.

Für eine anspruchsvollere Locator kann ich Service-Locator Unterklasse und Unterklasse, dass die in die Klassenvariable der Registrierung übergeben. Ich kann die statischen Methoden ändern vielmehr eine Methode auf die Instanz zu rufen als direkt Instanzvariablen zugreifen. Ich kann Faden liefern&# X2013; spezifische Locators durch Faden&# X2013; spezifische Lagerung. All dies kann ohne Änderung Kunden von Service-Locator erfolgen.

Eine Möglichkeit, dies zu denken ist, dass Service-Locator ein Registry kein Singleton ist. Ein Singleton bietet eine einfache Möglichkeit, eine Registrierung der Umsetzung, sondern, dass die Umsetzung Entscheidung wird leicht geändert.

Mit Hilfe einer Segregated Schnittstelle für den Locator

Eines der Probleme mit dem einfachen Ansatz oben ist, dass die MovieLister auf der Full-Service-Locator-Klasse abhängig ist, auch wenn es nur ein Dienst verwendet. Wir können dies verringern, indem eine Rolle Schnittstelle. Auf diese Weise, anstatt die Full-Service-Locator-Schnittstelle kann die lister nur die Bit-Interface deklarieren es braucht.

In dieser Situation würde der Anbieter der lister auch eine Locator-Schnittstelle bereitzustellen, die es halten, den Sucher zu bekommen muss.

Der Locator muss dann diese Schnittstelle zu implementieren, Zugriff auf einen Sucher zur Verfügung zu stellen.

Sie werden bemerken, dass da wir eine Schnittstelle verwenden möchten, können wir nicht nur auf die Dienste zugreifen, durch statische Methoden nicht mehr. Wir haben die Klasse zu verwenden, um eine Locator Instanz zu erhalten und dann verwenden, zu bekommen, was wir brauchen.

Ein dynamisches Service Locator

Das obige Beispiel war statisch, dass in der Service-Locator Klassenmethoden für jeden der Dienste, die Sie benötigen. Dies ist nicht der einzige Weg, es zu tun, können Sie auch einen dynamischen Service-Locator, die Sie jeden Service, den Sie in sie brauchen zu verstauen können und Ihre Entscheidungen zur Laufzeit machen.

In diesem Fall verwendet der Service Locator eine Karte anstelle von Feldern für jeden der Dienste und bietet generische Methoden Dienstleistungen zu erhalten und Last.

Konfigurieren beinhaltet einen Dienst mit einem entsprechenden Schlüssel zu laden.

Ich benutze den Service durch den gleichen Schlüssel Zeichenkette verwenden.

Im Großen und Ganzen mag ich diesen Ansatz. Obwohl es sicherlich flexibel ist, ist es nicht sehr explizit. Die einzige Art, wie ich herausfinden kann, wie man einen Service zu erreichen, ist durch Textschlüssel. Ich ziehe es explizite Methoden, weil es einfacher ist, zu finden, wo sie durch einen Blick auf die Schnittstellendefinitionen sind.

Mit beiden einen Lokator und Injektion mit Avalon

Dependency Injection und ein Service-Locator sind nicht unbedingt gegenseitig ausschließende Konzepte. Ein gutes Beispiel für beide zusammen mit der Avalon-Framework. Avalon verwendet einen Service Locator, sondern nutzt Einspritzkomponenten zu sagen, wo die Locator zu finden.

Berin Loritsch schickte mir diese einfache Version meiner laufenden Beispiel Avalon verwenden.

Die Service-Methode ist ein Beispiel für Interface-Injektion, so dass der Behälter einen Service-Manager in MyMovieLister zu injizieren. Der Service-Manager ist ein Beispiel für einen Service Locator. In diesem Beispiel wird die lister speichert nicht die Manager in einem Feld, anstatt sofort verwendet es den Finder zum Nachschlagen, die es Speicher tut.

welche Option Entscheidung zu verwenden,

Bisher habe ich konzentrierte sich auf zu erklären, wie ich sehe, diese Muster und deren Variationen. Jetzt kann ich sprechen über ihre Vor-und Nachteile zu helfen beginnen, welche herauszufinden, zu verwenden und wann.

Service Locator vs Dependency Injection

Die grundsätzliche Wahl zwischen Service Locator und Dependency Injection. Der erste Punkt ist, dass beide Implementierungen eine weitgehende Entkoppelung schaffen, die in der naiven Beispiel fehlt — in beiden Fällen Code Anwendung ist unabhängig von der konkreten Umsetzung der Service-Schnittstelle. Der wesentliche Unterschied zwischen den beiden Mustern geht darum, wie die Umsetzung der Anwendungsklasse vorgesehen ist. Mit Service-Locator fragt der Anwendungsklasse für sie explizit durch eine Mitteilung an den Locator. Mit Injektion gibt es keine explizite Anforderung, wird der Dienst in der Anwendungsklasse — also die Umkehrung der Kontrolle.

Inversion of Control ist ein gemeinsames Merkmal von Frameworks, aber es ist etwas, das zu einem Preis kommt. Es neigt dazu, schwer zu verstehen und führt zu Problemen, wenn Sie versuchen, zu debuggen. Also im Großen und Ganzen ziehe ich es zu vermeiden, es sei denn ich es brauche. Dies ist nicht zu sagen, es ist eine schlechte Sache, nur, dass ich denke, dass es sich über die einfachere Alternative zu rechtfertigen muss.

Der wesentliche Unterschied besteht darin, dass mit einem Service Locator jeder Nutzer eines Dienstes eine Abhängigkeit zu den Locator hat. Der Locator kann Abhängigkeiten zu anderen Implementierungen verstecken, aber Sie müssen den Locator zu sehen. So dass die Entscheidung zwischen Locator und Injektor abhängig davon, ob die Abhängigkeit ist ein Problem.

kann Dependency Injection Mit Hilfe es leichter zu sehen, was der Komponentenabhängigkeiten sind. Mit Abhängigkeit Injektor sehen Sie können einfach an der Injektionsmechanismus, wie zum Beispiel den Konstruktor und die Abhängigkeiten zu sehen. Mit dem Service Locator haben Sie den Quellcode für Anrufe an den Locator zu suchen. Moderne IDEs mit Referenzen suchen bieten diese einfacher zu machen, aber es ist immer noch nicht so einfach wie bei den Konstruktor oder Einstellungsmethoden suchen.

Eine Menge davon hängt von der Art des Benutzers des Dienstes. Wenn Sie eine Anwendung mit verschiedenen Klassen bauen, die einen Dienst nutzen, dann eine Abhängigkeit von den Anwendungsklassen zu den Locator ist keine große Sache. In meinem Beispiel einen Film Lister zu meinen Freunden zu geben, dann einen Service-Locator funktioniert recht gut. Alles, was sie tun müssen, um den Locator zu konfigurieren, um in den richtigen Service-Implementierungen, entweder durch einige Konfigurationscode oder über eine Konfigurationsdatei zu haken. In einem solchen Szenario sehe ich nicht die Umkehrung des Injektors so zwingend etwas bieten.

Der Unterschied kommt, wenn die lister eine Komponente, die ich auf eine Anwendung bin vorausgesetzt, dass andere Leute zu schreiben. In diesem Fall weiß ich nicht viel über die APIs der Service-Locators, die meine Kunden verwenden werden. Jeder Kunde kann seinen eigenen unvereinbar Service Locators haben. Ich kann mit Hilfe der getrennten Schnittstelle einige dieser umgehen. Jeder Kunde kann einen Adapter schreiben, die meine Schnittstelle zu ihren Locator übereinstimmt, in jedem Fall aber ich muss noch die erste Locator zu sehen meine spezifische Schnittstelle zum Nachschlagen. Und sobald der Adapter erscheint dann die Einfachheit der direkten Verbindung zu einem Locator beginnt zu rutschen.

Da mit einem Injektor Sie keine Abhängigkeit von einer Komponente zu dem Injektor aufweisen, kann die Komponente erhalten keine weiteren Leistungen aus dem Injektor einmal konfiguriert worden ist.

Ein häufiger Grund Menschen geben für die Bevorzugung Dependency Injection ist, dass es Tests erleichtert. Der Punkt hier ist, dass die Prüfung zu tun, müssen Sie einfach echte Service-Implementierungen mit Stubs oder Mocks ersetzen. Allerdings gibt es wirklich keinen Unterschied zwischen Dependency Injection und Service Locator: beide sind sehr zugänglich Anstoßen. Ich vermute, dass diese Beobachtung von Projekten kommt, wo die Menschen nicht die Mühe machen, um sicherzustellen, dass ihre Service-Locator kann leicht ausgewechselt werden. Dies ist, wo ständige Prüfung hilft, wenn Sie nicht so leicht Services für das Testen Stummel kann, dann ist dies ein ernsthaftes Problem mit Ihrem Design impliziert.

Natürlich wird das Testproblem durch die Komponente Umgebungen verschärft, die sehr aufdringlich sind, wie zum Beispiel Java EJB-Framework. Meine Ansicht ist, dass diese Art von Rahmen ihrer Auswirkungen auf den Anwendungscode zu minimieren sollte und vor allem die Dinge nicht tun sollten, die die edit-Ausführungszyklus verlangsamen. Schwergewichts-Komponenten zu ersetzen Plugins tut viel, diesen Prozess zu helfen, die für Praktiken wie Test Driven Development von entscheidender Bedeutung ist.

So ist die primäre Frage ist für Menschen, die Code schreiben, die in Anwendungen, die außerhalb der Kontrolle des Autors verwendet werden erwartet. In diesen Fällen ist auch nur eine minimale Annahme über einen Service Locator ist ein Problem.

Constructor gegen Setter Injection

Für Service-Kombination, haben Sie immer eine Konvention zu haben, um Dinge zusammen zu verbinden. Der Vorteil der Injektion ist in erster Linie, dass es sehr einfach Konventionen erfordert — zumindest für den Konstruktor und Setter-Injektionen. Sie müssen nichts seltsam in Ihrer Komponente zu tun und es ist ziemlich einfach für einen Injektor alles konfiguriert zu werden.

Interface-Injektion ist mehr invasive, da Sie eine Vielzahl von Schnittstellen zu schreiben, um die Dinge alle aussortiert. Für eine kleine Gruppe von Schnittstellen durch den Behälter erforderlich sind, wie in Avalon Ansatz, das ist nicht so schlimm. Aber es ist eine Menge Arbeit für Komponenten und Abhängigkeiten der Montage, weshalb die aktuelle Ernte von Lightweight-Container mit Setter und Konstruktor Injektion gehen.

Die Wahl zwischen Setter und Konstruktor Injektion ist interessant, da es eine allgemeinere Problem mit der objektorientierten Programmierung spiegelt — sollten Sie Felder in einem Konstruktor oder mit Setter füllen.

Meine lange Lauf Standard mit Objekten ist so viel wie möglich, gültige Objekte im Bauzeit zu erstellen. Dieser Rat geht an Kent Beck Smalltalk Best Practice Patterns zurück: Constructor-Methode und Constructor Parameter Methode. Konstruktoren mit Parametern geben Sie eine klare Aussage, was es bedeutet, ein gültiges Objekt in einem offensichtlichen Ort zu schaffen. Wenn es mehr als einen Weg, es zu tun, erstellen Sie mehrere Konstrukteure, die die verschiedenen Kombinationen zeigen.

Ein weiterer Vorteil bei Konstruktorinitialisierung ist, dass Sie alle Felder deutlich zu verbergen können, die durch einfaches unveränderlich sind keine Setter bietet. Ich denke, das ist wichtig — wenn etwas nicht, dann das Fehlen eines Setter dies sehr gut kommuniziert ändern sollte. Wenn Sie Setter für die Initialisierung verwenden, so kann dies ein Schmerz geworden. (In der Tat in diesen Situationen, die ich lieber die übliche Einstellung Konvention zu vermeiden, würde ich eine Methode, wie initFoo bevorzugen. Zu betonen, dass es ist etwas, das man nur bei der Geburt tun sollten.)

Aber mit jeder Situation gibt es Ausnahmen. Wenn Sie eine Menge von Konstruktorparameter Dinge haben kann unordentlich aussehen, vor allem in Sprachen ohne Keyword-Parameter. Es stimmt, dass ein langer Konstruktor ist oft ein Zeichen eines über beschäftigt Objekt, das aufgeteilt werden sollte, aber es gibt Fälle, in denen das ist, was Sie brauchen.

Wenn Sie mehrere Möglichkeiten, ein gültiges Objekt zu konstruieren, kann es schwierig sein, dies durch Konstrukteure zu zeigen, da Konstrukteure nur von der Anzahl und Art der Parameter variieren. Dies ist, wenn Factory-Methoden ins Spiel kommen, werden diese mit einer Kombination von privaten Bauherren nutzen können und Setter ihre Arbeit zu implementieren. Das Problem mit den klassischen Factory-Methoden für Komponenten Montage ist, dass sie in der Regel als statische Methoden zu sehen, und Sie können nicht mit denen Schnittstellen haben. Sie können eine Factory-Klasse machen, aber dann das wird nur ein weiterer Service-Instanz. Eine Fabrik Service ist oft eine gute Taktik, aber Sie haben immer noch die Fabrik instanziiert mit einer der Techniken hier.

Konstrukteurs leiden auch, wenn Sie einfache Parameter wie Saiten. Mit Setter Injektion können Sie jede Setter einen Namen geben, um anzuzeigen, was die Zeichenfolge tun soll. Mit Konstrukteure setzen Sie einfach auf die Position, die folgen schwerer ist.

Wenn Sie mehrere Konstruktoren und Vererbung haben, dann kann es besonders umständlich zu bekommen. Um alles, was Sie zu initialisieren haben Konstrukteure zur Verfügung zu stellen, um jeden Oberklassenkonstruktors weiterleiten, während Sie auch eigene Argumente hinzufügen. Dies kann zu einer noch größeren Explosion der Konstrukteure führen.

Trotz der Nachteile ist meine Präferenz mit Konstruktor Injektion zu starten, aber bereit sein, zu Setter Injektion zu wechseln, sobald die Probleme, die ich oben Beginn skizziert habe ein Problem zu werden.

Dieses Problem wurde auf eine große Debatte zwischen den verschiedenen Teams geführt, die Abhängigkeit Injektoren als Teil ihrer Frameworks. Allerdings scheint es, dass die meisten Menschen, die diese Gerüste bauen haben erkannt, dass es wichtig ist, beide Mechanismen zu unterstützen, auch wenn es eine Präferenz für einen von ihnen.

-Code oder Konfigurationsdateien

Eine separate, aber oft conflated Frage ist, ob Konfigurationsdateien oder Code auf einem API zu verwenden, um Dienste zu verkabeln. Für die meisten Anwendungen, die an vielen Orten eingesetzt werden, eine separate Konfigurationsdatei am sinnvollsten in der Regel wahrscheinlich macht. Fast die ganze Zeit dafür wird eine XML-Datei sein, und dies ist sinnvoll. Allerdings gibt es Fälle, in denen es einfacher ist, Programmcode zu verwenden, um die Montage zu tun. Ein Fall, in dem Sie eine einfache Anwendung, die nicht viel Einsatz Variation bekam ist. In diesem Fall kann ein bisschen Code klarer sein als eine separate XML-Datei.

Ein Kontrast Fall ist, wo die Montage recht komplex ist, an denen bedingte Schritte. Sobald du mit Programmiersprache immer in der Nähe beginnen dann beginnt XML brechen und es ist besser, eine wirkliche Sprache zu verwenden, die alle die Syntax ein klares Programm zu schreiben. Sie erhalten dann einen Builder-Klasse schreiben, die die Montage tut. Wenn Sie verschiedene Builder-Szenarien haben, können Sie mehrere Builder-Klassen bieten und eine einfache Konfigurationsdatei, zwischen ihnen zu wählen verwenden.

Ich denke oft, dass die Menschen übereifrige Konfigurationsdateien zu definieren. Oft ist eine Programmiersprache, macht eine einfache und leistungsfähige Konfigurationsmechanismus. Moderne Sprachen können kleine Monteuren leicht zusammenstellen, die verwendet werden können Plugins für größere Anlagen zu montieren. Wenn die Kompilierung ein Schmerz ist, dann gibt es Skriptsprachen, die gut auch arbeiten können.

Es wird oft gesagt, dass die Konfigurationsdateien sollten keine Programmiersprache verwenden, weil sie von Nicht-Programmierern bearbeitet werden müssen. Aber wie oft ist dies der Fall? Menschen wirklich Nicht-Programmierer Erwarten die Transaktionsisolationsstufen eines komplexen serverseitige Anwendung zu ändern? Nicht-Sprachkonfigurationsdateien funktionieren gut nur insoweit sie einfach sind. Wenn sie komplex geworden, dann ist es Zeit, über die Verwendung einer richtigen Programmiersprache zu denken.

Eine Sache, wir sind im Moment in der Java-Welt zu sehen, ist eine Kakophonie von Konfigurationsdateien, in dem jede Komponente ihre eigene Konfigurationsdateien hat, die unterschiedlich sind alle anderen. Wenn Sie ein Dutzend dieser Komponenten verwenden, können Sie einfach mit einem Dutzend Konfigurationsdateien am Ende zu synchron zu halten.

Mein Rat ist hier immer eine Möglichkeit zu bieten, die gesamte Konfiguration leicht mit einer programmatischen Schnittstelle zu tun, und dann eine separate Konfigurationsdatei als Option zu behandeln. Sie können ganz einfach Konfigurationsdatei Handhabung bauen die programmatische Schnittstelle zu verwenden. Wenn Sie eine Komponente schreiben Sie es dann zu Ihrem Benutzer überlassen, ob die programmatische Schnittstelle zu verwenden, Ihre Konfigurationsdateiformat, oder ihre eigenen Konfigurationsdateiformat zu schreiben und es in die programmatische Schnittstelle binden

Die Trennung Konfiguration von Nutzung

Die wichtige Frage in all dies, um sicherzustellen, dass die Konfiguration der Dienste aus ihrer Verwendung getrennt wird. Tatsächlich ist dies ein grundlegendes Gestaltungsprinzip, das mit der Trennung der Schnittstellen von der Implementierung sitzt. Es ist etwas, das wir in einem objektorientierten Programm sehen, wenn bedingte Logik entscheidet, welche Klasse instanziiert und dann künftige Bewertungen dieses bedingten durch Polymorphismus getan und nicht durch duplizierte bedingten Code.

Wenn diese Trennung innerhalb einer einzigen Code-Basis nützlich ist, ist es besonders wichtig, wenn Sie fremde Elemente wie Komponenten und Dienstleistungen verwenden. Die erste Frage ist, ob Sie die Wahl der Implementierungsklasse für bestimmte Einsätze zu verschieben wollen. Wenn ja, brauchen Sie einige Implementierung von Plugins zu verwenden. Sobald Sie Plugins verwenden, dann ist es wichtig, dass die Montage der Plugins wird getrennt vom Rest der Anwendung durchgeführt, so dass Sie verschiedene Konfigurationen leicht für verschiedene Einsätze ersetzen können. Wie Sie erreichen dies zweitrangig. Diese Konfigurationsmechanismus kann entweder einen Service Locator konfigurieren oder Injektion verwenden, um Objekte direkt konfigurieren.

Einige weitere Fragen

In diesem Artikel habe ich auf die grundlegenden Fragen der Service-Konfiguration Injection und Service Locator mit Abhängigkeit konzentriert. Es gibt einige weitere Themen, die in diesem Spiel, die auch die Aufmerksamkeit verdienen, aber ich habe noch keine Zeit hatte zu graben in. Insbesondere gibt es das Problem der Lebenszyklusverhalten. Einige Komponenten haben unterschiedliche Lebenszyklusereignisse: Stopp und startet zum Beispiel. Ein weiteres Problem ist das wachsende Interesse Aspekt orientierte Ideen mit diesen Behältern in Verwendung. Obwohl ich nicht dieses Material in dem Artikel im Moment betrachtet haben, kann ich hoffen, um mehr darüber zu schreiben, entweder durch diesen Artikel erweitern oder durch ein anderes zu schreiben.

Sie können den Lightweight-Container gewidmet, indem Sie auf den Webseiten viel mehr über diese Ideen erfahren. von den picocontainer und Frühjahr Websites Surfen wird Ihnen in viel mehr Diskussion dieser Fragen und einem Start auf einige der weiteren Problemen führen.

Abschließende Gedanken

Der aktuelle Ansturm von Lightweight-Container haben alle ein gemeinsames Grundmuster, wie sie es tun Service Montage — die Abhängigkeit Injektor Muster. Dependency Injection ist eine sinnvolle Alternative zu Service Locator. Wenn Anwendungsklassen bauen die beiden sind in etwa gleichwertig, aber ich denke, Service Locator einen leichten Vorsprung aufgrund seiner einfacheren Verhalten. Allerdings, wenn Sie Klassen bauen in mehreren Anwendungen verwendet werden, dann ist Dependency Injection eine bessere Wahl.

Wenn Sie Dependency Injection dort verwenden, sind eine Reihe von Stilen zwischen wählen. Ich würde vorschlagen, Sie Konstruktor Injektion folgen, wenn Sie mit diesem Ansatz in einer der spezifischen Probleme laufen, in welchem ​​Fall Schalter auf Setter Injektion. Wenn Sie entscheiden sich für einen Container zu bauen oder zu erhalten, suchen Sie nach einem, der sowohl Konstruktor und Setter-Injektion unterstützt.

Die Wahl zwischen Service Locator und Dependency Injection ist weniger wichtig als das Prinzip der innerhalb eines Anwendungsdienstkonfiguration aus der Nutzung der Dienste zu trennen.

Wenn Sie diesen Artikel nützlich gefunden, bitte teilen. Ich schätze das Feedback und Ermutigung

ZUSAMMENHÄNGENDE POSTS

  • Wie Steuern oder Wild beseitigen …

    Wie der wilde Veilchen Die wilde Veilchen loszuwerden ist ein Unkraut, das in erster Linie Staude; cool, gewürzt und hatte breite Blätter. Sie gedeiht in feuchten, schattigen Bereichen und Blüten im zeitigen Frühjahr, früher …

  • Mobile Home Schädlingsbekämpfung Tipps, Rotauge Entfernung für Häuser.

    Mobile Home Schädlingsbekämpfung Tipps Wohnmobil Wohn hat viele Vorteile. Zum einen ist es viel billiger als zu besitzen und ein traditionelles Haus zu halten. Wie jedes Haus obwohl, Mobilheime sind …

  • Nicht giftig Schädlingsbekämpfung, nicht giftig …

    Kann Schwefelgranulat verwendet werden Flöhe auf dem Hof ​​zu befreien? Was würden Sie für das Reinigen Flöhe auf dem Hof ​​vorschlagen? Vielen Dank. Einmal auf dem Hof ​​gegründet, wird es viel mehr als Schwefel nehmen …

  • Natürliche Schädlingsbekämpfung, wo kann …

    Natürliche Schädlingsbekämpfung 1997 2009 Melissa Kaplan Wenn Sie Kinder oder Haustiere haben, finden sichere Wege der natürlichen Schädlinge loszuwerden immer wichtiger. Hat man eingeschlossen oder …

  • Natürliche Geburtenkontrolle Hot …

    Natürliche Birth Control: heißes Wasser und Papaya-Samen (Newstarget) In der heutigen Gesellschaft, ein großer Teil der Last der Geburtenkontrolle umfasst Chemikalien, Arzneimittel und oft fällt auf die Frauen. Wie auch immer, es…

  • So steuern Schaben bei …

    Das Nachdenken über Kakerlaken bringt böse Blicke auf allen unseren Gesichtern. Schaben sind die am weitesten verachtete Schädling in der Welt. Sie sind nachtaktive Insekten, und es vorziehen, in der Dunkelheit zu leben. Ihr…