Lose gekoppelter Python-Code mit Dependency Injection

Jan 13 2023
Software muss flexibel sein, um auf Veränderungen reagieren zu können. Dependency Injection ist eine Technik zur Verwaltung der Abhängigkeiten zwischen Softwarekomponenten.

Software muss flexibel sein, um auf Veränderungen reagieren zu können. Dependency Injection ist eine Technik zur Verwaltung der Abhängigkeiten zwischen Softwarekomponenten. Es handelt sich um ein Entwurfsmuster, das die Umkehrung der Kontrolle nutzt , um die Möglichkeit zu bieten, Komponenten zu Test- oder anderen Zwecken auszutauschen, ohne einen anderen Teil des Systems zu ändern.

Abhängigkeitsinjektion in Python

Die Hauptidee hinter der Abhängigkeitsinjektion besteht darin, die Verantwortung für die Bereitstellung von Diensten von Konstruktorfunktionen oder direkten Instanziierungsmethoden auf andere Objekte zu übertragen. Mit anderen Worten: Einem Objekt werden seine Abhängigkeiten zugewiesen, anstatt sie zu erstellen. Wenn wir auf diese Weise neue Funktionen hinzufügen möchten, müssen wir den vorhandenen Code nicht ändern, sondern müssen ihm stattdessen neue Abhängigkeiten übergeben.

Abstraktionen mit Schnittstellen erstellen

Das Schreiben von lose gekoppeltem Python-Code mit Abhängigkeitsinjektion besteht aus drei Schritten. Der erste Schritt besteht darin, zu identifizieren, welche Abhängigkeiten der Code benötigt, der zweite Schritt besteht darin, Schnittstellen für jede Abhängigkeit zu erstellen, und der dritte Schritt besteht darin, sie über seine Konstruktor- oder Methodenparameter an das abhängige Objekt zu übergeben.

Kürzlich habe ich Funktionen implementiert, die einen Byte-Encoder und -Decoder zur Manipulation von Geschäftsdaten erforderten ( ich habe die Implementierung aus Gründen der Lesbarkeit vereinfacht ). Ich habe ihre Verantwortlichkeiten auf Schnittstellen abstrahiert. Python hat kein Schnittstellenschlüsselwort wie Golang, aber Sie können die Schnittstellenfunktionalität mit der ABC- Bibliothek replizieren.

Schnittstellen für Encoder und Decoder.

Konzentriert man sich auf die IPacketEncoderSchnittstelle, muss eine konkrete Encoderklasse diese implementieren. Die anfänglichen Geschäftsanforderungen erforderten die Kodierung aller Typen und das Auffüllen von Zeichenfolgen mit ihrer Länge. Glücklicherweise stellt Python die Strukturbibliothek zur Verfügung , um den Großteil der harten Arbeit zu erledigen.

Konkrete Klasse von PaddedPacketEncoder.

Implementierung der Abhängigkeitsinjektion

Die encode_nameFunktion ist eine einfache Implementierung zum Erstellen einer codierten Nutzlast mit dem angegebenen Namen. Als Parameter werden ein IPacketEncoderObjekt und eine Zeichenfolge benötigt.

Die PaddedPacketEncoder-Instanz wird an die Funktion „encode_name“ übergeben.

Die beiden wichtigsten Implementierungsdetails sind die Verwendung der IPacketEncoderSchnittstelle anstelle der PaddedPacketEncoderkonkreten Klasse und encoderdie Tatsache, dass sie ein Argument ist und nicht innerhalb der Funktion initialisiert wird. Dies ist eine Abhängigkeitsinjektion. Da IPacketEncoderes durchgereicht wird, kann es leicht erweitert, geändert oder mit einem Stutzen versehen werden , wodurch das System lose gekoppelt bleibt.

Einige Wochen später gab es eine neue Anforderung an einen Encoder, der nullterminierte Codierungen verarbeiten muss. Glücklicherweise kann es aufgrund der vorhandenen Implementierung problemlos implementiert werden.

Konkrete NullTerminatedPacketEncoder-Klasse.

NullTerminatedPacketEncoderfügt im Wesentlichen NULL_BYTEnach jeder codierten Zeichenfolge hinzu. Zurück zur encode_nameFunktion: Eine Instanz von NullTerminatedPacketEncoderist ein gültiges Encoder-Argument, da NullTerminatedPacketEncodersie die IPacketEncoderSchnittstelle ähnlich wie implementiert PaddedPacketEncoder.

NullTerminatedPacketEncoder wird an die Funktion „encode_name“ übergeben.

Wenn hier keine Abhängigkeitsinjektion verwendet würde, könnte dies zu Code führen, der eng gekoppelt und schwer zu ändern ist, zum Beispiel:

Gekoppelter Codierungs-Python-Code.

Wird ein neuer Encoder benötigt, encode_namemuss die Funktion samt Tests nahezu vollständig geändert werden, wodurch sich die Lieferzeiten verlängern. Schlimmer noch: Es kann zu versteckten Nebenwirkungen kommen, insbesondere wenn der ursprüngliche Entwickler nicht mehr verfügbar ist.

Abschließend werde ich ein weniger einfaches Beispiel für diese Technik liefern. SocketServiceist dafür verantwortlich, einen Operationscode zu dekodieren, seinen relevanten Handler aufzurufen und sein Ergebnis zurückzugeben. Alle seine Argumenttypen sind Schnittstellenabstraktionen, einschließlich seines Konstruktors. Die CLI ist dafür verantwortlich, zu bestimmen, welcher Encoder verwendet werden soll, z. B. NullTerminated oder Padded, und seine Instanz an die SocketServiceKlasse weiterzuleiten.

SocketService nutzt Abhängigkeitsinjektion.

Da Projekte weiter wachsen, empfiehlt es sich, ein Dependency-Injection-Framework zu verwenden, um diese Abhängigkeiten automatisch zu „injizieren“, z. B. Dependency Injector .

Hoffentlich zeigt dies, wie man lose gekoppelten Python-Code mit Abhängigkeitsinjektion schreibt. Viele Python-Entwickler mögen die Abhängigkeitsinjektion nicht, weil sie der Meinung sind, dass sie nicht „pythonisch“ ist, aber ich würde behaupten, dass ihre Vorteile nicht in Misskredit gebracht werden können, insbesondere wenn sie in so vielen anderen Programmiersprachen vorhanden ist.

Danke fürs Lesen . Wenn Sie mit mir in Kontakt treten möchten, finden Sie mich auf Linkedin oder Github .