Código Python fracamente acoplado com injeção de dependência

Jan 13 2023
O software deve ser flexível para responder às mudanças. A injeção de dependência é uma técnica para gerenciar as dependências entre componentes de software.

O software deve ser flexível para responder às mudanças. A injeção de dependência é uma técnica para gerenciar as dependências entre componentes de software. É um padrão de projeto que usa inversão de controle para fornecer a capacidade de trocar componentes para teste ou outros propósitos sem alterar nenhuma outra parte do sistema.

Injeção de Dependência em Python

A ideia principal por trás da injeção de dependência é passar a responsabilidade de fornecer serviços de funções construtoras ou métodos de instanciação diretos para outros objetos. Em outras palavras, um objeto recebe suas dependências em vez de criá-las. Dessa forma, se quisermos adicionar novos recursos, não precisamos alterar o código existente, mas sim passar novas dependências para ele.

Criando abstrações com interfaces

Existem três etapas para escrever código Python fracamente acoplado com injeção de dependência. A primeira etapa é identificar quais dependências o código precisa, a segunda etapa é criar interfaces para cada dependência e a terceira etapa é passá-las para o objeto dependente por meio de seu construtor ou parâmetros de método.

Recentemente, implementei uma funcionalidade que exigia um codificador e um decodificador de bytes para manipular dados de negócios ( simplifiquei sua implementação para fins de legibilidade ). Abstraí suas responsabilidades com as interfaces. O Python não possui uma palavra-chave de interface como Golang, mas você pode replicar a funcionalidade da interface com a biblioteca ABC .

Interfaces para codificador e decodificador.

Com foco na IPacketEncoderinterface, uma classe codificadora concreta deve implementá-la. Os requisitos comerciais iniciais exigiam que todos os tipos fossem codificados e as cadeias de caracteres preenchidas com seu comprimento. Felizmente, o Python fornece a biblioteca struct para fazer a maior parte do trabalho pesado.

Classe concreta PaddedPacketEncoder.

Implementando injeção de dependência

A encode_namefunção é uma implementação simples para criar uma carga útil codificada com o nome especificado. Leva um IPacketEncoderobjeto e uma string como parâmetros.

Instância de PaddedPacketEncoder passada para a função encode_name.

Os dois principais detalhes de implementação estão usando a IPacketEncoderinterface em vez da PaddedPacketEncoderclasse concreta e encoderé um argumento em vez de ser inicializado dentro da função. Isso é injeção de dependência. Por IPacketEncoderser repassado, pode ser facilmente estendido, trocado ou provido de ponta , mantendo assim o sistema frouxamente acoplado.

Algumas semanas depois, houve um novo requisito para um codificador lidar com codificações terminadas em nulo. Felizmente, devido à implementação existente, ela pode ser facilmente implementada.

Classe concreta NullTerminatedPacketEncoder.

NullTerminatedPacketEncoderessencialmente adiciona NULL_BYTEapós cada string codificada. Voltando à encode_namefunção, uma instância de NullTerminatedPacketEncoderé um argumento de codificador válido, pois NullTerminatedPacketEncoderimplementa a IPacketEncoderinterface de maneira semelhante a PaddedPacketEncoder.

NullTerminatedPacketEncoder passado para a função encode_name.

Se a injeção de dependência não fosse usada aqui, isso poderia resultar em um código fortemente acoplado e difícil de mudar, por exemplo:

Código Python de codificação acoplada.

Caso seja necessário um novo codificador, a encode_namefunção terá que ser alterada quase totalmente junto com seus testes, estendendo os prazos de entrega. O que é pior, pode resultar em efeitos colaterais ocultos, especialmente se o desenvolvedor original não estiver mais disponível.

Para concluir, fornecerei um exemplo menos simplista usando essa técnica. SocketServiceé responsável por decodificar um código de operação, chamando seu manipulador relevante e retornando seu resultado. Todos os seus tipos de argumento são abstrações de interface, incluindo seu construtor. A CLI é responsável por determinar qual codificador usar, ou seja, NullTerminated ou Padded, e passar sua instância para a SocketServiceclasse.

SocketService utilizando injeção de dependência.

À medida que os projetos continuam crescendo, é recomendável utilizar uma estrutura de injeção de dependência para “injetar” essas dependências automaticamente, como o Dependency Injector .

Esperançosamente, isso demonstra como escrever código Python fracamente acoplado com injeção de dependência. Muitos desenvolvedores Python não gostam de injeção de dependência porque acham que não é “pythonic”, mas eu diria que seus benefícios não podem ser desacreditados, especialmente quando está presente em tantas outras linguagens de programação.

Obrigado por ler . Se você quiser se conectar comigo, pode me encontrar no Linkedin ou no Github .