Leis (in) quebráveis ​​de código legal: The Law of Demeter (com exemplos em TypeScript)

Quando aprendi sobre esses princípios, a flexibilidade do meu código parecia x2 e a velocidade de tomada de decisão no design de entidade x5.



Se SOLID é um conjunto de princípios para escrever código de qualidade, então a Lei de Demeter (LoD) e Diga, Não Pergunte (TDA) são truques específicos para alcançar SOLID.



Hoje vamos falar sobre a Lei de Deméter ("Lei de Deméter").



Exagerado



Este princípio ajuda a determinar: "Como irei obter / modificar objetos aninhados" - aplicável em linguagens onde você pode definir "classes" com propriedades e métodos.



Freqüentemente, há uma situação em que de algum lugar (por exemplo, de uma solicitação HTTP) recebemos o id da entidade `a`, seguimos para o banco de dados e da entidade` a` precisamos obter / alterar a entidade` b` chamando o método` Método`.



Então a Wikipedia diz:

O código `abMethod ()` viola a Lei de Demeter, e o código `a.Method ()` está correto.


Exemplo



O usuário tem postagens com comentários. Você deseja receber "Comentários da última postagem".



Você pode registrar isto:



const posts = user.posts
const lastPostComments = posts[posts.length-1].comments


Ou assim:



const userLastPostComments = user.getPosts().getLast().getComments()


Problema: o código conhece toda a hierarquia de dados aninhados, e se esta hierarquia muda / se expande, onde quer que esta cadeia seja chamada, você terá que fazer mudanças (refatorar o código + testes).



Para resolver o problema, aplique o LoD:



const userLastPostComments = user.getLastPostComments()


Mas já em ` User` escrevemos:



class User {
  // ...
  getLastPostComments(): Comments {
    return this.posts.getLastComments()
  }
  // ...
}


A mesma história com a adição de um comentário. Nós somos de:



const newComment = new Comment(req.body.postid, req.body.content)
user.getPosts().addComment(newComment)


Ou, se você quiser mostrar seu diagnóstico:



const newComment = new Comment(req.body.postid, req.body.content)
const posts = user.posts
posts[posts.length-1].comments.push(newComment)


Transformando isso em:



const posts = user.addCommentToPost(req.body.postid, req.body.content)


E para ` User` :



class User {
  // ...
  addCommentToPost(postId: string, content: string): void {
    // The cleanest
    const post = this.posts.getById(postId)
    return post.addComment(content)
  }
  // ...
}




Esclarecimento: ` new Comment` pode ser criado fora de` user` ou ` post` , tudo depende de como a lógica de sua aplicação está organizada, mas quanto mais próximo do dono da entidade (neste caso,` post` ), melhor.



O que isso faz?



Escondemos os detalhes de implementação, e se houver uma extensão de mudança / hierarquia (por exemplo, as posições não ficarão apenas na `propriedade das postagens '), você só precisa refatorar o método` getLastPostComments` / ` addCommentToPost` e reescrever o teste de unidade apenas este método.



Quais são os contras



Muito extra código.



Em projetos pequenos, a maioria dos métodos será apenas ` getter` /` setter` .



Quando usar



(1) LoD é bom para modelos, entidades, agregados ou classes que têm conexões profundas / complexas / mescladas.



(2) O código requer o conceito: "Pegue os comentários da última postagem" - e suas postagens não estão na 1ª propriedade, mas em 2 ou mais, então, com certeza, você precisa fazer o método ` getLastPostComments` e mesclar várias propriedades com diferentes Postagens.



(3) Tento usar esse princípio tão frequentemente quanto possível quando se trata de transformar (alterar, criar, excluir) dados. E com menos frequência quando se trata de receber dados.



(4) Com base no bom senso.



Hack da vida



Muitos começaram a se preocupar com o número de métodos de proxy, então aqui está uma simplificação:



para não criar um número infinito de métodos de proxy, LoD pode ser usado na classe de nível superior (no nosso caso, ʻUser`), e dentro da implementação já está infringindo a lei. Por exemplo:



const userLastPostComments = user.getLastPostComments()

class User {
  // ...
  getLastPostComments(): Comments {
    //   LoD,    Post    
    const lastPost = this.posts[this.posts.length-1]
    return lastPost.comments
  }
  // ...
}




Com o tempo, se o `post` crescer, será possível torná-lo métodos` getLast () ʻou` getLastComments ()` e isso não implicará em muita refatoração.



O que é necessário para isso



O LoD funciona bem se você tiver uma árvore / hierarquia de dependência de entidade adequada .



O que ler



(1) https://qna.habr.com/q/44822

Certifique-se de ler todos os comentários e comentários de comentários (antes do comentário Vyacheslav GolovanovSLY_G), lembrando que existem exemplos corretos e incorretos

(2) https://ru.wikipedia.org/wiki/Demeter_Law

(3) Artigo



PS



Eu poderia estragar alguns detalhes / exemplos ou explicar que não está claro o suficiente, então escreva nos comentários que você notou, eu farei alterações. Tudo bom.



PPS



Leia esta linha de comentários , nela nóscovil analisamos um caso incompletamente iluminado em mais detalhes neste artigo



All Articles