Saya punya tiga pertanyaan tentang hukum Demeter.
Terlepas dari kelas yang secara khusus ditunjuk untuk mengembalikan objek - seperti kelas pabrik dan pembangun - apakah boleh metode untuk mengembalikan objek, misalnya objek yang dipegang oleh salah satu properti kelas atau akankah itu melanggar hukum demeter (1) ? Dan jika itu melanggar hukum demeter, apakah penting jika objek yang dikembalikan adalah objek yang tidak dapat diubah yang mewakili sepotong data dan tidak berisi apa pun selain getter untuk data ini (2a)? Atau apakah ValueObject semacam itu merupakan anti-pola dalam dirinya sendiri, karena semua yang dilakukan dengan data di kelas itu dilakukan di luar kelas (2b)?
Dalam kode semu:
class A {}
class B {
private A a;
public A getA() {
return this.a;
}
}
class C {
private B b;
private X x;
public void main() {
// Is it okay for B.getA to exist?
A a = this.b.getA();
a.doSomething();
x.doSomethingElse(a);
}
}
Saya menduga bahwa hukum Demeter melarang pola seperti di atas. Apa yang dapat saya lakukan untuk memastikan bahwa doSomethingElse()
dapat dipanggil sementara tidak melanggar hukum (3)?
sumber
x
mengatur?x
hanyalah properti berbeda yang nilainya hanya beberapa objek yang dapat dipanggildoSomethingElse
.user.currentSession.isLoggedIn()
. karena pasangan klienuser
untuk tidak hanyauser
tetapi juga kolaboratornyasession
,. Sebaliknya Anda ingin dapat menulisuser.isLoggedIn()
. ini biasanya dicapai dengan menambahkanisLoggedIn
metodeuser
yang didelegasikan kepada implementasinyacurrentSession
.Jawaban:
Dalam banyak bahasa pemrograman, semua nilai yang dikembalikan adalah objek. Seperti yang dikatakan orang lain, tidak bisa menggunakan metode objek yang dikembalikan memaksa Anda untuk tidak pernah mengembalikan apa pun sama sekali. Anda harus bertanya pada diri sendiri, "Apa tanggung jawab kelas A, B, dan C?" Inilah sebabnya mengapa menggunakan nama variabel metasyntactic seperti A, B dan C selalu meminta jawaban "itu tergantung" karena tidak ada tanggung jawab bawaan dalam istilah itu.
Anda mungkin ingin melihat Desain Domain Driven, yang akan memberi Anda set heuristik yang lebih bernuansa tentang alasan di mana fungsi harus pergi dan siapa yang harus memohon apa.
Pertanyaan kedua Anda tentang objek yang tidak dapat diubah berbicara tentang gagasan
Obyek Nilai dibandingkan dengan objek Entitas. di DDD, Anda dapat melewatkan objek nilai dengan hampir tanpa batasan. Ini tidak berlaku untuk objek entitas.
LOD yang simplistik jauh lebih baik diekspresikan dalam DDD sebagai aturan untuk mengakses Agregat . Tidak ada objek eksternal yang boleh menyimpan referensi ke anggota kelompok unsur kehidupan. Hanya referensi root yang harus dimiliki.
Selain DDD, Anda setidaknya ingin mengembangkan set stereotip Anda sendiri untuk kelas Anda yang masuk akal untuk domain Anda. Menegakkan aturan tentang stereotip tersebut saat Anda merancang sistem Anda.
Juga selalu ingat bahwa semua aturan ini adalah untuk mengatur kompleksitas, bukan untuk melukai diri sendiri.
sumber
Law of Demeter
,tell, don't ask
,getters are evil
,object encapsulation
dansingle responsibility principle
tampaknya mendidih ke pertanyaan ini: ketika harus delegasi ke objek domain digunakan sebagai lawan untuk mendapatkan nilai objek dan melakukan sesuatu dengan itu? Akan sangat berguna untuk dapat menerapkan kualifikasi yang bernuansa pada situasi di mana keputusan seperti itu harus dibuat.Ya itu pasti.
Mari kita lihat poin-poin utama :
Ketiganya membuat Anda bertanya satu pertanyaan: Siapa teman?
Ketika memutuskan apa yang akan dikembalikan, Hukum Demeter atau prinsip pengetahuan paling sedikit (LoD) tidak menentukan bahwa Anda membela terhadap coders yang bersikeras melanggarnya. Ini menentukan bahwa Anda tidak memaksa pembuat kode untuk melanggarnya.
Membuat bingung ini adalah alasan mengapa begitu banyak yang berpikir seorang setter harus selalu kembali batal. Tidak. Anda harus mengizinkan cara untuk membuat kueri (getter) yang tidak mengubah status sistem. Itu pemisahan permintaan perintah dasar .
Apakah ini berarti Anda bebas mempelajari rantai kode bersama-sama apa pun yang Anda inginkan? Tidak. Rantai bersama hanya apa yang dimaksudkan untuk dirantai bersama. Kalau tidak, rantai mungkin berubah dan tiba-tiba barang Anda rusak. Inilah yang dimaksud oleh teman.
Rantai panjang bisa dirancang untuk. Antarmuka yang lancar, iDSL, sebagian besar Java8, dan StringBuilder tua yang baik semuanya dimaksudkan untuk membuat Anda membangun rantai panjang. Mereka tidak melanggar LoD karena semua yang ada di rantai dimaksudkan untuk bekerja bersama dan berjanji untuk terus bekerja bersama. Anda melanggar demeter saat Anda menyatukan hal-hal yang tidak pernah saling mendengar. Teman adalah mereka yang berjanji untuk menjaga agar rantai Anda tetap berfungsi. Friends of Friends tidak.
Ini hanya menciptakan peluang untuk melanggar demeter. Ini bukan pelanggaran. Ini bahkan belum tentu buruk.
Kekekalan itu baik tetapi tidak relevan di sini. Mendapatkan barang melalui rantai yang lebih panjang tidak membuatnya lebih baik. Apa yang membuatnya lebih baik adalah memisahkan cara mendapatkan dari penggunaan. Jika Anda menggunakan, tanyakan apa yang Anda butuhkan sebagai parameter. Jangan berburu untuk itu dengan menggali getter orang asing.
Sebelum saya berbicara tentang
x.doSomethingElse(a)
mengerti bahwa Anda pada dasarnya telah menulisb.getA().doSomething()
Sekarang, LoD bukan latihan penghitungan titik . Tetapi ketika Anda membuat rantai Anda mengatakan bahwa Anda tahu cara mendapatkan
A
(dengan menggunakanB
) dan Anda tahu cara menggunakanA
. Baiklah sekarangA
danB
lebih baik berteman dekat karena Anda baru saja menggabungkan mereka.Jika Anda baru saja meminta sesuatu untuk memberi Anda
A
sesuatu yang dapat Anda gunakanA
dan tidak akan peduli dari mana asalnya danB
bisa hidup bahagia dan bebas dari obsesi Anda untuk mendapatkanA
dariB
.Adapun
x.doSomethingElse(a)
tanpa rincian dari manax
asalnya, LoD tidak memiliki apa-apa untuk dikatakan tentang hal itu.LoD dapat mendorong pemisahan penggunaan dari konstruksi . Tapi saya akan tunjukkan jika Anda secara religius memperlakukan setiap objek sebagai tidak ramah Anda akan terjebak menulis kode dalam metode statis. Anda dapat membuat grafik objek yang sangat rumit dengan cara utama ini tetapi pada akhirnya Anda harus memanggil metode untuk memulai sesuatu yang berfungsi. Anda hanya perlu memutuskan siapa teman Anda. Tidak ada jalan keluar dari itu.
Jadi ya, sebuah kelas diperbolehkan untuk mengembalikan salah satu anggotanya di bawah LoD. Ketika Anda melakukannya, Anda harus menjelaskan apakah anggota itu ramah dengan kelas Anda karena jika tidak, beberapa klien dapat mencoba menghubungkan Anda ke kelas itu dengan menggunakan Anda untuk mendapatkannya sebelum menggunakannya. Itu penting karena sekarang apa yang Anda kembalikan harus selalu mendukung penggunaan itu.
Ada banyak kasus di mana ini bukan masalah. Koleksi dapat mengabaikan ini hanya karena mereka dimaksudkan untuk berteman dengan semua yang menggunakannya. Demikian pula benda bernilai ramah dengan semua orang. Tetapi jika Anda menulis alamat yang memvalidasi utilitas yang menuntut objek karyawan tempat ia mengekstraksi alamat, daripada hanya meminta alamatnya, Anda sebaiknya berharap bahwa karyawan dan alamat keduanya berasal dari perpustakaan yang sama.
sumber
settings.darkTheme().monospaceFont()
hanyalah gula sintaksis untuksettings.darkTheme(); settings.monospaceFont();
Saya tidak mengerti argumen ini. Objek apa pun dapat mengembalikan objek sebagai hasil dari invokasi pesan. Terutama jika Anda berpikir dalam "semuanya sebagai objek".
Namun, kelas adalah satu-satunya objek yang dapat membuat instance objek baru dengan mengirimkannya pesan "baru" (atau sering diganti dengan kata kunci baru pada bahasa OOP yang paling umum)
Ngomong-ngomong, mengenai pertanyaan Anda, saya agak curiga tentang objek yang mengembalikan salah satu propertinya untuk digunakan di tempat lain. Bisa jadi objek ini akan membocorkan informasi tentang negara atau rincian implementasinya.
Dan Anda tidak ingin objek C tahu tentang detail implementasi B. Ini adalah ketergantungan yang tidak diinginkan pada objek A dari perspektif objek C.
Jadi Anda mungkin ingin bertanya pada diri sendiri apakah C, dengan mengetahui A, tidak tahu terlalu banyak untuk pekerjaan yang harus ia lakukan, terlepas dari LOD.
Ada kasus-kasus di mana ini bisa sah, misalnya, meminta objek koleksi untuk salah satu itemnya sesuai dan tidak melanggar aturan Demeter. Sebaliknya, meminta objek Buku untuk objek koneksi SQLC, berisiko dan harus dihindari.
LOD ada karena banyak programmer cenderung meminta informasi objek sebelum melakukan pekerjaan di luar itu. LOD ada di sana untuk mengingatkan kita bahwa kadang-kadang (jika tidak selalu) kita hanya memperumit hal dengan menginginkan benda kita melakukan terlalu banyak. Terkadang kita hanya perlu meminta objek lain untuk melakukan pekerjaan untuk kita. LOD ada di sana untuk membuat kita berpikir dua kali tentang di mana kita menempatkan perilaku pada objek kita.
sumber
Kenapa tidak? Anda sepertinya menyarankan perlunya memberi sinyal kepada sesama programmer bahwa kelas tersebut secara khusus dimaksudkan untuk mengembalikan objek, atau tidak boleh mengembalikan objek sama sekali. Dalam bahasa di mana semuanya adalah objek, ini akan sangat membatasi pilihan Anda, karena Anda tidak akan dapat mengembalikan apa pun sama sekali.
sumber
b.getA().doSomething()
danx.doSomethingElse(b.getA())
A a = b.getA(); a.DoSomething();
- kembali ke satu titik.b.getA().doSomething()
danx.doSomethingElse(b.getA())
Anda seharusnya secara eksplisit bertanya tentang itu. Seperti berdiri, pertanyaan Anda tampaknya tentangthis.b.getA()
.A
bukan anggota dariC
, tidak diciptakan dalamC
dan tidak diberikan kepadaC
, tampaknya menggunakanA
diC
(dengan cara apapun) melanggar LOD. Menghitung titik bukan tentang LoD, melainkan hanya alat ilustratif. Dimungkinkan untuk mengkonversiDriver().getCar().getSteeringWheel().getFrontWheels().toRight()
menjadi pernyataan terpisah yang masing-masing berisi satu titik, tetapi pelanggaran LoD masih ada. Saya membaca di banyak posting di forum ini bahwa melakukan tindakan harus didelegasikan ke objek yang mengimplementasikan perilaku aktual yaitutell don't ask
. Mengembalikan nilai tampaknya bertentangan dengan itu.Tergantung pada apa yang Anda lakukan. Jika Anda mengekspos anggota kelas Anda, ketika ini bukan urusan siapa pun bahwa ini adalah anggota kelas Anda, atau apa jenis anggota itu, itu adalah hal yang buruk. Jika Anda kemudian mengubah jenis anggota itu, dan jenis fungsi mengembalikan anggota, dan setiap orang harus mengubah kode mereka, itu jahat.
Di sisi lain, jika antarmuka kelas Anda menyatakan bahwa ia akan menyediakan turunan dari kelas A yang terkenal, itu bagus. Jika Anda beruntung dan Anda dapat melakukan ini dengan memasok anggota kelas Anda, bagus untuk Anda. Jika Anda mengubah anggota itu dari menjadi A menjadi B, Anda kemudian perlu menulis ulang metode yang mengembalikan A, jelas itu sekarang harus membangun satu dan mengisinya dengan data yang tersedia, bukan hanya pengembalian satu-liner seorang anggota. Antarmuka kelas Anda tidak akan berubah.
sumber