O que significa injetar dados (vs comportamento) em um construtor de classe e por que isso é considerado uma má prática?


10

Estou lendo o livro "Learning TypeScript" de Remo Jansen. Em uma seção, o autor descreve como criar uma estrutura MVC de prova de conceito muito simples, incluindo como criar a Modelclasse e diz o seguinte:

Um modelo precisa ser fornecido com a URL do serviço da web que consome. Vamos usar um decorador de classe chamado ModelSettings para definir a URL do serviço a ser consumido. Poderíamos injetar a URL do serviço por meio de seu construtor, mas é considerado uma prática ruim injetar dados (em oposição a um comportamento) por meio de um construtor de classe .

Eu não entendo essa última frase. Em particular, não entendo o que significa "injetar dados". Parece-me que em quase todas as introduções às classes JavaScript usando exemplos simplificados, os dados são introduzidos ("injetados"?) No construtor por meio de seus parâmetros. Por exemplo:

class Person {
  constructor(name) {
    this.name = name;
  }
}

Eu certamente penso namecomo dados, não como comportamento, e é universalmente incluído nesse tipo de exemplo como um parâmetro construtor, e nunca há menção de que essa é uma prática ruim. Assim, suponho que estou entendendo mal algo na citação acima, seja o que se entende por "dados" ou "injetar" ou qualquer outra coisa.

Suas respostas podem incluir explicações sobre quando, onde, como e por que usar decoradores em JavaScript / TypeScript, pois suspeito fortemente que o conceito esteja intimamente conectado ao entendimento que procuro. No entanto, o mais importante, quero entender de maneira mais geral o que significa injeção de dados por meio de um construtor de classe e por que isso é ruim.


Para dar mais contexto à cotação acima, esta é a situação: ModelÉ criada uma classe que, neste exemplo, será usada para criar modelos de bolsa de valores, um para NASDAQ e outro para NYSE. Cada modelo requer o caminho do serviço da web ou arquivo de dados estático que fornecerá os dados brutos. O livro afirma que um decorador deve ser usado para essas informações, em vez de um parâmetro construtor, levando ao seguinte:

@ModelSettings("./data/nasdaq.json")
class NasdaqModel extends Model implements IModel {
  constructor(metiator : IMediator) {
    super(metiator);
  }
...
}

Só não entendi por que devo adicionar o URL do serviço por meio do decorador, em vez de simplesmente como um parâmetro para o construtor, por exemplo

constructor(metiator : IMediator, serviceUrl : string) {...

Eu sugiro que você faça uma pesquisa rápida no Google sobre injeção de dependência . Este não é o fórum correto para fazer esta pergunta. :)
toskv

1
Levarei sua resposta a sério, mas pesquisei no google e encontrei discussões sobre injeção de dependência. "Injeção de dependência" e "injeção de dados" se referem à mesma coisa? Além disso, tenho a impressão de que "injeção de dependência" é uma "coisa boa" (ou pelo menos uma "coisa alternativa"), enquanto a discussão sobre "injeção de dados" na citação que forneci faz parecer uma "coisa ruim" .

Injeção de dependência e injeção de dados são duas coisas diferentes. o primeiro é um princípio de design, enquanto o segundo é um tipo de ataque. Se você deseja um termo de pesquisa mais claro, tente "inversão de controle". É um pouco mais amplo, mas ajuda a pintar uma imagem mais clara também.
toskv

1
Os ataques de "injeção de dados" são, acredito, um animal muito diferente do que o autor do livro citado fala quando diz "injetar dados". Essa é uma das razões pelas quais fiquei frustrado com as pesquisas no Google sobre isso. Mesmo que eu precise entender melhor, por exemplo, os princípios do SOLID, não entendo como fornecer um "nome" como parâmetro para um construtor "Person" é normal e OK, mas fornecer um "serviceUrl" como parâmetro para um "Model" O construtor é inadequado ou como é diferente do exemplo "name" / "Person".

7
Eu acho que Remo está enganado. Parâmetros são dados, não importa o que ele diga. Os dados que são injetados sempre têm um tipo e todos os tipos em linguagens orientadas a objetos têm algum tipo de comportamento.
Robert Harvey

Respostas:


5

Darei ao autor o benefício da dúvida e talvez seja dessa maneira que o Typescript seja, mas, caso contrário, em outros ambientes, é uma afirmação totalmente infundada que não deve ser levada a sério.

De cabeça para baixo, posso pensar em uma variedade de situações em que a transmissão de dados via construtor é boa, algumas neutras, mas nenhuma em que é ruim.

Se uma classe em particular depende de um dado em particular para estar em um estado válido e ser executado corretamente, faz todo o sentido exigir esses dados no construtor. Uma classe que representa uma porta serial pode levar o nome da porta, um objeto de arquivo pode exigir o nome do arquivo, uma tela de desenho que requer sua resolução etc. A menos que você passe os dados no construtor, é possível que o objeto esteja em um estado inválido. deve ser vigiado e verificado. Caso contrário, você poderá verificar apenas na instanciação do objeto e, posteriormente, assumir o funcionamento em sua maior parte. Os autores afirmam que essa situação benéfica é impossível.

Além disso, a decisão de proibir a transmissão de dados em um construtor também impossibilita praticamente todos os objetos imutáveis. Objetos imutáveis ​​têm uma variedade de benefícios em muitas situações, e todos seriam descartados com a política do autor.

Mesmo que objetos mutáveis ​​sejam o que você deseja, como é essa má prática:

var blah = new Rectangle(x,y,width,height);

A favor de:

var blah = new Rectangle();
blah.X = x;
blah.Y = y;
blah.Width = width;
blah.Height = height;

O autor realmente acha que a primeira é uma má prática e eu sempre devo ir com a opção 2? Eu acho que é uma conversa louca.

Portanto, como não tenho o livro e não o leria de qualquer maneira, mesmo que o fizesse, veria essa declaração e praticamente qualquer declaração geral nesse momento com uma quantidade significativa de suspeita.


Muito obrigado pela sua discussão. O que você diz "cheira bem" para mim, especialmente quando você dá o exemplo Retângulo. Ainda me pergunto se o autor está fazendo uma distinção entre os dados necessários para a classe e para cada instância da classe. No entanto, não acho que o projeto que o livro descreve tenha profundidade suficiente para esclarecer isso. Como observação lateral, sua resposta me enviou uma investigação inicial sobre imutabilidade de objetos, por mais que isso se relacione ou não à minha pergunta original, então obrigado por isso também!
Andrew Willems

0

Eu acho que depende do contexto que tipo de modelo está sendo discutido aqui. Não tenho o livro de Remo, mas acho que o modelo é um tipo de modelo de serviço, que precisa recuperar os dados de um serviço da Web remoto. Se for esse o caso, sendo um modelo de serviço da web, é melhor passar todos os dados necessários como argumentos nos métodos do serviço da web, tornando o serviço sem estado.

O serviço sem estado tem várias vantagens, por exemplo, quem lê uma chamada de método de serviço não precisa procurar quando o serviço é construído para descobrir os detalhes do serviço chamado. Todos os detalhes são mostrados nos argumentos que estão sendo usados ​​na chamada do método (exceto na URL remota).


Sim, o modelo precisa recuperar os dados (eventualmente de um serviço da Web remoto, como você sugere, mas no livro isso é apenas uma demonstração, portanto, inicialmente, são apenas dados simulados codificados diretamente na linha). Não estou entendendo sua sugestão sobre a transmissão de dados como argumentos "nos métodos do serviço da web". Eu estava perguntando sobre a diferenciação entre passar dados como parâmetros (1) para um construtor versus (2) para um decorador. Você parece sugerir uma terceira opção, ou seja, transmitir dados como parâmetros / argumentos para um método do serviço da web. Estou perdendo o seu ponto?
Andrew Willems

0

Apenas adivinhando.

Se eu ouvir 'injetar comportamento, não dados', pensaria em vez de fazer o seguinte:

(Desculpe pelo exemplo no pseudocódigo):

class NoiseMaker{
  String noise;
  NoiseMaker(String noise){
     this.noise = noise;
  }
  void soNoise(){
    writeToOutput(noise)
  }
}

Para fazer isso:

interface Noise{
  String getAudibleNoise();
}

class PanicYell implements Noise{
   String getAudibleNoise(){
       return generateRandomYell();
   }
   .....
}



class WhiteNoise implements Noise{
   String getAudibleNoise(){
       return generateNoiseAtAllFrequences();
   }
   .....
}

class NoiseMaker{
  Noise noise;
  NoiseMaker(Noise noise){
     this.noise = noise;
  }
  void soNoise(){
    writeToOutput(noise.getAudibleNoise())
  }
}

Dessa forma, você pode alterar o comportamento do ruído sempre, torná-lo aleatório, dependente de uma variável interna ...

Eu acho que é tudo sobre a regra 'favor composto sobre herança'. Qual é uma ótima regra, devo dizer.

Isso NÃO SIGNIFICA que você não pode 'injetar' o nome no objeto 'Pessoa', obviamente, porque esse nome é puramente dados comerciais. Mas no exemplo que você fornece, o serviço web, a URL é algo que você precisa para gerar algo que de alguma forma conecta um serviço. De alguma forma, isso é um comportamento: se você injeta a URL, injeta os 'dados' necessários para criar um 'comportamento'; portanto, é melhor fazer com que o comportamento seja externo e injetar pronto para ser usado: em vez disso, injete uma injeção de URL uma conexão utilizável ou um construtor de conexões utilizável.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.