Saya membaca buku berjudul Rails AntiPatterns dan mereka berbicara tentang menggunakan delegasi untuk menghindari melanggar Hukum Demeter. Inilah contoh utama mereka:
Mereka percaya bahwa memanggil sesuatu seperti ini di controller itu buruk (dan saya setuju)
@street = @invoice.customer.address.street
Solusi yang mereka usulkan adalah melakukan yang berikut:
class Customer
has_one :address
belongs_to :invoice
def street
address.street
end
end
class Invoice
has_one :customer
def customer_street
customer.street
end
end
@street = @invoice.customer_street
Mereka menyatakan bahwa karena Anda hanya menggunakan satu titik, Anda tidak melanggar Hukum Demeter di sini. Saya pikir ini tidak benar, karena Anda masih melalui pelanggan untuk pergi melalui alamat untuk mendapatkan jalan faktur. Saya terutama mendapat ide ini dari posting blog yang saya baca:
http://www.dan-manges.com/blog/37
Dalam posting blog contoh utama adalah
class Wallet
attr_accessor :cash
end
class Customer
has_one :wallet
# attribute delegation
def cash
@wallet.cash
end
end
class Paperboy
def collect_money(customer, due_amount)
if customer.cash < due_ammount
raise InsufficientFundsError
else
customer.cash -= due_amount
@collected_amount += due_amount
end
end
end
Posting blog menyatakan bahwa meskipun hanya ada satu titik customer.cash
alih-alih customer.wallet.cash
, kode ini masih melanggar Hukum Demeter.
Sekarang dalam metode collect_money Paperboy, kami tidak memiliki dua titik, kami hanya memiliki satu di "customer.cash". Apakah delegasi ini menyelesaikan masalah kita? Tidak semuanya. Jika kita melihat perilaku itu, seorang tukang koran masih merogoh dompet pelanggan untuk mendapatkan uang tunai.
EDIT
Saya benar-benar mengerti dan setuju bahwa ini masih merupakan pelanggaran dan saya perlu membuat metode Wallet
penarikan yang disebut menangani pembayaran untuk saya dan saya harus memanggil metode itu di dalam Customer
kelas. Yang tidak saya dapatkan adalah bahwa menurut proses ini, contoh pertama saya masih melanggar Hukum Demeter karena Invoice
masih menjangkau langsung ke Customer
jalan.
Adakah yang bisa membantu saya menghilangkan kebingungan ini? Saya telah mencari selama 2 hari terakhir mencoba untuk membiarkan topik ini masuk, tetapi masih membingungkan.
sumber
Jawaban:
Contoh pertama Anda tidak melanggar hukum Demeter. Ya, dengan kode seperti berdiri, mengatakan
@invoice.customer_street
tidak terjadi untuk mendapatkan yang sama nilai yang hipotetis yang@invoice.customer.address.street
akan, tetapi pada setiap langkah dari traversal, nilai yang dikembalikan ditentukan oleh keberadaan objek bertanya - itu bukan bahwa" mencapai tukang koran ke dalam dompet pelanggan ", itu adalah" tukang koran meminta uang tunai kepada pelanggan, dan pelanggan mendapatkan uang tunai dari dompet mereka ".Ketika Anda mengatakan
@invoice.customer.address.street
, Anda mengasumsikan pengetahuan pelanggan dan alamat internal - ini adalah hal yang buruk. Ketika Anda berkata@invoice.customer_street
, Anda bertanyainvoice
, "Hei, saya suka jalan pelanggan, Anda memutuskan bagaimana Anda mendapatkannya ". Pelanggan kemudian berkata ke alamatnya, "Hei, saya suka jalan Anda , Anda memutuskan bagaimana Anda mendapatkannya ".Dorongan Demeter bukanlah 'Anda tidak akan pernah bisa mengetahui nilai dari objek yang jauh dalam grafik dari Anda "; melainkan' Anda sendiri tidak boleh melintasi jauh sepanjang grafik objek untuk mendapatkan nilai '.
Saya setuju ini mungkin tampak seperti perbedaan yang halus, tetapi pertimbangkan ini: dalam kode Demeter-compliant, berapa banyak kode yang perlu diubah ketika representasi internal suatu
address
perubahan? Bagaimana dengan kode yang tidak sesuai dengan Demeter?sumber
Contoh pertama dan kedua sebenarnya tidak terlalu sama. Sementara yang pertama berbicara tentang aturan umum "satu titik", yang kedua berbicara lebih banyak tentang hal-hal lain dalam desain OO, terutama " Katakan, Jangan tanya "
Sekali lagi, " hanya untuk perilaku, bukan untuk atribut "
Jika Anda meminta atribut, Anda seharusnya bertanya . "Hei, teman, berapa banyak uang yang kamu miliki di kantong? Tunjukkan padaku, aku akan mengevaluasi apakah kamu dapat membayar ini." Itu salah, tidak ada petugas belanja akan berperilaku seperti ini. Sebaliknya, mereka akan berkata, "Silakan bayar"
Adalah tugas pelanggan sendiri untuk mengevaluasi apakah ia harus membayar dan apakah ia dapat membayar. Dan tugas panitera selesai setelah memberitahu pelanggan untuk membayar.
Jadi, apakah contoh kedua membuktikan yang pertama salah?
Menurutku. Tidak , asalkan:
1. Anda melakukannya dengan kendala diri.
Meskipun Anda dapat mengakses semua atribut pelanggan
@invoice
melalui delegasi, Anda jarang membutuhkannya dalam kasus normal.Pikirkan tentang halaman yang menampilkan faktur di aplikasi Rails. Akan ada bagian di atas untuk menunjukkan detail pelanggan. Jadi, dalam templat faktur, akankah Anda membuat kode seperti ini?
Itu salah dan tidak efisien. Pendekatan yang lebih baik adalah
Kemudian biarkan sebagian pelanggan untuk memproses semua atribut milik pelanggan.
Jadi secara umum Anda tidak membutuhkannya. Tapi Anda mungkin memiliki halaman daftar yang menunjukkan semua faktur terbaru, ada bidang pengarahan di setiap
li
menampilkan nama pelanggan. Dalam hal ini, Anda perlu menunjukkan atribut pelanggan, dan itu benar-benar sah untuk mengkodekan templat sebagai2. Tidak ada tindakan lebih lanjut tergantung pada pemanggilan metode ini.
Dalam kasus laman daftar di atas, faktur menanyakan atribut nama pelanggan, tetapi tujuan sebenarnya adalah " perlihatkan nama Anda ", jadi pada dasarnya masih berupa perilaku tetapi bukan atribut . Tidak ada evaluasi dan tindakan lebih lanjut berdasarkan pada atribut ini seperti, jika nama Anda "Mike", saya akan menyukai Anda dan memberi Anda 30 hari lebih banyak kredit. Tidak, faktur hanya mengatakan "tunjukkan namamu", tidak lebih. Jadi itu benar-benar dapat diterima sesuai dengan aturan "Katakan Jangan Tanya" pada contoh 2.
sumber
Baca lebih lanjut di artikel kedua dan saya pikir idenya akan menjadi lebih jelas. Idenya hanya meminta pelanggan menawarkan kemampuan untuk membayar dan benar-benar menyembunyikan di mana kasing disimpan. Apakah itu bidang, anggota dompet, atau yang lainnya? Penelepon tidak tahu, tidak perlu tahu dan tidak berubah jika detail implementasi berubah.
Jadi saya pikir referensi kedua Anda adalah memberikan rekomendasi yang lebih bermanfaat.
Gagasan "satu titik" saja, adalah keberhasilan parsial, karena ia menyembunyikan beberapa detail yang mendalam, tetapi masih dalam tahap peningkatan antara komponen yang terpisah.
sumber
Kedengarannya seperti Dan mencabut contohnya dari artikel ini: The Paperboy, The Wallet, dan The Law Of Demeter
Saya mengerti bahwa contoh Anda ada di Ruby, tetapi ini harus berlaku di semua bahasa OOP.
sumber