Menurut Kapan obsesi primitif bukan bau kode? , Saya harus membuat objek ZipCode untuk mewakili kode pos, bukan objek String.
Namun, dalam pengalaman saya, saya lebih suka melihat
public class Address{
public String zipCode;
}
dari pada
public class Address{
public ZipCode zipCode;
}
karena saya pikir yang terakhir mengharuskan saya untuk pindah ke kelas ZipCode untuk memahami program.
Dan saya percaya saya perlu bergerak di antara banyak kelas untuk melihat definisi jika setiap bidang data primitif digantikan oleh kelas, yang terasa seperti menderita masalah yo-yo (pola-anti).
Jadi saya ingin memindahkan metode ZipCode ke kelas baru, misalnya:
Tua:
public class ZipCode{
public boolean validate(String zipCode){
}
}
Baru:
public class ZipCodeHelper{
public static boolean validate(String zipCode){
}
}
sehingga hanya orang yang perlu memvalidasi kode pos yang akan bergantung pada kelas ZipCodeHelper. Dan saya menemukan "manfaat" lain dari menjaga obsesi primitif: itu membuat kelas tampak seperti bentuk serial, jika ada, misalnya: tabel alamat dengan kode kolom string zipCode.
Pertanyaan saya adalah, apakah "menghindari masalah yo-yo" (berpindah di antara definisi kelas) merupakan alasan yang sah untuk memungkinkan "obsesi primitif"?
sumber
Jawaban:
Asumsinya adalah bahwa Anda tidak perlu yo-yo ke kelas ZipCode untuk memahami kelas Alamat. Jika ZipCode dirancang dengan baik, seharusnya sudah jelas apa fungsinya hanya dengan membaca kelas Address.
Program tidak dibaca ujung ke ujung - biasanya program terlalu rumit untuk memungkinkan hal ini. Anda tidak dapat menyimpan semua kode dalam suatu program di pikiran Anda secara bersamaan. Jadi kami menggunakan abstraksi dan enkapsulasi untuk "memotong" program menjadi unit yang bermakna, sehingga Anda dapat melihat satu bagian dari program (misalkan kelas Alamat) tanpa harus membaca semua kode yang tergantung padanya.
Sebagai contoh, saya yakin Anda tidak perlu membaca kode sumber untuk String setiap kali Anda menemukan kode String.
Mengganti nama kelas dari ZipCode ke ZipCodeHelper menyarankan sekarang ada dua konsep terpisah: kode pos dan penolong kode pos. Jadi dua kali lebih kompleks. Dan sekarang sistem tipe tidak dapat membantu Anda membedakan antara string arbitrer dan kode pos yang valid karena mereka memiliki tipe yang sama. Di sinilah "obsesi" tepat: Anda menyarankan alternatif yang lebih kompleks dan kurang aman hanya karena Anda ingin menghindari jenis pembungkus sederhana di sekitar primitif.
Menggunakan primitif adalah IMHO dibenarkan dalam kasus-kasus di mana tidak ada validasi atau logika lain tergantung pada tipe khusus ini. Tetapi segera setelah Anda menambahkan logika apa pun, akan jauh lebih mudah jika logika ini dienkapsulasi dengan tipenya.
Adapun serialisasi saya pikir itu terdengar seperti batasan dalam kerangka yang Anda gunakan. Tentunya Anda harus dapat membuat serial kode pos ke string atau memetakannya ke kolom dalam database.
sumber
ZipCodeHelper
(yang saya lebih suka meneleponZipCodeValidator
) mungkin sangat baik membuat koneksi ke layanan web untuk melakukan pekerjaan itu. Itu tidak akan menjadi bagian dari tanggung jawab tunggal "memegang data kode pos". Membuat jenis sistem tidak mengizinkan kode pos yang masih dapat dicapai dengan membuatZipCode
konstruktor yang setara dengan paket-private Java dan memanggilnya denganZipCodeFactory
yang selalu memanggil validator.ZipCode
akan menjadi "struktur data" danZipCodeHelper
sebuah "objek '. Bagaimanapun, saya pikir kami sepakat bahwa kami tidak perlu melewati koneksi web ke konstruktor ZipCodeint
sebagai ID memungkinkan mengalikan ID dengan ID ...)Foo
dan terpisahFooValidator
. Kami dapat memilikiZipCode
kelas yang memvalidasi format danZipCodeValidator
yang mengenai beberapa layanan Web untuk memeriksa apakah formatZipCode
yang benar benar-benar aktual. Kita tahu bahwa kode ZIP berubah. Namun secara praktis, kita akan memiliki daftar kode ZIP yang valid yang dienkapsulasiZipCode
, atau dalam beberapa basis data lokal.Jika bisa:
Dan konstruktor untuk Kode Pos tidak:
Kemudian Anda telah merusak enkapsulasi, dan menambahkan ketergantungan yang cukup konyol ke kelas ZipCode. Jika konstruktor tidak memanggil
ZipCodeHelper.validate(...)
maka Anda telah mengisolasi logika di pulau sendiri tanpa benar-benar menegakkannya. Anda dapat membuat kode pos yang tidak valid.The
validate
Metode harus menjadi metode statis pada kelas kode pos. Sekarang pengetahuan tentang kode pos "valid" dibundel bersama dengan kelas ZipCode. Karena contoh kode Anda terlihat seperti Java, konstruktor dari ZipCode harus memberikan pengecualian jika format yang salah diberikan:Konstruktor memeriksa format dan melempar pengecualian, sehingga mencegah kode pos tidak valid dibuat, dan
validate
metode statis tersedia untuk kode lain sehingga logika memeriksa format dienkapsulasi dalam kelas ZipCode.Tidak ada "yo-yo" di varian kelas ZipCode ini. Itu hanya disebut Pemrograman Berorientasi Objek yang tepat.
Kami juga akan mengabaikan internasionalisasi di sini, yang mungkin mengharuskan kelas lain yang disebut ZipCodeFormat atau PostalService (mis. PostalService.isValidPostalCode (...), PostalService.parsePostalCode (...), dll.).
sumber
ZipCode.validate
Metode ini adalah pemeriksaan awal yang dapat dilakukan sebelum memanggil konstruktor yang melempar pengecualian.Jika Anda banyak bergulat dengan pertanyaan ini, mungkin bahasa yang Anda gunakan bukan alat yang tepat untuk pekerjaan itu? "Primitif tipe-domain" semacam ini mudah untuk diungkapkan, misalnya, F #.
Di sana Anda dapat, misalnya, menulis:
Ini sangat berguna untuk menghindari kesalahan umum, seperti membandingkan id entitas yang berbeda. Dan karena primitif yang diketik ini jauh lebih ringan daripada kelas C # atau Java, Anda akan benar-benar menggunakannya.
sumber
ZipCode
?Jawabannya sepenuhnya tergantung pada apa yang sebenarnya ingin Anda lakukan dengan kode ZIP. Berikut adalah dua kemungkinan ekstrem:
(1) Semua alamat dijamin berada di satu negara. Tidak ada pengecualian sama sekali. (Misalnya tidak ada pelanggan asing, atau tidak ada karyawan yang alamat privatnya berada di luar negeri saat mereka bekerja untuk pelanggan asing). Negara ini memiliki kode ZIP dan mereka diharapkan tidak akan pernah menjadi masalah serius (yaitu mereka tidak memerlukan input bentuk bebas) seperti "saat ini D4B 6N2, tetapi ini berubah setiap 2 minggu"). Kode ZIP digunakan tidak hanya untuk pengalamatan, tetapi untuk validasi informasi pembayaran atau tujuan serupa. - Dalam keadaan ini, kelas kode ZIP sangat masuk akal.
(2) Alamat dapat di hampir setiap negara, sehingga puluhan atau ratusan skema pengalamatan dengan atau tanpa kode ZIP (dan dengan ribuan pengecualian aneh dan kasus khusus) adalah relevan. Kode "ZIP" benar-benar hanya diminta untuk mengingatkan orang-orang dari negara-negara di mana kode ZIP digunakan untuk tidak lupa memberikan kode mereka. Alamat hanya digunakan sehingga jika seseorang kehilangan akses ke akun mereka dan mereka dapat membuktikan nama dan alamat mereka, akses akan dipulihkan. - Dalam keadaan ini, kelas kode ZIP untuk semua negara yang relevan akan menjadi upaya yang sangat besar. Untungnya mereka tidak dibutuhkan sama sekali.
sumber
Jawaban lain telah berbicara tentang pemodelan domain OO dan menggunakan tipe yang lebih kaya untuk mewakili nilai Anda.
Saya tidak setuju, terutama mengingat kode contoh yang Anda posting.
Tetapi saya juga bertanya-tanya apakah itu benar-benar menjawab judul pertanyaan Anda.
Pertimbangkan skenario berikut (diambil dari proyek aktual yang sedang saya kerjakan):
Anda memiliki aplikasi jarak jauh pada perangkat lapangan yang berbicara ke server pusat Anda. Salah satu bidang DB untuk entri perangkat adalah kode pos untuk alamat di mana perangkat bidang berada. Anda tidak peduli dengan kode pos (atau alamat lainnya dalam hal ini). Semua orang yang peduli tentang hal itu berada di sisi lain dari batas HTTP: Anda hanya menjadi satu-satunya sumber kebenaran untuk data tersebut. Tidak ada tempat dalam pemodelan domain Anda. Anda cukup merekamnya, memvalidasinya, menyimpannya, dan atas permintaan mengocoknya dalam gumpalan JSON ke poin di tempat lain.
Dalam skenario ini, melakukan banyak hal di luar memvalidasi memasukkan dengan kendala regex SQL (atau ORM yang setara) mungkin berlebihan dari varietas YAGNI.
sumber
RegexValidatedString
, berisi string itu sendiri dan regex yang digunakan untuk memvalidasinya. Tetapi kecuali setiap instance memiliki regex unik (yang mungkin tetapi tidak mungkin) ini tampaknya agak konyol dan boros memori (dan mungkin regex waktu kompilasi). Jadi Anda bisa meletakkan regex ke dalam tabel terpisah dan meninggalkan kunci pencarian di setiap contoh untuk menemukannya (yang bisa dibilang lebih buruk karena tipuan) atau Anda menemukan cara untuk menyimpannya sekali untuk setiap jenis umum pembagian nilai yang mengatur - - mis. bidang statis pada tipe domain, atau metode yang setara, seperti yang dikatakan IMSoP.The
ZipCode
abstraksi hanya bisa masuk akal jika AndaAddress
kelas tidak juga memilikiTownName
properti. Jika tidak, Anda memiliki setengah abstraksi: kode pos menunjuk kota, tetapi dua bit informasi terkait ini ditemukan di kelas yang berbeda. Itu tidak masuk akal.Namun, meskipun demikian, itu masih bukan aplikasi yang benar (atau lebih tepatnya solusi untuk) obsesi primitif; yang, seperti yang saya pahami, terutama berfokus pada dua hal:
Kasus Anda juga tidak. Alamat adalah konsep yang terdefinisi dengan baik dengan properti yang jelas diperlukan (jalan, nomor, zip, kota, negara bagian, negara, ...). Ada sedikit atau tidak ada alasan untuk memecah data ini karena memiliki tanggung jawab tunggal : menunjuk lokasi di Bumi. Alamat membutuhkan semua bidang ini agar bermakna. Setengah alamat tidak ada gunanya.
Ini adalah bagaimana Anda tahu bahwa Anda tidak perlu membagi lagi: memecahnya lebih jauh akan mengurangi niat fungsional
Address
kelas. Demikian pula, Anda tidak perluName
subclass untuk digunakan diPerson
kelas, kecualiName
(tanpa orang yang dilampirkan) adalah konsep yang bermakna dalam domain Anda. Yang mana (biasanya) tidak. Nama digunakan untuk mengidentifikasi orang, mereka biasanya tidak memiliki nilai sendiri.sumber
Name
subclass untuk digunakan diPerson
kelas, kecuali Nama (tanpa orang yang dilampirkan) adalah konsep yang bermakna dalam domain Anda ." Ketika Anda memiliki validasi khusus untuk nama-nama, nama tersebut kemudian menjadi konsep yang bermakna di domain Anda; yang secara eksplisit saya sebutkan sebagai use case yang valid untuk menggunakan subtipe. Kedua, untuk validasi kode pos, Anda memperkenalkan asumsi tambahan, seperti kode pos yang harus mengikuti format negara tertentu. Anda memulai topik yang jauh lebih luas dari maksud pertanyaan OP.Dari artikel:
Kode sumber dibaca jauh lebih sering daripada yang tertulis. Jadi, masalah yo-yo, karena harus beralih di antara banyak file adalah masalah.
Namun, tidak , masalah yo-yo terasa jauh lebih relevan ketika berhadapan dengan modul atau kelas yang sangat saling tergantung (yang saling bolak-balik). Itu adalah jenis mimpi buruk khusus untuk dibaca, dan kemungkinan apa yang ada dalam pikiran Anda tentang masalah yo-yo.
Namun - ya , menghindari terlalu banyak lapisan abstraksi itu penting!
Sebagai contoh, saya tidak setuju dengan asumsi yang dibuat dalam jawaban mmmaaa bahwa "Anda tidak perlu yo-yo untuk [(mengunjungi)] kelas ZipCode untuk memahami kelas Alamat". Pengalaman saya adalah yang Anda lakukan - setidaknya beberapa kali pertama Anda membaca kode. Namun, seperti orang lain telah mencatat, ada yang kalanya
ZipCode
kelas sesuai.YAGNI (Ya Apakah tidak Akan Butuh Ini) adalah pola yang lebih baik untuk mengikuti untuk menghindari kode Lasagna (kode dengan terlalu banyak lapisan) - abstraksi, seperti jenis dan kelas yang ada untuk membantu programmer, dan tidak boleh digunakan kecuali mereka yang sebuah pertolongan.
Saya pribadi bertujuan untuk "menyimpan baris kode" (dan tentu saja yang terkait "menyimpan file / modul / kelas", dll). Saya yakin ada beberapa orang yang akan memberi saya julukan "terobsesi primitif" - saya merasa lebih penting untuk memiliki kode yang mudah dipikirkan daripada khawatir tentang label, pola, dan anti-pola. Pilihan yang tepat kapan membuat fungsi, modul / file / kelas, atau meletakkan fungsi di lokasi umum sangat situasional. Saya bertujuan kira-kira untuk fungsi 3-100 baris, 80-500 file baris, dan "1, 2, n" untuk kode perpustakaan yang dapat digunakan kembali ( SLOC - tidak termasuk komentar atau boilerplate; Saya biasanya ingin setidaknya 1 tambahan minimum SLOC per baris wajib) boilerplate).
Sebagian besar pola positif muncul dari pengembang yang melakukan hal itu, ketika mereka membutuhkannya . Jauh lebih penting untuk mempelajari cara menulis kode yang dapat dibaca daripada mencoba menerapkan pola tanpa masalah yang sama untuk dipecahkan. Setiap pengembang yang baik dapat menerapkan pola pabrik tanpa pernah melihatnya sebelumnya dalam kasus yang tidak umum di mana itu cocok untuk masalah mereka. Saya telah menggunakan pola pabrik, pola pengamat, dan mungkin ratusan selain itu, tanpa mengetahui nama mereka (yaitu, apakah ada "pola penugasan variabel"?). Untuk percobaan yang menyenangkan - lihat berapa banyak pola GoF yang dibangun ke dalam bahasa JS - Saya berhenti menghitung setelah sekitar 12-15 pada tahun 2009. Pola Pabrik semudah mengembalikan objek dari konstruktor JS, misalnya - tidak perlu untuk sebuah WidgetFactory.
Jadi - ya , terkadang
ZipCode
kelasnya bagus. Namun, tidak , masalah yo-yo tidak sepenuhnya relevan.sumber
Masalah yo-yo hanya relevan jika Anda harus bolak-balik. Itu disebabkan oleh satu atau dua hal (terkadang keduanya):
Jika Anda dapat melihat nama dan memiliki ide yang masuk akal tentang apa yang dilakukannya, dan metode dan properti melakukan hal-hal yang cukup jelas, Anda tidak perlu melihat kode, Anda bisa menggunakannya. Itulah inti seluruh kelas di tempat pertama - mereka adalah potongan kode modular yang dapat digunakan dan dikembangkan secara terpisah. Jika Anda harus melihat apa pun selain API untuk kelas untuk melihat apa yang dilakukannya, itu paling baik merupakan kegagalan parsial.
sumber
Ingat, tidak ada peluru perak. Jika Anda menulis aplikasi yang sangat sederhana yang perlu dirayapi dengan cepat, maka string sederhana dapat melakukan pekerjaan itu. Namun dalam 98% kesempatan, Value Object seperti yang dijelaskan oleh Eric Evans dalam DDD, akan sangat cocok. Anda dapat dengan mudah melihat semua manfaat yang disediakan benda Nilai dengan membaca sekitar.
sumber