Saya memiliki dua objek yang mewakili 'Bar / Klub' (tempat Anda minum / bersosialisasi).
Dalam satu skenario saya perlu nama bar, alamat, jarak, slogon
Dalam skenario lain saya perlu nama bar, alamat, url situs web, logo
Jadi saya punya dua objek yang mewakili hal yang sama tetapi dengan bidang yang berbeda.
Saya suka menggunakan objek yang tidak dapat diubah, jadi semua bidang diatur dari konstruktor .
Salah satu opsi adalah memiliki dua konstruktor dan membatalkan bidang lainnya yaitu:
class Bar {
private final String name;
private final Distance distance;
private final Url url;
public Bar(String name, Distance distance){
this.name = name;
this.distance = distance;
this.url = null;
}
public Bar(String name, Url url){
this.name = name;
this.distance = null;
this.url = url;
}
// getters
}
Saya tidak suka ini karena Anda harus memeriksa nol ketika Anda menggunakan getter
Dalam contoh nyata saya, skenario pertama memiliki 3 bidang dan skenario kedua memiliki sekitar 10, jadi akan sangat menyakitkan memiliki dua konstruktor , jumlah bidang yang harus saya nyatakan nol dan kemudian ketika objek sedang digunakan Anda tidak akan ' t tahu di mana Bar
Anda menggunakan dan bidang apa yang akan null dan apa yang tidak.
Opsi apa lagi yang saya miliki?
Dua kelas menelepon BarPreview
dan Bar
?
Beberapa jenis warisan / antarmuka?
Hal lain yang mengagumkan?
Bar
sebagai pengidentifikasi!You should only ask practical, answerable questions based on actual problems that you face.
dan itulah yang terjadi di siniJawaban:
Pikiran saya:
"Bilah", sebagaimana diwakili dalam domain Anda, memiliki semua hal yang mungkin diperlukan di kedua tempat: nama, alamat, URL, logo, slogan, dan "jarak" (Saya menebak dari lokasi pemohon). Karenanya, di domain Anda, harus ada satu kelas "Bilah" yang merupakan sumber data otoritatif untuk satu bilah, di mana pun data akan digunakan nanti. Kelas ini harus bisa diubah, sehingga perubahan data bar dapat dibuat dan disimpan jika perlu.
Namun, Anda memiliki dua tempat di mana data objek Bar ini diperlukan, dan keduanya hanya memerlukan subset (dan Anda tidak ingin data itu diubah). Jawaban yang biasa adalah "objek transfer data" atau DTO; POJO (objek Java polos) yang berisi pengambil properti yang tidak dapat diubah. DTO ini dapat diproduksi dengan memanggil metode pada objek domain Bar utama: "toScenario1DTO ()" dan "toScenario2DTO ()"; hasilnya menjadi DTO terhidrasi (artinya Anda hanya perlu menggunakan konstruktor yang panjang dan rumit di satu tempat).
Jika Anda perlu mengirim data kembali ke kelas domain utama (untuk memperbaruinya; apa gunanya data jika Anda tidak dapat mengubahnya sesuai kebutuhan untuk mencerminkan keadaan dunia nyata saat ini?), Anda dapat membuat salah satu DTO, atau gunakan DTO yang dapat berubah baru, dan serahkan kembali ke kelas Bar menggunakan metode "updateFromDto ()".
EDIT: untuk memberikan contoh:
Kelas Alamat, Jarak dan Url harus dapat diubah sendiri, atau ketika digunakan dalam DTO, kelas tersebut harus diganti dengan rekan yang tidak dapat diubah.
sumber
Jika Anda hanya peduli pada subset properti, dan Anda ingin memastikan mereka tidak tercampur, buat dua antarmuka, dan gunakan itu untuk berbicara dengan objek dasar Anda.
sumber
Bar
kelasThe Builder Pola (atau sesuatu yang dekat dengan itu) mungkin penggunaan sini.
Memiliki objek abadi adalah hal yang patut dikagumi, tetapi kenyataannya adalah bahwa dengan Refleksi di Jawa, tidak ada yang benar-benar aman ;-).
sumber
HawaiianPizzaBuilder
kerjanya karena nilai yang dibutuhkan hardcoded. Namun bagaimana Anda bisa menggunakan pola ini jika nilai diambil dan diteruskan ke konstruktor? TheHawaiianPizzaBuilder
masih akan memiliki semua getter bahwaSpicyPizzaBuilder
memiliki begitu nol adalah mungkin. Kecuali jika Anda menggabungkan ini dengan @JarrodsNull Object Pattern
. Contoh kode denganBar
akan menyampaikan maksud AndaPoin kunci di sini adalah perbedaan antara apa "Bar" itu dan bagaimana Anda menggunakannya dalam konteks satu atau yang lain.
Bar adalah entitas tunggal di dunia nyata (atau dunia artifisial, seperti game), dan hanya SATU contoh objek yang harus mewakili itu. Kapan saja, ketika Anda tidak membuat instance dari segmen kode, tetapi memuatnya dari file konfigurasi atau database, ini akan lebih jelas.
(Untuk menjadi lebih esoteris: setiap instance Bar memiliki siklus hidup yang berbeda dari objek yang mewakilinya saat program Anda berjalan. Bahkan jika Anda memiliki kode sumber yang membuat instance itu, itu berarti bahwa entitas Bar seperti yang dijelaskan, "ada "dalam keadaan tidak aktif dalam kode sumber Anda, dan" terbangun "ketika kode itu benar-benar membuatnya dalam memori ...)
Maaf untuk awal yang panjang, tapi saya harap ini membuat poin saya jelas. Anda memiliki SATU kelas Bar yang memiliki semua atribut yang Anda perlukan, dan satu instance Bar mewakili setiap entitas Bar. Ini benar dalam kode Anda, dan terlepas dari cara Anda ingin melihat contoh yang sama dalam konteks yang berbeda .
Yang terakhir dapat diwakili oleh dua antarmuka yang berbeda , yang berisi metode akses yang diperlukan (getName (), getURL (), getDistance ()), dan kelas Bar harus mengimplementasikan keduanya. (Dan mungkin "jarak" akan berubah menjadi "lokasi", dan getDistance () menjadi perhitungan dari lokasi lain :-))
Tetapi kreasi adalah untuk entitas Bar dan bukan untuk cara Anda ingin menggunakan entitas itu: satu konstruktor, semua bidang.
Diedit: Saya dapat menulis kode! :-)
sumber
Pola yang sesuai
Apa yang Anda cari paling sering disebut sebagai
Null Object Pattern
. Jika Anda tidak suka namanya, Anda dapat menyebutnyaUndefined Value Pattern
label semantik yang sama dan berbeda. Terkadang pola ini disebutPoison Pill Pattern
.Dalam semua kasus ini, Object adalah pengganti atau pengganti
Default Value
bukannull. It doesn't replace the semantic of
null nullbut makes it easier to work with the data model in a more predictable way because
sekarang seharusnya tidak pernah menjadi keadaan yang valid.Ini adalah Pola di mana Anda memesan instance khusus dari kelas yang diberikan untuk mewakili
null
opsi sebaliknya sebagai aDefault Value
. Dengan cara ini Anda tidak perlu memeriksanull
, Anda dapat memeriksa identitas terhadapNullObject
contoh yang diketahui . Anda dapat memanggil metode dengan aman dan sejenisnya tanpa khawatirNullPointerExceptions
.Dengan cara ini Anda mengganti
null
tugas Anda denganNullObject
contoh representatif mereka dan Anda selesai.Analisis Berorientasi Objek Yang Tepat
Dengan cara ini Anda dapat memiliki kesamaan
Interface
untuk polimorfisme dan masih memiliki perlindungan dari harus khawatir tentang tidak adanya data dalam implementasi spesifik antarmuka. Jadi beberapaBar
mungkin tidak memiliki keberadaan web, dan beberapa mungkin tidak memiliki data lokasi pada saat pembangunan.Null Object Patter
memungkinkan Anda memberikan nilai default untuk masing-masingmarker
data yang untuk data yang mengatakan hal yang sama, tidak ada yang disediakan di sini, tanpa harus berurusan dengan memeriksaNullPointerException
semua tempat.Desain Berorientasi Objek Yang Tepat
Pertama, memiliki
abstract
implementasi yang merupakan set super semua atribut yang baikBar
danClub
berbagi.Kemudian Anda bisa mengimplementasikan sub kelas dari
Establishment
kelas ini dan menambahkan hanya hal-hal spesifik yang Anda butuhkan untuk masing-masing kelasBar
danClub
yang tidak berlaku untuk yang lain.Kegigihan
Objek placeholder ini jika dibangun dengan benar dapat disimpan secara transparan dalam database tanpa penanganan khusus juga.
Bukti masa depan
Jika Anda memutuskan untuk melompat pada kereta musik Inversi / Ketergantungan Injeksi nanti, pola ini membuatnya mudah untuk menyuntikkan objek penanda ini juga.
sumber
Saya pikir masalahnya adalah Anda tidak memodelkan Bar di salah satu skenario tersebut (Dan Anda memodelkan dua masalah, objek, dll.) Yang berbeda. Jika saya melihat Bar kelas, saya mengharapkan beberapa fungsi yang berkaitan dengan minuman, menu, kursi yang tersedia, dan objek Anda tidak memilikinya. Jika saya melihat perilaku objek Anda, Anda memodelkan informasi tentang suatu pendirian. Bar adalah apa yang Anda gunakan untuk saat ini, tetapi itu bukan perilaku intrinsik yang mereka terapkan. (Dalam konteks lain: Jika Anda memodelkan suatu Pernikahan, Anda akan memiliki dua variabel instan sebagai Istri orang; Suami pribadi; seorang istri adalah peran saat ini yang Anda berikan pada objek itu pada saat itu, tetapi objek tersebut masihlah seorang Pribadi). Saya akan melakukan sesuatu seperti ini:
sumber
Benar-benar tidak perlu rumit ini. Anda membutuhkan dua jenis objek yang berbeda? Buat dua kelas.
Jika Anda perlu mengoperasikannya secara merata, pertimbangkan untuk menambahkan antarmuka, atau menggunakan refleksi.
sumber
null
. Ingatnull
berarti tidak adanya data , bukan tidak adanya atribut.Jawaban saya kepada siapa pun dengan jenis masalah ini adalah memecahnya menjadi langkah-langkah yang dapat dikelola .
BarOne
danBarTwo
(atau panggil mereka berduaBar
tetapi dalam paket yang berbeda)bar
jika tidak mengubah nama kelas yang menyinggung menjadi apa yang diwakilinyainterface
atausuperclass
dengan perilaku umuminterface
atausuperclass
Anda dapat membuatbuilder
ataufactory
untuk membuat / mengambil objek implementasi Anda(4 dan 5 adalah tentang jawaban lain dari pertanyaan ini)
sumber
Anda memerlukan kelas dasar, misalnya, Lokasi yang memiliki nama dan alamat . Sekarang, Anda memiliki dua Bar kelas dan BarPreview memperpanjang Lokasi kelas dasar . Di setiap kelas Anda menginisialisasi variabel umum kelas super dan kemudian variabel unik Anda:
Dan serupa untuk kelas BarPreview ..
Jika itu membuat Anda tidur lebih nyenyak di malam hari, gantilah semua contoh Lokasi dalam kode saya dengan Apa Pun Yang Anda Pikirkan Akan Jadi Nama yang Pantas.
sumber
final
.final
bidang @ David.Bar
seharusnya tidakextend Location
meskipun itu tidak masuk akal. MungkinBar extends BaseBar
danBarPreview extends BaseBar
nama-nama ini juga tidak terdengar terlalu bagus, saya berharap untuk sesuatu yang lebih elegan