Módulo que requer vs injeção de dependência em Javascript

24

Hoje em dia uma pergunta surgiu em minha mente:

A maneira como o Javascript contraria quase tudo o que é considerado uma boa prática no desenvolvimento de software tradicional?

Eu tenho uma série de perguntas / observações relacionadas a essa declaração, mas, a fim de respeitar o formato do StackExchange, será melhor dividi-las em perguntas diferentes.

Módulo que requer

Atualmente, o código JavaScript padrão se parece com:

const someModule = require('./someModule')

module.exports = function doSomethingWithRequest() {
  // do stuff
  someModule.someFunc()
  // do other stuff
}

Vantagens

  • Encapsulamento: o módulo funciona de maneira independente e sabe tudo o que é necessário para desempenhar suas funções.
  • Como colorista, é mais fácil para os clientes usar o módulo.

Desvantagens

  • Baixa testabilidade: isso é padrão quando não se usa DI, mas em linguagens dinâmicas, como Javscript, pode ser contornado * por módulos como mockeryou rewire.
  • Certamente viola o DIP - não deve ser confundido com injeção de dependência. - já que só posso importar módulos concretos.
  • Provavelmente viola o OCP - por exemplo, imagine que eu tenho um módulo de log que grava no sistema de arquivos (através do fsmódulo). Se eu quiser estender esse módulo de log para enviá-lo à rede, seria muito difícil.

* Isso pode funcionar com os módulos CommonJS ou AMD, uma vez que eles são implementados principalmente na área do usuário. No entanto, não tenho certeza de como isso pode ser possível com a importsintaxe do ES6 .

Injeção de dependência

Usando injeção de dependência, seria mais como:

module.exports = function doSomethingWithRequest(someModule) {
  // do stuff
  someModule.someFunc()
  // do other stuff
}

Vantagens

  • Maior testabilidade: agora é mais fácil stub / mock someModule, mesmo usando a sintaxe ES6.
  • É possível honrar o DIP: não necessariamente, pois o módulo do cliente ainda pode ser programado para a implementação e não para uma interface.

Desvantagens

  • Encapsulamento quebrado: a principal questão restante é:

    Ok, então quem criará / exigirá as dependências?

  • Fazer isso em todos os clientes do módulo parece muito MOLHADO .
  • Provavelmente, isso exigiria que eu usasse um contêiner de DI para ser viável em um projeto real.

Então, a verdadeira questão aqui é:

Por que os desenvolvedores Javascript tendem a se inclinar para a primeira abordagem?

É apenas "o caminho do Javascript"?

Eu mesmo escrevo código assim na maioria das vezes. Eu tive o meu quinhão de configuração de teste usando bibliotecas de simulação, mas sempre me senti meio errado ao fazê-lo.

Estou esquecendo de algo?

Henrique Barcelos
fonte
Como um cara .Net que recentemente se interessou por NodeJs, eu também tenho lutado com isso. Eu descobri macaco patching dependências com Proxyquire (muito parecido com ReWire) para ser aprovado para fins de teste, mas às vezes você legítimas necessidade múltiplas implementações de uma dependência ...
RubberDuck

Respostas:

6

Eu sou principalmente um programador de PHP, mas estive em contato com 4 equipes de JavaScript no último ano.

Como programador orientado a objetos, o princípio da injeção de dependência pareceria o melhor caminho, no entanto, poucos desenvolvedores de JS me disseram o contrário. JS é um mundo totalmente diferente.

Como o JavaScript permite que você conserte tudo e qualquer coisa usando técnicas muito simples, os desenvolvedores de JavaScript aprenderam a adaptar uma tecnologia diferente sobre como criar aplicativos JavaScript em maior escala. A maioria deles é criada como conjuntos de módulos independentes, que expõem a funcionalidade por meio de exportações públicas, ocultando as partes internas do módulo para não permitir que outras pessoas reescrevam as funções nas quais você confia.

A abordagem usual geralmente é chegar ao ponto de não expor um construtor, mas expor a construção de um objeto usando um invólucro de fábrica - pelo mesmo motivo: se você der a alguém acesso a um objeto, ele poderá instanciar diretamente o objeto. tem permissão para mudar qualquer coisa.

Ao aproveitar o design modular, você nega que outras pessoas mexam com suas funções que espera funcionar, mas ainda tem a capacidade de testar seus módulos - por meio da API pública do arquivo necessário, a API que você criou.

Andy
fonte