(Un)クールなコードの壊れやすい法則:Demeterの法則(TypeScriptの例を含む)

これらの原則について学んだとき、コードの柔軟性はx2であり、エンティティの設計を決定する速度はx5でした。SOLIDが高品質のコードを作成するための一連の原則である



場合、Demeterの法則(LoD)Tell Do n't Ask(TDA)は、SOLIDを達成するための特定のトリックです。 今日はデメテル法則についてお話します







誇張



この原則は、「ネストされたオブジェクトを取得/変更する方法」を決定するのに役立ちます-プロパティとメソッドを使用して「クラス」を定義できる言語に適用できます。



どこかから(たとえば、HTTPリクエストから)エンティティ「a」のIDを取得し、それをデータベースにたどり、エンティティ「a」からメソッド「メソッド」を呼び出してエンティティ「b」を取得/変更する必要がある場合に、状況がよく発生します。



だからウィキペディアは言う:

ʻabMethod() `コードはDemeterの法則に違反しており、ʻa.Method()`コードは正しいです。




ユーザーにはコメントのある投稿があります。「最後の投稿コメント」を受け取りたい。



あなたはこれを提出することができます:



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


またはこのように:



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


問題:コードはネストされたデータの階層全体を認識しており、この階層が変更/拡張された場合、このチェーンが呼び出された場所で、変更を加える必要があります(コードとテストをリファクタリングします)。



この問題を解決するには、LoDを適用します。



const userLastPostComments = user.getLastPostComments()


しかし、すでに「ユーザー」には次のように書いています。



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


コメントを追加した同じ話。から来ました:



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


または、診断を誇示したい場合:



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


これをこれに変える:



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


そして `ユーザー`のために



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




明確化: ` newComment`は` user`または ` post`の外部で作成できます。これはすべて、アプリケーションのロジックの配置方法によって異なりますが、エンティティの所有者(この場合は` post`)に近いほど良いです。



それは何をするためのものか?



私たちは、実装の詳細を隠し、これまでの変更/階層拡張が存在する場合(例えば、位置だけでなく、うその`プロパティになります投稿「)だけmethod`のリファクタリングする必要がありgetLastPostComments ` / ` addCommentToPost `と書き換えユニットテストにのみ、この方法を。



短所は何ですか



余分なものがたくさん コード。



小さなプロジェクトでは、ほとんどのメソッドは単なる ` getter` /` setter`になります。



いつ使用するか



(1)LoDは、深い/複雑な/マージされた接続を持つモデル、エンティティ、集合体、またはクラスに適しています。



(2)コードには、「最後の投稿のコメントを取得する」という概念が必要です。投稿は1番目のプロパティではなく、2つ以上のプロパティにあるため、確かに、 ` getLastPostComments`メソッドを作成し、いくつかのプロパティを異なるプロパティにマージする必要があります。投稿。



(3)データの変換(変更、作成、削除)に関しては、この原則をできるだけ頻繁に使用するようにしています。そして、データの取得に関してはそれほど頻繁ではありません。



(4)常識に基づく。



ライフハック



多くの人がプロキシメソッドの数について心配し始めたので、ここに簡略化



を示します。無限の数のプロキシメソッドを作成しないために、LoDをトップレベルクラス(この場合は「User」)で使用でき、実装内ではすでに法律に違反しています。例えば:



const userLastPostComments = user.getLastPostComments()

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




時間が経つにつれて、 `post`が大きくなると、それをメソッド` getLast() ʻまたは `getLastComments()ʻにすることが可能になり、これは多くのリファクタリングを必要としません。



これに必要なもの



適切なエンティティ依存関係ツリー/階層がある場合、LoDはうまく機能します。



何を読むか



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

必ずすべてのコメントとコメントのコメントを読んでください(コメントの前にVyacheslav GolovanovSLY_G)、正しい例と間違った例の両方があることを思い出してください

2)https://ru.wikipedia.org/wiki/Zakon_Demeter

(3)記事



PS



私はいくつかの詳細/例を台無しにするか、それが十分に明確ではないことを説明することができたので、あなたが気づいたコメントに書き留めてください、私は変更を加えます。すべて良い。



PPS



読むコメント行をそれで、我々隠れ家 この記事では、不完全に照らされたケースをより詳細に分析します



All Articles