Blogpost ini diposting di Hacker News dengan beberapa upvotes. Berasal dari C ++, sebagian besar contoh ini tampaknya bertentangan dengan apa yang telah saya ajarkan.
Seperti contoh # 2:
Buruk:
def check_for_overheating(system_monitor)
if system_monitor.temperature > 100
system_monitor.sound_alarms
end
end
versus bagus:
system_monitor.check_for_overheating
class SystemMonitor
def check_for_overheating
if temperature > 100
sound_alarms
end
end
end
Saran dalam C ++ adalah Anda harus lebih memilih fungsi bebas daripada fungsi anggota karena meningkatkan enkapsulasi. Keduanya identik secara semantik, jadi mengapa lebih suka pilihan yang memiliki akses ke lebih banyak negara?
Contoh 4:
Buruk:
def street_name(user)
if user.address
user.address.street_name
else
'No street name on file'
end
end
versus bagus:
def street_name(user)
user.address.street_name
end
class User
def address
@address || NullAddress.new
end
end
class NullAddress
def street_name
'No street name on file'
end
end
Mengapa tanggung jawab User
untuk memformat string kesalahan yang tidak terkait? Bagaimana jika saya ingin melakukan sesuatu selain mencetak 'No street name on file'
jika tidak ada jalan? Bagaimana jika jalan itu dinamai hal yang sama?
Bisakah seseorang mencerahkan saya pada keuntungan dan alasan "Katakan, Jangan Tanyakan"? Saya tidak mencari yang lebih baik, tetapi mencoba memahami sudut pandang penulis.
sumber
Jawaban:
Menanyakan objek tentang kondisinya, dan kemudian memanggil metode pada objek tersebut berdasarkan keputusan yang dibuat di luar objek, berarti bahwa objek tersebut sekarang merupakan abstraksi yang bocor; beberapa perilakunya terletak di luar objek, dan keadaan internal terpapar (mungkin tidak perlu) ke dunia luar.
http://pragprog.com/articles/tell-dont-ask
sumber
Secara umum, artikel itu menyarankan agar Anda tidak mengekspos negara anggota untuk orang lain untuk mempertimbangkan, jika Anda bisa memikirkannya sendiri .
Namun, apa yang tidak dinyatakan dengan jelas adalah bahwa undang-undang ini jatuh ke dalam batas yang sangat jelas ketika alasannya lebih dari tanggung jawab kelas tertentu. Misalnya, setiap kelas yang tugasnya memegang nilai atau memberikan nilai - terutama nilai generik, atau di mana kelas memberikan perilaku yang harus diperluas.
Misalnya, jika sistem menyediakan
temperature
sebagai kueri, maka besok, klien dapatcheck_for_underheating
tanpa harus berubahSystemMonitor
. Ini tidak terjadi ketikaSystemMonitor
mengimplementasikannyacheck_for_overheating
sendiri. Dengan demikian,SystemMonitor
kelas yang tugasnya membangkitkan alarm ketika suhu terlalu tinggi mengikuti ini- tetapiSystemMonitor
kelas yang tugasnya adalah memungkinkan sepotong kode lain untuk membaca suhu sehingga dapat mengontrol, katakanlah, TurboBoost atau sesuatu seperti itu , seharusnya tidak.Perhatikan juga bahwa contoh kedua tanpa tujuan menggunakan Null Object Anti-pattern.
sumber
check_for_underheating
tanpa harus mengubahSystemMonitor
". Bagaimana klien berbeda dariSystemMonitor
pada saat itu? Bukankah sekarang Anda menghilangkan logika pemantauan di berbagai kelas? Saya juga tidak melihat masalah dengan kelas monitor yang memberikan informasi sensor ke kelas lain sambil menyimpan fungsi alarm. Pengontrol boost harus mengendalikan boost tanpa khawatir menaikkan alarm jika suhunya terlalu tinggi.Masalah sebenarnya dengan contoh overheating Anda adalah bahwa aturan untuk apa yang memenuhi syarat sebagai overheating tidak mudah bervariasi untuk sistem yang berbeda. Misalkan Sistem A adalah seperti yang Anda miliki (suhu> 100 terlalu panas) tetapi Sistem B lebih halus (suhu> 93 terlalu panas). Apakah Anda mengubah fungsi kontrol untuk memeriksa jenis sistem, dan kemudian menerapkan nilai yang benar?
Atau apakah Anda memiliki masing-masing jenis sistem menentukan kapasitas pemanasannya?
SUNTING:
Cara sebelumnya membuat fungsi kontrol Anda menjadi jelek ketika Anda mulai berurusan dengan lebih banyak sistem. Yang terakhir memungkinkan fungsi kontrol menjadi stabil seiring berjalannya waktu.
sumber
Pertama, saya merasa saya harus menganggap karakterisasi contoh Anda sebagai "buruk" dan "baik". Artikel ini menggunakan istilah "Tidak begitu baik" dan "Lebih baik", saya pikir istilah itu dipilih karena suatu alasan: ini adalah pedoman, dan tergantung pada keadaan, pendekatan "Tidak begitu baik" mungkin tepat, atau memang satu-satunya solusi.
Ketika diberi pilihan, Anda harus memberikan preferensi untuk memasukkan fungsionalitas apa pun yang hanya bergantung pada kelas di kelas daripada di luarnya - alasannya adalah karena enkapsulasi, dan fakta bahwa itu membuatnya lebih mudah untuk mengembangkan kelas dari waktu ke waktu. Kelas ini juga melakukan pekerjaan yang lebih baik dalam mengiklankan kemampuannya daripada banyak fungsi gratis.
Terkadang Anda harus memberi tahu, karena keputusannya bergantung pada sesuatu di luar kelas atau karena itu adalah sesuatu yang tidak Anda inginkan dilakukan sebagian besar pengguna kelas. Terkadang Anda ingin memberi tahu, karena perilakunya kontra intuitif untuk kelas, dan Anda tidak ingin membingungkan sebagian besar pengguna kelas.
Misalnya, Anda mengeluh tentang alamat jalan yang mengembalikan pesan kesalahan, bukan, apa yang dilakukannya adalah memberikan nilai default. Namun terkadang nilai default tidak sesuai. Jika ini Negara Bagian atau Kota, Anda mungkin ingin default saat menetapkan catatan untuk seorang salesman atau pengambil survei, sehingga semua yang tidak dikenal pergi ke orang tertentu. Di sisi lain, jika Anda mencetak amplop, Anda mungkin lebih suka pengecualian atau penjaga yang mencegah Anda membuang-buang kertas pada surat yang tidak dapat dikirim.
Jadi mungkin ada kasus di mana "Tidak begitu baik" adalah cara untuk pergi, tetapi secara umum, "Lebih baik" adalah, well, lebih baik.
sumber
Data / Objek Anti-Simetri
Seperti yang ditunjukkan orang lain, Tell-Dont-Ask khusus untuk kasus di mana Anda mengubah status objek setelah Anda bertanya (lihat misalnya teks Pragprog yang diposting di tempat lain di halaman ini). Ini tidak selalu terjadi, misalnya objek 'pengguna' tidak berubah setelah diminta untuk penggunanya. Alamat. Karena itu dapat diperdebatkan apakah ini kasus yang sesuai untuk menerapkan Tell-Dont-Ask.
Tell-Dont-Ask berkaitan dengan tanggung jawab, dengan tidak menarik logika dari kelas yang harus dibenarkan di dalamnya. Tetapi tidak semua logika yang berhubungan dengan objek adalah logika dari objek tersebut. Ini mengisyaratkan pada tingkat yang lebih dalam, bahkan di luar Tell-Dont-Ask, dan saya ingin menambahkan komentar singkat tentang itu.
Sebagai soal desain arsitektur, Anda mungkin ingin memiliki objek yang benar-benar hanya wadah untuk properti, mungkin bahkan tidak berubah, dan kemudian menjalankan berbagai fungsi di atas koleksi objek seperti itu, mengevaluasi, memfilter atau mengubah mereka daripada mengirimkannya perintah (yang merupakan lebih banyak lagi domain Tell-Dont-Ask).
Keputusan yang lebih tepat untuk masalah Anda tergantung pada apakah Anda berharap memiliki data yang stabil (objek deklaratif) tetapi dengan mengubah / menambahkan di sisi fungsi. Atau jika Anda berharap memiliki seperangkat fungsi yang stabil dan terbatas tetapi mengharapkan lebih banyak fluks pada tingkat objek, misalnya dengan menambahkan tipe baru. Dalam situasi pertama Anda lebih suka fungsi bebas, dalam metode objek kedua.
Bob Martin, dalam bukunya "Clean Code", menyebut ini "Data / Object Anti-Symmetry" (hal.95 dst), komunitas lain mungkin menyebutnya sebagai " masalah ekspresi ".
sumber
Paradigma ini kadang-kadang disebut sebagai 'Katakan, jangan tanya' , artinya beri tahu objek apa yang harus dilakukan, jangan tanya tentang keadaannya; dan terkadang sebagai 'Tanya, jangan katakan' , artinya minta objek untuk melakukan sesuatu untuk Anda, jangan katakan seperti apa keadaannya. Apa pun cara terbaik dalam praktik yang sama - cara suatu objek harus melakukan tindakan adalah perhatian objek itu, bukan objek pemanggil. Antarmuka harus menghindari mengekspos negara mereka (misalnya melalui pengakses atau properti publik) dan bukannya mengekspos metode 'melakukan' yang implementasinya buram. Yang lain telah membahas hal ini dengan tautan ke programmer pragmatis.
Aturan ini terkait dengan aturan tentang menghindari kode "titik ganda" atau "panah ganda", sering disebut sebagai 'Hanya bicara dengan teman langsung', yang menyatakan
foo->getBar()->doSomething()
buruk, alih-alih menggunakanfoo->doSomething();
yang merupakan panggilan pembungkus di sekitar fungsionalitas bilah, dan diimplementasikan sebagai sederhanareturn bar->doSomething();
- jikafoo
bertanggung jawab untuk mengelolabar
, maka biarkan melakukannya!sumber
Selain jawaban baik lainnya tentang "katakan, jangan tanya", beberapa komentar tentang contoh spesifik Anda yang mungkin membantu:
Pilihan itu tidak memiliki akses ke lebih banyak negara. Mereka berdua menggunakan jumlah negara yang sama untuk melakukan pekerjaan mereka, tetapi contoh 'buruk' mengharuskan negara kelas menjadi publik untuk melakukan pekerjaannya. Lebih lanjut, perilaku kelas itu dalam contoh 'buruk' tersebar ke fungsi bebas, membuatnya lebih sulit untuk ditemukan dan lebih sulit untuk refactor.
Mengapa 'street_name' bertanggung jawab untuk melakukan keduanya 'mendapatkan nama jalan' dan 'memberikan pesan kesalahan'? Setidaknya dalam versi 'baik', masing-masing bagian memiliki satu tanggung jawab. Namun, itu bukan contoh yang bagus.
sumber
Jawaban-jawaban ini sangat bagus, tetapi berikut ini contoh lain yang perlu ditekankan: perhatikan bahwa ini biasanya cara untuk menghindari duplikasi. Misalnya, katakan Anda memiliki beberapa tempat dengan kode seperti:
Itu berarti Anda sebaiknya memiliki sesuatu seperti ini:
Karena duplikasi itu berarti sebagian besar klien dari antarmuka Anda akan menggunakan metode baru, alih-alih mengulangi logika yang sama di sana-sini. Anda memberi delegasi pekerjaan yang ingin Anda lakukan, alih-alih meminta info yang Anda butuhkan untuk melakukannya sendiri.
sumber
Saya percaya ini lebih benar ketika menulis objek tingkat tinggi, tetapi kurang benar ketika turun ke tingkat yang lebih dalam misalnya perpustakaan kelas karena tidak mungkin untuk menulis setiap metode tunggal untuk memuaskan semua konsumen kelas.
Misalnya # 2, saya pikir ini terlalu disederhanakan. Jika kita benar-benar akan mengimplementasikan ini, SystemMonitor akan akhirnya memiliki kode untuk akses perangkat keras tingkat rendah dan logika untuk abstraksi tingkat tinggi yang tertanam di kelas yang sama. Sayangnya, jika kita mencoba memisahkannya menjadi dua kelas, kita akan melanggar "Katakan, Jangan tanya" itu sendiri.
Contoh # 4 kurang lebih sama - itu menanamkan logika UI ke dalam data tier. Sekarang jika kita akan memperbaiki apa yang ingin dilihat pengguna jika tidak ada alamat, kita harus memperbaiki objek di tingkat data, dan bagaimana jika dua proyek menggunakan objek yang sama tetapi perlu menggunakan teks yang berbeda untuk alamat null?
Saya setuju bahwa jika kita dapat menerapkan "Tell, Don't ask" untuk semuanya, itu akan sangat berguna - saya sendiri akan senang jika saya bisa mengatakan daripada bertanya (dan melakukannya sendiri) dalam kehidupan nyata! Namun, sama seperti dalam kehidupan nyata, kelayakan solusi sangat terbatas pada kelas tingkat tinggi.
sumber