Menurut hukum Demeter, apakah kelas diperbolehkan untuk mengembalikan salah satu anggotanya?


13

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)?


9
Banyak orang (pemrogram fungsional khususnya) memandang Hukum Demeter sebagai anti-pola. Yang lain melihatnya sebagai "Saran Demeter yang Kadang-kadang Berguna."
David Hammen

1
Saya akan menjawab pertanyaan ini karena mengilustrasikan absurditas Hukum Demeter. Ya, LoD "kadang-kadang berguna", tetapi biasanya bodoh.
user949300

Bagaimana cara xmengatur?
candied_orange

@CandiedOrange xhanyalah properti berbeda yang nilainya hanya beberapa objek yang dapat dipanggil doSomethingElse.
user2180613

1
Contoh Anda tidak melanggar LOD. LOD adalah tentang mencegah klien dari suatu objek dari penggabungan ke objek detail internal atau kolaborator. Anda ingin menghindari hal-hal seperti user.currentSession.isLoggedIn(). karena pasangan klien useruntuk tidak hanya usertetapi juga kolaboratornya session,. Sebaliknya Anda ingin dapat menulis user.isLoggedIn(). ini biasanya dicapai dengan menambahkan isLoggedInmetode useryang didelegasikan kepada implementasinya currentSession.
Jonah

Jawaban:


7

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.


1
Apakah DDD memberikan aturan kapan anggota kelas seperti data dapat diekspos (mis. Pengambil harus ada)? Semua diskusi di sekitar Law of Demeter, tell, don't ask, getters are evil, object encapsulationdan single responsibility principletampaknya 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.
user2180613

Pedoman seperti "getter is evil" ada karena banyak orang memperlakukan instance kelas sebagai struktur data (Satu tas data untuk diedarkan). Ini bukan pemrograman berorientasi objek. Objek adalah kumpulan fungsionalitas / perilaku yang sebenarnya menyediakan beberapa pekerjaan. Lingkup pekerjaan ditentukan oleh tanggung jawab. Pekerjaan ini jelas akan membuat objek yang dikembalikan dari metode, dll. Jika kelas Anda benar-benar melakukan pekerjaan yang bermakna yang Anda dapat dengan jelas mendefinisikan di luar "mewakili objek data X dengan atribut Y dan Z", maka Anda berada di jalur yang benar . Saya tidak akan stres karenanya.
Jeremy

Untuk memperluas pada titik itu, ketika Anda menggunakan banyak getter / penanya, itu biasanya berarti Anda memiliki beberapa metode besar di suatu tempat yang mengatur semuanya, yang Fowler menyebut skrip transaksi. Sebelum Anda mulai menggunakan pengambil, tanyakan pada diri sendiri ... mengapa saya meminta data ini dari objek? Mengapa fungsi ini tidak menggunakan metode pada objek? Jika bukan tanggung jawab kelas ini untuk mengimplementasikan fungsi ini, maka silakan dan gunakan pengambil. Buku [Refactoring] ( martinfowler.com/books/refactoring.html ) oleh Fowler adalah buku tentang heuristik untuk pertanyaan semacam itu.
Jeremy

Dalam bukunya Implementing Domain-Driven Design, Vaughn Vernon menyebutkan Hukum Demeter dan Tell, Don't Ask dalam Bab 10, hlm. 382. Mungkin patut dilihat jika Anda memiliki akses ke sana.
Jeremy

6

Menurut hukum Demeter, apakah kelas diperbolehkan untuk mengembalikan salah satu anggotanya?

Ya itu pasti.

Mari kita lihat poin-poin utama :

  • Setiap unit harus memiliki pengetahuan yang terbatas tentang unit lain: hanya unit "erat" yang terkait dengan unit saat ini.
  • Setiap unit hanya boleh berbicara dengan teman-temannya; jangan berbicara dengan orang asing.
  • Hanya berbicara dengan teman dekat Anda.

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.

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) ?

Ini hanya menciptakan peluang untuk melanggar demeter. Ini bukan pelanggaran. Ini bahkan belum tentu buruk.

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 (2)?

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.

Dalam kode semu:

Saya menduga bahwa hukum Demeter melarang pola seperti di atas. Apa yang dapat saya lakukan untuk memastikan doSomethingElse () dapat dipanggil tanpa melanggar hukum (3)?

Sebelum saya berbicara tentang x.doSomethingElse(a)mengerti bahwa Anda pada dasarnya telah menulis

b.getA().doSomething()

Sekarang, LoD bukan latihan penghitungan titik . Tetapi ketika Anda membuat rantai Anda mengatakan bahwa Anda tahu cara mendapatkan A(dengan menggunakan B) dan Anda tahu cara menggunakan A. Baiklah sekarang Adan Blebih baik berteman dekat karena Anda baru saja menggabungkan mereka.

Jika Anda baru saja meminta sesuatu untuk memberi Anda Asesuatu yang dapat Anda gunakan Adan tidak akan peduli dari mana asalnya dan Bbisa hidup bahagia dan bebas dari obsesi Anda untuk mendapatkan Adari B.

Adapun x.doSomethingElse(a)tanpa rincian dari mana xasalnya, 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.


Antarmuka yang lancar bukan pengecualian untuk LOD karena beberapa pengertian tentang keramahan .... Dalam antarmuka yang lancar, masing-masing metode dalam rantai dipanggil pada objek yang sama, karena semuanya kembali sendiri. settings.darkTheme().monospaceFont()hanyalah gula sintaksis untuksettings.darkTheme(); settings.monospaceFont();
Jonah

3
@Jonah Returning self adalah keramahan tertinggi. Dan tidak semua antarmuka lancar melakukannya. Banyak iDSL juga fasih dan mengembalikan jenis yang berbeda untuk mengubah metode yang tersedia di berbagai titik. Pertimbangkan jOOQ , langkah pembangun , pembangun penyihir , dan sendiri mimpi buruk saya pembangun rakasa . Lancar adalah gaya. Bukan sebuah pola.
candied_orange

2

Terlepas dari kelas yang secara khusus ditunjuk untuk mengembalikan objek [...]

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.


0

Terlepas dari kelas yang secara khusus ditunjuk untuk mengembalikan objek - seperti kelas pabrik dan pembangun - apakah boleh metode untuk mengembalikan objek?

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.


Contohnya adalah tentang implisit b.getA().doSomething()danx.doSomethingElse(b.getA())
user2180613

A a = b.getA(); a.DoSomething();- kembali ke satu titik.
Robert Harvey

2
@ user2180613 - Jika Anda ingin bertanya b.getA().doSomething()dan x.doSomethingElse(b.getA())Anda seharusnya secara eksplisit bertanya tentang itu. Seperti berdiri, pertanyaan Anda tampaknya tentang this.b.getA().
David Hammen

1
Sebagai Abukan anggota dari C, tidak diciptakan dalam Cdan tidak diberikan kepada C, tampaknya menggunakan Adi C(dengan cara apapun) melanggar LOD. Menghitung titik bukan tentang LoD, melainkan hanya alat ilustratif. Dimungkinkan untuk mengkonversi Driver().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 yaitu tell don't ask. Mengembalikan nilai tampaknya bertentangan dengan itu.
user2180613

1
Saya kira itu benar tetapi tidak menjawab pertanyaan.
user2180613

0

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.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.