Jadi saya baru-baru ini membuat beberapa refactoring utama pada kode saya. Salah satu hal utama yang saya coba lakukan adalah membagi kelas saya menjadi objek data dan objek pekerja. Ini terinspirasi, antara lain, oleh bagian dari Kode Bersih ini :
Hibrida
Kebingungan ini kadang-kadang menyebabkan struktur data hibrida yang tidak menguntungkan yang setengah objek dan setengah struktur data. Mereka memiliki fungsi yang melakukan hal-hal signifikan, dan mereka juga memiliki variabel publik atau pengakses publik dan mutator yang, untuk semua maksud dan tujuan, membuat variabel pribadi menjadi publik, menggoda fungsi eksternal lainnya untuk menggunakan variabel-variabel itu seperti cara program prosedural menggunakan suatu struktur data.
Hibrida seperti itu membuat sulit untuk menambahkan fungsi baru tetapi juga membuatnya sulit untuk menambahkan struktur data baru. Mereka adalah yang terburuk dari kedua dunia. Hindari membuatnya. Mereka adalah indikasi dari desain kacau yang penulis tidak yakin - atau lebih buruk, tidak tahu - apakah mereka membutuhkan perlindungan dari fungsi atau jenis.
Baru-baru ini saya melihat kode ke salah satu objek pekerja saya (yang kebetulan menerapkan Pola Pengunjung ) dan melihat ini:
@Override
public void visit(MarketTrade trade) {
this.data.handleTrade(trade);
updateRun(trade);
}
private void updateRun(MarketTrade newTrade) {
if(this.data.getLastAggressor() != newTrade.getAggressor()) {
this.data.setRunLength(0);
this.data.setLastAggressor(newTrade.getAggressor());
}
this.data.setRunLength(this.data.getRunLength() + newTrade.getLots());
}
Saya langsung berkata pada diri saya sendiri "fitur iri! Logika ini harus di Data
kelas - khususnya dalam handleTrade
metode. handleTrade
Dan updateRun
harus selalu terjadi bersama". Tetapi kemudian saya berpikir "kelas data hanyalah public
struktur data, jika saya mulai melakukan itu, maka akan muncul Objek Hibrid!"
Apa yang lebih baik dan mengapa? Bagaimana Anda memutuskan mana yang harus dilakukan?
Jawaban:
Teks yang Anda kutip memiliki saran yang bagus, meskipun saya akan mengganti "struktur data" dengan "catatan", dengan asumsi bahwa sesuatu seperti struct dimaksudkan. Catatan hanyalah kumpulan data yang bodoh. Sementara mereka mungkin bisa berubah (dan dengan demikian stateful dalam pola pikir fungsional-pemrograman), mereka tidak memiliki keadaan internal, tidak ada invarian yang harus dilindungi. Ini benar-benar valid untuk menambahkan operasi ke catatan yang membuat penggunaannya lebih mudah.
Sebagai contoh, kita dapat berpendapat bahwa vektor 3D adalah catatan bodoh. Namun, itu seharusnya tidak mencegah kita menambahkan metode seperti
add
, yang membuat menambahkan vektor lebih mudah. Menambahkan perilaku tidak mengubah rekaman (tidak begitu bodoh) menjadi hibrida.Baris ini dilintasi ketika antarmuka publik dari suatu objek memungkinkan kita untuk memecah enkapsulasi: Ada beberapa internal yang dapat kita akses secara langsung, sehingga membawa objek ke keadaan tidak valid. Bagi saya yang
Data
memang memiliki keadaan, dan itu dapat dibawa ke keadaan tidak valid:Jika ada negara yang valid untuk data Anda, maka semuanya baik-baik saja dengan kode Anda, dan Anda dapat melanjutkan. Sebaliknya:
Data
Kelas bertanggung jawab atas konsistensi datanya sendiri. Jika menangani perdagangan selalu melibatkan memperbarui agresor, perilaku ini harus menjadi bagian dariData
kelas. Jika mengubah agresor melibatkan pengaturan panjang lari ke nol, perilaku ini harus menjadi bagian dariData
kelas.Data
tidak pernah menjadi catatan bodoh. Anda telah membuatnya menjadi hybrid dengan menambahkan setter publik.Ada satu skenario di mana Anda dapat mempertimbangkan untuk melonggarkan tanggung jawab yang ketat ini: Jika
Data
bersifat pribadi untuk proyek Anda dan karenanya bukan bagian dari antarmuka publik, Anda masih dapat memastikan penggunaan kelas yang tepat. Namun, ini menempatkan tanggung jawab untuk menjagaData
konsistensi di seluruh kode, daripada mengumpulkannya di lokasi pusat.Baru-baru ini saya menulis jawaban tentang enkapsulasi , yang membahas lebih dalam tentang apa itu enkapsulasi dan bagaimana Anda dapat memastikannya.
sumber
Fakta bahwa
handleTrade()
danupdateRun()
selalu terjadi bersama-sama (dan metode kedua sebenarnya pada pengunjung dan memanggil beberapa metode lain pada objek data) berbau kopling temporal . Ini berarti bahwa Anda harus memanggil metode dalam urutan tertentu, dan saya kira metode memanggil yang rusak akan merusak sesuatu yang terburuk, atau gagal memberikan hasil yang berarti di terbaik. Tidak baik.Biasanya cara yang benar untuk memperbaiki ketergantungan yang keluar adalah untuk setiap metode untuk mengembalikan hasil yang dapat dimasukkan ke metode berikutnya atau ditindaklanjuti secara langsung.
Kode lama:
Kode baru:
Ini memiliki beberapa keunggulan:
sumber
updateRun
metode ini bersifat pribadi . Menghindari penggabungan berurutan adalah saran yang bagus, tetapi itu hanya berlaku untuk desain API / antarmuka publik, dan tidak untuk detail implementasi. Pertanyaan sebenarnya tampaknya apakahupdateRun
harus di pengunjung atau di kelas data, dan saya tidak melihat bagaimana jawaban ini mengatasi masalah itu.updateRun
tidak relevan, yang penting adalah implementasithis.data
yang tidak ada dalam pertanyaan dan objek yang dimanipulasi oleh objek pengunjung.Dari sudut pandang saya kelas harus mengandung "nilai untuk negara (variabel anggota) dan implementasi perilaku (fungsi anggota, metode)".
"Struktur data hibrid yang tidak menguntungkan" muncul jika Anda membuat variabel-variabel anggota negara bagian (atau pengambil / penentu mereka) publik yang tidak boleh publik.
Jadi saya melihat tidak perlu memiliki kelas yang terpisah untuk objek data data dan objek pekerja.
Anda harus dapat menjaga variabel negara-anggota-non-publik (layer database Anda harus mampu menangani variabel-anggota-non-publik)
Cemburu fitur adalah kelas yang menggunakan metode kelas lain secara berlebihan. Lihat Code_smell . Memiliki kelas dengan metode dan status akan menghilangkan ini.
sumber