(Tidak) hukum kode keren yang dapat dipecahkan: Law of Demeter (dengan contoh dalam TypeScript)

Ketika saya mempelajari tentang prinsip-prinsip ini, fleksibilitas kode saya terasa x2, dan kecepatan pengambilan keputusan pada desain entitas x5.



Jika SOLID adalah seperangkat prinsip untuk menulis kode kualitas, maka Law of Demeter (LoD) dan Tell Don't Ask (TDA) adalah trik khusus untuk mencapai SOLID.



Hari ini kita akan berbicara tentang Hukum Demeter ("Hukum Demeter").



Berlebihan



Prinsip ini membantu untuk menentukan: "Bagaimana saya mendapatkan / memodifikasi objek bersarang" - berlaku dalam bahasa di mana Anda dapat mendefinisikan "kelas" dengan properti dan metode.



Situasi sering muncul ketika kita dari suatu tempat (misalnya, dari permintaan HTTP) mendapatkan id dari entitas ʻa`, mengikutinya ke database dan dari entitas ʻa` kita perlu mendapatkan / mengubah entitas `b` dengan memanggil metode` Metode`.



Jadi Wikipedia mengatakan:

Kode `abMethod ()` melanggar Hukum Demeter, dan kode `a.Method ()` benar.


Contoh



Pengguna memiliki Posting yang memiliki Komentar. Anda ingin menerima "Komentar Posting Terakhir".



Anda dapat mengajukan ini:



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


Atau seperti ini:



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


Masalah: kode mengetahui tentang seluruh hierarki data bersarang dan jika hierarki ini berubah / meluas, di mana pun rantai ini dipanggil, Anda harus membuat perubahan (refactor the code + tes).



Untuk mengatasi masalah tersebut, terapkan LoD:



const userLastPostComments = user.getLastPostComments()


Tapi sudah di ` User` kita menulis:



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


Cerita yang sama dengan komentar ditambahkan. Kami dari:



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


Atau, jika Anda ingin memamerkan diagnosis Anda:



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


Mengubah ini menjadi ini:



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


Dan untuk ` Pengguna` :



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




Klarifikasi: ` new Comment` dapat dibuat di luar` user` atau ` post` , semuanya tergantung pada bagaimana logika aplikasi Anda diatur, tetapi semakin dekat dengan pemilik entitas (dalam hal ini,` post` ), semakin baik.



Apa fungsinya?



Kami menyembunyikan detail implementasi, dan jika ada perubahan / ekstensi hierarki (misalnya, posisi tidak hanya akan terletak di `properti posts ') Anda hanya perlu memfaktor ulang metode` getLastPostComments` / ` addCommentToPost` dan menulis ulang pengujian unit hanya untuk metode ini.



Apa kekurangannya



Banyak tambahan kode.



Dalam project kecil, sebagian besar metode hanya akan menjadi ` getter` /` setter` .



Kapan digunakan



(1) LoD cocok untuk Model, Entitas, Agregat, atau Kelas yang memiliki koneksi dalam / kompleks / gabungan.



(2) Kode memerlukan konsep: "Dapatkan komentar dari posting terakhir" - dan posting Anda tidak ada di properti pertama, tetapi di 2 atau lebih, maka, yang pasti, Anda perlu membuat metode ` getLastPostComments` dan menggabungkan beberapa properti dengan properti berbeda posting.



(3) Saya mencoba menggunakan prinsip ini sesering mungkin dalam hal mengubah (mengubah, membuat, menghapus) data. Dan lebih jarang dalam hal mengambil data.



(4) Berdasarkan akal sehat.



Retas hidup



Banyak yang mulai mengkhawatirkan jumlah metode proxy, jadi inilah penyederhanaannya:



Agar tidak membuat metode proxy dalam jumlah tak terbatas, LoD dapat digunakan di kelas tingkat atas (dalam kasus kami, ʻUser`), dan di dalam implementasinya, ini sudah melanggar hukum. Misalnya:



const userLastPostComments = user.getLastPostComments()

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




Seiring waktu, jika `post` bertambah, akan memungkinkan untuk membuatnya menjadi` getLast ()` atau` getLastComments () `dan ini tidak akan memerlukan banyak refactoring.



Apa yang dibutuhkan untuk ini



LoD berfungsi dengan baik jika Anda memiliki hierarki / pohon dependensi entitas yang tepat .



Apa yang harus dibaca



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

Pastikan untuk membaca semua komentar dan komentar di komentar (sebelum komentar Vyacheslav GolovanovSLY_G), mengingat bahwa ada contoh benar dan salah

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

(3) Artikel



PS



Saya bisa mengacaukan beberapa detail / contoh atau menjelaskannya tidak cukup jelas, jadi tulis di komentar yang Anda perhatikan, saya akan membuat perubahan. Semuanya bagus.



PPS



Baca baris komentar ini , di dalamnya kitasarang kami menganalisis kasus yang tidak sepenuhnya menyala secara lebih rinci di artikel ini



All Articles