Saya membaca tentang bau kode umum dalam buku Refactoring Martin Fowler . Dalam konteks itu, saya bertanya-tanya tentang pola yang saya lihat di basis kode, dan apakah orang dapat secara objektif menganggapnya sebagai anti-pola.
Pola adalah satu tempat objek dilewatkan sebagai argumen ke satu atau lebih metode, yang semuanya mengubah keadaan objek, tetapi tidak ada yang mengembalikan objek. Jadi ia mengandalkan sifat referensi pass by (dalam hal ini) C # /. NET.
var something = new Thing();
// ...
Foo(something);
int result = Bar(something, 42);
Baz(something);
Saya menemukan bahwa (terutama ketika metode tidak dinamai dengan tepat) saya perlu melihat ke metode tersebut untuk memahami jika keadaan objek telah berubah. Itu membuat pemahaman kode lebih kompleks, karena saya perlu melacak beberapa level dari panggilan-tumpukan.
Saya ingin mengusulkan untuk meningkatkan kode tersebut untuk mengembalikan objek lain (dikloning) dengan status baru, atau apa pun yang diperlukan untuk mengubah objek di situs panggilan.
var something1 = new Thing();
// ...
// Let's return a new instance of Thing
var something2 = Foo(something1);
// Let's use out param to 'return' other info about the operation
int result;
var something3 = Bar(something2, out result);
// If necessary, let's capture and make explicit complex changes
var changes = Baz(something3)
something3.Apply(changes);
Bagi saya sepertinya pola pertama dipilih berdasarkan asumsi
- bahwa itu kurang berfungsi, atau membutuhkan lebih sedikit baris kode
- bahwa itu memungkinkan kita untuk mengubah objek, dan mengembalikan beberapa informasi lainnya
- bahwa itu lebih efisien karena kita memiliki lebih sedikit contoh
Thing
.
Saya mengilustrasikan suatu alternatif, tetapi untuk mengusulkannya, orang perlu memiliki argumen yang menentang solusi asli. Apa, jika ada, argumen yang dapat dibuat untuk membuat kasus bahwa solusi asli adalah anti-pola?
Dan apa, jika ada, yang salah dengan solusi alternatif saya?
sumber
Jawaban:
Ya, solusi asli adalah anti-pola karena alasan yang Anda jelaskan: membuat sulit untuk berpikir tentang apa yang sedang terjadi, objek tidak bertanggung jawab atas keadaan / implementasinya sendiri (melanggar enkapsulasi). Saya juga akan menambahkan bahwa semua perubahan negara adalah kontrak implisit dari metode ini, membuat metode itu rapuh dalam menghadapi perubahan persyaratan.
Yang mengatakan, solusi Anda memiliki beberapa kelemahan sendiri, yang paling jelas adalah bahwa objek kloning tidak hebat. Ini bisa lambat untuk benda besar. Ini dapat menyebabkan kesalahan di mana bagian lain dari kode berpegang pada referensi lama (yang kemungkinan terjadi dalam basis kode yang Anda uraikan). Membuat objek-objek tersebut secara eksplisit tidak dapat diselesaikan menyelesaikan setidaknya beberapa masalah ini, tetapi merupakan perubahan yang lebih drastis.
Kecuali benda-benda itu kecil dan agak sementara (yang menjadikannya kandidat yang baik untuk kekekalan), saya akan cenderung untuk memindahkan lebih banyak transisi negara ke objek itu sendiri. Itu memungkinkan Anda menyembunyikan detail implementasi transisi ini dan menetapkan persyaratan yang lebih kuat di sekitar siapa / apa / di mana transisi negara tersebut terjadi.
sumber
Bar(something)
(dan memodifikasi keadaansomething
), buatlahBar
anggotasomething
tipe.something.Bar(42)
lebih cenderung bermutasisomething
, sementara juga memungkinkan Anda untuk menggunakan alat OO (negara pribadi, antarmuka, dll) untuk melindungisomething
negaraSebenarnya, itu adalah bau kode asli. Jika Anda memiliki objek yang dapat diubah, ia menyediakan metode untuk mengubah kondisinya. Jika Anda memiliki panggilan ke metode semacam itu yang tertanam dalam tugas dari beberapa pernyataan lagi, tidak apa-apa untuk meringkas tugas itu ke metode sendiri - yang membuat Anda dengan tepat situasi yang dijelaskan. Tetapi jika Anda tidak memiliki nama metode seperti
Foo
danBar
, tetapi metode yang menjelaskan bahwa mereka mengubah objek, saya tidak melihat masalah di sini. Pikirkanatau
atau
atau
atau sesuatu seperti itu - Saya tidak melihat alasan di sini untuk mengembalikan objek yang dikloning untuk metode tersebut, dan juga tidak ada alasan untuk melihat penerapannya untuk memahami bahwa mereka akan mengubah keadaan objek yang dilewati.
Jika Anda tidak ingin efek samping, buat objek Anda tidak berubah, itu akan memberlakukan metode seperti yang di atas untuk mengembalikan objek yang diubah (dikloning) tanpa mengubah yang asli.
sumber
Ya, lihat http://codebetter.com/matthewpodwysocki/2008/04/30/side-effecting-functions-are-code-smells/ untuk salah satu dari banyak contoh orang yang menunjukkan bahwa efek samping yang tidak diharapkan adalah buruk.
Secara umum prinsip dasarnya adalah bahwa perangkat lunak dibangun berlapis-lapis, dan setiap lapisan harus menyajikan abstraksi paling bersih untuk yang berikutnya. Dan abstraksi yang bersih adalah abstraksi di mana Anda harus menyimpan sesedikit mungkin dalam pikiran untuk menggunakannya. Itu disebut modularitas, dan berlaku untuk semuanya, mulai dari fungsi tunggal hingga protokol jaringan.
sumber
ForEach<T>
terjadi.Pertama-tama, ini tidak tergantung pada "pass by reference nature of" itu tergantung pada objek yang menjadi tipe referensi yang bisa berubah. Dalam bahasa non-fungsional itu hampir selalu akan terjadi.
Kedua, apakah ini masalah atau tidak, tergantung pada objek dan seberapa erat perubahan dalam prosedur yang berbeda diikat bersama - jika Anda gagal membuat perubahan di Foo dan yang menyebabkan Bar mogok, maka itu merupakan masalah. Tidak perlu bau kode, tapi ini masalah dengan Foo atau Bar atau Sesuatu (mungkin Bar karena harus memeriksa inputnya, tetapi bisa juga Sesuatu yang dimasukkan ke dalam kondisi tidak valid yang harus dicegah).
Saya tidak akan mengatakan bahwa itu naik ke tingkat anti-pola, melainkan sesuatu yang harus diperhatikan.
sumber
Saya berpendapat ada sedikit perbedaan antara
A.Do(Something)
memodifikasisomething
dansomething.Do()
memodifikasisomething
. Dalam kedua kasus, itu harus jelas dari nama metode yang dipanggil yangsomething
akan dimodifikasi. Jika tidak jelas dari nama metode, terlepas dari apakahsomething
itu parameterthis
,, atau bagian dari lingkungan, itu tidak boleh dimodifikasi.sumber
Saya pikir tidak apa-apa untuk mengubah keadaan objek dalam beberapa skenario. Misalnya, saya memiliki daftar pengguna dan saya ingin menerapkan filter berbeda ke daftar sebelum mengembalikannya ke klien.
Dan ya, Anda bisa membuatnya cantik dengan memindahkan pemfilteran ke metode lain, sehingga Anda akan berakhir dengan sesuatu di baris:
Di mana
Filter(users)
akan menjalankan filter di atas.Saya tidak ingat di mana tepatnya saya menemukan ini sebelumnya, tapi saya pikir itu disebut sebagai pipa penyaringan.
sumber
Saya tidak yakin apakah solusi baru yang diusulkan (menyalin objek) adalah sebuah pola. Masalahnya, seperti yang telah Anda tunjukkan, adalah tata nama fungsi yang buruk.
Katakanlah saya menulis operasi matematika yang kompleks sebagai fungsi f () . Saya mendokumentasikan bahwa f () adalah fungsi yang memetakan
NXN
keN
, dan algoritma di belakangnya. Jika fungsi tersebut dinamai secara tidak tepat, dan tidak didokumentasikan, dan tidak memiliki kasus pengujian yang menyertainya, dan Anda harus memahami kode tersebut, dalam hal ini kode tersebut tidak berguna.Tentang solusi Anda, beberapa pengamatan:
X
menjadiY
setelahf()
, tetapiX
sebenarnyaY
,) dan mungkin inkonsistensi temporal.Masalah yang Anda coba atasi adalah masalah yang valid; Namun, bahkan dengan rekayasa ulang yang sangat besar dilakukan, masalahnya diatasi, tidak diselesaikan.
sumber