Zum Inhalt

Dependency Injection und Interface Pattern in Python

Anwendung von Dependency Injection und Interface Pattern in Python am Beispiel eines
symmetrischen Verschlüsselungsprojekts: github.com/eugen-hoppe/symmetric

In der modernen Softwareentwicklung sind Design Patterns unerlässlich, da sie bewährte Lösungen für häufig auftretende Probleme bieten. Sie helfen, den Code wartbar, modular und erweiterbar zu gestalten. In diesem Beitrag wird das eigene GitHub-Projekt symmetric vorgestellt, das eine Bibliothek für symmetrische Verschlüsselung in Python umfasst. Im Fokus stehen zwei wichtige Design Patterns: das Interface Pattern und Dependency Injection.

Das Interface Pattern

Das Interface Pattern ist ein Grundstein der objektorientierten Programmierung und spielt eine zentrale Rolle in der Strukturierung und dem Entwurf von Software. Es definiert die "Form" oder die Methoden, die Klassen implementieren müssen, ohne deren konkrete Implementierung vorzuschreiben. In Python wird dies oft durch die Verwendung von abstrakten Basisklassen (ABCs) und der abstractmethod-Dekoration erreicht.

Beispiel aus dem Projekt

Im "symmetric"-Projekt wird das Interface Pattern im Modul interface.py angewendet. Hier wird eine Klasse SymmetricEncryptionInterface1 definiert, die abstrakte Methoden encrypt und decrypt enthält. Diese Methoden müssen von allen Verschlüsselungsalgorithmen, die in der Bibliothek implementiert werden, bereitgestellt werden.

class SymmetricEncryptionInterface(ABC):
    # ...
    @abstractmethod
    def encrypt(
        self,
        payload: str,
        symmetric_key: str,
        options: Options | None,
    ) -> str:
        """
        Encrypts payload using the specified symmetric key and options
        """
        pass
    # ...
class SymmetricEncryptionInterface(ABC):
    # ...
    @abstractmethod
    def decrypt(
        self,
        cipher: str,
        symmetric_key: str,
        options: Options | None,
    ) -> str:
        """
        Decrypts encrypted data using the specified key and options
        """
        pass
    # ...

Durch diese Definition wird sichergestellt, dass jede Verschlüsselungsklasse, die dieses Interface implementiert, die Methoden encrypt und decrypt in einer standardisierten Weise anbietet, was den Austausch von Algorithmen ohne Änderung der Nutzungsweise ermöglicht.

Docstrings und abstrakte Klassen

Die Verwendung einer abstrakten Klasse eignet sich hervorragend dazu, umfassende Dokumentationen in Form von Docstrings bereitzustellen. Diese Docstrings erläutern klar die Funktionen und Erwartungen an jede Methode, die von den erbenden Klassen implementiert werden muss. Dadurch, dass die abstrakte Klasse die strukturellen und dokumentarischen Grundlagen vorgibt, können sich die Entwickler der erbenden Klassen stärker auf die spezifische Implementierung des Codes konzentrieren. Dies fördert eine klare Trennung zwischen der Erklärung der Funktionalität und ihrer tatsächlichen Umsetzung, was zu einem besser verständlichen und wartbaren Code führt.

Dependency Injection

Dependency Injection ist ein Design Pattern, das die Flexibilität und Erweiterbarkeit von Code durch das Entkoppeln von Komponenten verbessert. Es ermöglicht, dass Abhängigkeiten (z.B. Verschlüsselungsalgorithmen) zur Laufzeit oder über die Konfiguration bereitgestellt werden, anstatt hart im Code verdrahtet zu sein.

Anwendung im Projekt

Im Modul encryption.py2 wird Dependency Injection verwendet, um verschiedene Verschlüsselungsalgorithmen einzubinden. Die Klasse Key nimmt ein Objekt, das SymmetricEncryptionInterface implementiert, und verwendet dieses für Verschlüsselungsoperationen. Dies ermöglicht es, zur Laufzeit unterschiedliche Algorithmen wie AES oder ChaCha20 auszuwählen.

class Key(AbstractKey, SymmetricEncryptionInterface):
    def __init__(
        self,
        algorithm: SymmetricEncryptionInterface = AES(),
        options: Options = Options(),
    ):
        self.algorithm = algorithm
        self.options = options

Hier wird algorithm als Parameter "injiziert", was bedeutet, dass die spezifische Implementierung der Verschlüsselungsmethoden dynamisch basierend auf dem übergebenen Algorithmus bestimmt wird. Dieser Ansatz fördert eine lose Kopplung und erleichtert das Testen und Erweitern des Systems.

Fazit

Die Anwendung von Design Patterns wie dem Interface Pattern und Dependency Injection im "symmetric"-Projekt demonstriert, wie diese Muster zur Steigerung der Modularität, Erweiterbarkeit und Wartbarkeit von Software beitragen können. Durch die Verwendung dieser Muster entsteht ein konsistentes, erweiterbares System, das in der Lage ist, verschiedene bspw. Verschlüsselungsalgorithmen leicht auszutauschen, ohne die zugrundeliegende Anwendungslogik zu beeinträchtigen. Solche Techniken sind entscheidend, um robuste und funktionale Software zu entwickeln.


  1. Die vollständige "Schnittstellen"-Klasse ist unter diesem Link: SymmetricEncryptionInterface zu finden. 

  2. Die vollständige "Injektions"-Klasse ist unter diesem Link: Key zu finden.