Leyes (in) rompibles del código genial: Ley de Demeter (con ejemplos en TypeScript)

Cuando aprendí sobre estos principios, la flexibilidad de mi código se sintió x2 y la velocidad de la toma de decisiones sobre el diseño de entidades x5.



Si SOLID es un conjunto de principios para escribir código de calidad, entonces Law of Demeter (LoD) y Tell Don't Ask (TDA) son trucos específicos para lograr SOLID.



Hoy hablaremos de la Ley de Demeter ("Ley de Demeter").



Exagerado



Este principio ayuda a determinar: "¿Cómo obtendré / modificaré objetos anidados?" - aplicable en lenguajes donde se pueden definir "clases" con propiedades y métodos.



A menudo, hay una situación en la que desde algún lugar (por ejemplo, de una solicitud HTTP) recibimos el ID de la entidad ʻa`, lo seguimos a la base de datos y desde la entidad` a` necesitamos obtener / cambiar la entidad `b` llamando al método` Método`.



Entonces Wikipedia dice:

El código ʻabMethod () `viola la Ley de Demeter, y el código ʻa.Method ()` es correcto.


Ejemplo



El usuario tiene publicaciones que tienen comentarios. Quieres recibir "Comentarios de la última publicación".



Puede presentar esto:



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


O así:



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


Problema: el código conoce toda la jerarquía de datos anidados, y si esta jerarquía cambia / se expande, donde sea que se llame a esta cadena, tendrá que hacer cambios (código de refactorización + pruebas).



Para resolver el problema, aplique LoD:



const userLastPostComments = user.getLastPostComments()


Pero ya en ` Usuario` escribimos:



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


La misma historia con un comentario agregado. Somos de:



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


O, si desea presumir de su diagnóstico:



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


Convirtiendo esto en esto:



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


Y para ` Usuario` :



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




Aclaración: el " nuevo comentario" puede crearse fuera de " usuario " o " publicación " , todo depende de cómo esté organizada la lógica de la aplicación, pero cuanto más cerca del propietario de la entidad (en este caso, " publicación " ), mejor.



¿Qué hace?



Ocultamos los detalles de implementación, y si alguna vez hay un cambio / extensión de jerarquía (por ejemplo, las posiciones estarán no solo en la `propiedad de las publicaciones '), solo tiene que refactorizar el método` getLastPostComments` / ` addCommentToPost` y reescribir la prueba de unidad solo este método.



Cuales son las desventajas



Muchos extras código.



En proyectos pequeños, la mayoría de los métodos serán simplemente " getter " / " setter " .



Cuándo usar



(1) LoD es bueno para modelos, entidades, agregados o clases que tienen conexiones profundas / complejas / fusionadas.



(2) El código requiere el concepto: "Obtenga los comentarios de la última publicación" - y sus publicaciones no están en la primera propiedad, sino en 2 o más, entonces, seguro, debe usar el método ` getLastPostComments` y fusionar varias propiedades con diferentes puestos.



(3) Intento utilizar este principio con la mayor frecuencia posible cuando se trata de transformar (cambiar, crear, eliminar) datos. Y con menos frecuencia cuando se trata de recuperar datos.



(4) Basado en el sentido común.



Truco de vida



Muchos comenzaron a preocuparse por la cantidad de métodos de proxy, así que aquí hay una simplificación:



para no crear una cantidad infinita de métodos de proxy, LoD puede usarse en la clase de nivel superior (en nuestro caso, `User`), y dentro de la implementación ya está infringiendo la ley. Por ejemplo:



const userLastPostComments = user.getLastPostComments()

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




Con el tiempo, si `post` crece, será posible convertirlo en métodos` getLast () ʻo` getLastComments ()` y esto no implicará mucha refactorización.



Que se necesita para esto



LoD funciona bien si tiene un árbol / jerarquía de dependencia de entidad adecuado .



Que leer



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

Asegúrese de leer todos los comentarios y comentarios de los comentarios (antes del comentario Vyacheslav GolovanovSLY_G), recordando que hay ejemplos correctos e incorrectos

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

(3) Artículo



PD



Podría meter la pata con algunos detalles / ejemplos o explicar que no está lo suficientemente claro, así que escriba en los comentarios que notó, haré cambios. Todo bien.



PPS



Lea esta línea de comentarios , en ellaguarida Desmontamos una carcasa incompleta iluminada con más detalle en este artículo.



All Articles