(Un) zerbrechliche Gesetze des coolen Codes: Gesetz des Demeters (mit Beispielen in TypeScript)

Als ich von diesen Prinzipien erfuhr, fühlte sich die Flexibilität meines Codes x2 und die Geschwindigkeit der Entscheidungsfindung beim Entitätsdesign x5 an.



Wenn SOLID eine Reihe von Prinzipien zum Schreiben von Qualitätscode ist, sind Law of Demeter (LoD) und Tell Don't Ask (TDA) spezifische Tricks, um SOLID zu erreichen.



Heute werden wir über das Gesetz von Demeter ("Gesetz von Demeter") sprechen .



Übertrieben



Dieses Prinzip hilft bei der Bestimmung: "Wie erhalte / ändere ich verschachtelte Objekte" - anwendbar in Sprachen, in denen Sie "Klassen" mit Eigenschaften und Methoden definieren können.



Eine Situation entsteht oft, wenn wir von irgendwoher (zum Beispiel von einer HTTP-Anfrage) die ID der Entität "a" erhalten, ihr in die Datenbank gefolgt sind und von der Entität "a" die Entität "b" durch Aufrufen der Methode "Method" erhalten / ändern müssen.



Also sagt Wikipedia :

Der Code "abMethod ()" verstößt gegen das Gesetz von Demeter, und der Code "a.Method ()" ist korrekt.


Beispiel



Der Benutzer hat Beiträge mit Kommentaren. Sie möchten "Last Post Comments" erhalten.



Sie können dies einreichen:



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


Oder so:



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


Problem: Der Code kennt die gesamte Hierarchie verschachtelter Daten. Wenn sich diese Hierarchie ändert / erweitert, müssen Sie überall dort, wo diese Kette aufgerufen wurde, Änderungen vornehmen (Code + Tests umgestalten).



Wenden Sie LoD an, um das Problem zu lösen:



const userLastPostComments = user.getLastPostComments()


Aber schon in ` User` schreiben wir:



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


Die gleiche Geschichte mit einem Kommentar. Wir sind aus:



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


Oder wenn Sie Ihre Diagnose vorführen möchten:



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


Daraus Folgendes machen:



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


Und für ` User` :



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




Erläuterung : " Neuer Kommentar" kann außerhalb von " Benutzer" oder " Post " erstellt werden. Dies hängt davon ab, wie die Logik Ihrer Anwendung angeordnet ist. Je näher der Eigentümer der Entität (in diesem Fall " Post" ) ist, desto besser.



Was tut es?



Wir verbergen die Implementierungsdetails, und falls es jemals eine Änderung / Hierarchieerweiterung gibt (z. B. liegen Positionen nicht nur in der Eigenschaft der Posts ), müssen Sie nur die Methode getLastPostComments / addCommentToPost umgestalten und den Komponententest nur für diese Methode neu schreiben.



Was sind die Nachteile



Viel extra Code.



In kleinen Projekten sind die meisten Methoden nur " Getter" / " Setter" .



Wann zu verwenden



(1) LoD eignet sich für Modelle, Entitäten, Aggregate oder Klassen mit tiefen / komplexen / zusammengeführten Verbindungen.



(2) Der Code erfordert das Konzept: "Holen Sie sich die Kommentare des letzten Beitrags" - und Ihre Beiträge befinden sich nicht in der 1. Eigenschaft, sondern in 2 oder mehr. Dann müssen Sie sicher die Methode " getLastPostComments " ausführen und mehrere Eigenschaften mit unterschiedlichen zusammenführen Beiträge.



(3) Ich versuche, dieses Prinzip so oft wie möglich anzuwenden, wenn es darum geht, Daten zu transformieren (zu ändern, zu erstellen, zu löschen). Und seltener beim Abrufen von Daten.



(4) Basierend auf gesundem Menschenverstand.



Life Hack



Viele begannen sich Gedanken über die Anzahl der Proxy-Methoden zu machen, daher hier eine Vereinfachung:



Um nicht unendlich viele Proxy-Methoden zu erstellen, kann LoD in der Klasse der obersten Ebene (in unserem Fall "Benutzer") verwendet werden und verstößt innerhalb der Implementierung bereits gegen das Gesetz. Zum Beispiel:



const userLastPostComments = user.getLastPostComments()

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




Im Laufe der Zeit, wenn "post" wächst, wird es möglich sein, es "getLast ()" oder "getLastComments ()" zu machen, und dies wird nicht viel Refactoring erfordern.



Was wird dafür benötigt?



LoD funktioniert gut, wenn Sie einen geeigneten Entitätsabhängigkeitsbaum / eine ordnungsgemäße Hierarchie haben.



Was zu lesen



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

Lesen Sie unbedingt alle Kommentare und Kommentare von Kommentaren (vor dem Kommentar Vyacheslav GolovanovSLY_G), wobei zu beachten ist, dass es sowohl richtige als auch falsche Beispiele gibt

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

(3) Artikel



PS



Ich könnte einige Details / Beispiele vermasseln oder erklären, dass es nicht klar genug ist. Schreiben Sie also in die Kommentare, die Sie bemerkt haben, ich werde Änderungen vornehmen. Alles gut.



PPS



Lesen Sie diese Kommentarzeile , darin wirHöhle In diesem Artikel analysieren wir einen unvollständig beleuchteten Fall genauer



All Articles