JavaRush /Blogue Java /Random-PT /Uma maneira fácil de injetar dependências

Uma maneira fácil de injetar dependências

Publicado no grupo Random-PT
A injeção de dependência (DI) não é um conceito fácil de entender e aplicá-lo a aplicativos novos ou existentes é ainda mais confuso. Jess Smith mostra como fazer injeção de dependência sem um contêiner de injeção nas linguagens de programação C# e Java. Maneira simples de injeção de dependência - 1Neste artigo, mostrarei como implementar injeção de dependência (DI) em aplicativos .NET e Java. O conceito de injeção de dependência chamou a atenção dos desenvolvedores pela primeira vez em 2000, quando Robert Martin escreveu o artigo “Design Principles and Patterns” (mais tarde conhecido pela sigla SOLID ). O D em SOLID refere-se à Dependência de Inversão (DOI), que mais tarde ficou conhecida como injeção de dependência. A definição original e mais comum: inversão de dependência é uma inversão da forma como uma classe base gerencia dependências. O artigo original de Martin usou o seguinte código para ilustrar a dependência de uma classe Copyem uma classe de nível inferior WritePrinter:
void Copy()
	{
	 int c;
	 while ((c = ReadKeyboard()) != EOF)
		WritePrinter(c);
	}
O primeiro problema óbvio é que se você alterar a lista de parâmetros ou os tipos de um método WritePrinter, precisará implementar atualizações sempre que houver dependência desse método. Este processo aumenta os custos de manutenção e é uma fonte potencial de novos erros.
Interessado em ler sobre Java? Junte-se ao grupo de desenvolvedores Java !
Outro problema: a classe Copy não é mais uma candidata potencial para reutilização. Por exemplo, e se você precisar enviar os caracteres inseridos no teclado para um arquivo em vez de para uma impressora? Para fazer isso, você pode modificar a classe Copyda seguinte maneira (sintaxe da linguagem C++):
void Copy(outputDevice dev)
	{
	int c;
	while ((c = ReadKeyboard()) != EOF)
		if (dev == printer)
			WritePrinter(c);
		else
			WriteDisk(c);
	}
Apesar da introdução de uma nova dependência WriteDisk, a situação não melhorou (mas piorou) porque outro princípio foi violado: "entidades de software, isto é, classes, módulos, funções, e assim por diante, devem estar abertas para extensão, mas fechadas para modificação." Martin explica que essas novas instruções condicionais if/else reduzem a estabilidade e a flexibilidade do código. A solução é inverter as dependências para que os métodos de gravação e leitura dependam do Copy. Em vez de "empurrar" dependências, elas são passadas pelo construtor. O código modificado fica assim:
class Reader
	{
		public:
		virtual int Read() = 0;
	};
	class Writer
	{
		public:
		virtual void Write(char) = 0;
	};
	void Copy(Reader& r, Writer& w)
	{
		int c;
		while((c=r.Read()) != EOF)
		w.Write(c);
	}
Agora a classe Copypode ser facilmente reutilizada com diferentes implementações de métodos de classe Readere Writer. A classe Copynão possui nenhuma informação sobre a estrutura interna dos tipos Readere Writer, possibilitando reutilizá-los com diferentes implementações. Mas se tudo isso parece uma espécie de bobagem para você, talvez os exemplos a seguir em Java e C# esclareçam a situação.

Exemplo em Java e C#

Para ilustrar a facilidade da injeção de dependência sem um contêiner de dependência, vamos começar com um exemplo simples que pode ser personalizado para uso DIem apenas algumas etapas. Digamos que temos uma classe HtmlUserPresentationque, quando seus métodos são chamados, gera uma interface de usuário HTML. Aqui está um exemplo simples:
HtmlUserPresentation htmlUserPresentation = new HtmlUserPresentation();
String table = htmlUserPresentation.createTable(rowTableVals, "Login Error Status");
Qualquer projeto que utilize esse código de classe terá uma dependência da classe HtmlUserPresentation, resultando nos problemas de usabilidade e manutenção descritos acima. Uma melhoria surge imediatamente: criar uma interface com assinaturas de todos os métodos atualmente disponíveis na classe HtmlUserPresentation. Aqui está um exemplo desta interface:
public interface IHtmlUserPresentation {
	String createTable(ArrayList rowVals, String caption);
	String createTableRow(String tableCol);
	// Оставшиеся сигнатуры
}
Após criar a interface, modificamos a classe HtmlUserPresentationpara utilizá-la. Voltando a instanciar o type HtmlUserPresentation, agora podemos usar o tipo interface em vez do tipo base:
IHtmlUserPresentation htmlUserPresentation = new HtmlUserPresentation();
String table = htmlUserPresentation.createTable(rowTableVals, "Login Error Status");
A criação de uma interface nos permite usar facilmente outras implementações do IHtmlUserPresentation. Por exemplo, se quisermos testar este tipo, podemos facilmente substituir o tipo base HtmlUserPresentationpor outro tipo chamado HtmlUserPresentationTest. As alterações feitas até agora tornam o código mais fácil de testar, manter e escalar, mas não fazem nada para reutilização, pois todas as HtmlUserPresentationclasses que usam o tipo ainda estão cientes de sua existência. Para remover essa dependência direta, você pode passar um tipo de interface IHtmlUserPresentationpara o construtor (ou lista de parâmetros do método) da classe ou método que irá utilizá-la:
public UploadFile(IHtmlUserPresentation htmlUserPresentation)
O construtor UploadFileagora tem acesso a todas as funcionalidades do tipo IHtmlUserPresentation, mas nada sabe sobre a estrutura interna da classe que implementa esta interface. Neste contexto, a injeção de tipo ocorre quando uma instância da classe é criada UploadFile. Um tipo de interface IHtmlUserPresentationtorna-se reutilizável ao passar diferentes implementações para diferentes classes ou métodos que requerem funcionalidades diferentes.

Conclusão e recomendações para consolidação do material

Você aprendeu sobre injeção de dependência e que diz-se que as classes dependem diretamente umas das outras quando uma delas instancia outra para obter acesso à funcionalidade do tipo de destino. Para dissociar a dependência direta entre os dois tipos, você deve criar uma interface. Uma interface dá a um tipo a capacidade de incluir diferentes implementações, dependendo do contexto da funcionalidade necessária. Ao passar um tipo de interface para um construtor ou método de classe, a classe/método que precisa da funcionalidade não conhece nenhum detalhe sobre o tipo que implementa a interface. Por causa disso, um tipo de interface pode ser reutilizado em diferentes classes que exigem comportamento semelhante, mas não idêntico.
  • Para experimentar a injeção de dependência, observe seu código de um ou mais aplicativos e tente converter um tipo base muito usado em uma interface.

  • Altere as classes que instanciam diretamente esse tipo base para utilizar esse novo tipo de interface e passe-o pelo construtor ou lista de parâmetros do método da classe que irá utilizá-lo.

  • Crie uma implementação de teste para testar esse tipo de interface. Depois que seu código for refatorado, DIele se tornará mais fácil de implementar e você notará como seu aplicativo se tornará muito mais flexível em termos de reutilização e manutenção.
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION