Untuk kelas dengan bidang opsional, apakah lebih baik menggunakan warisan atau properti nullable? Pertimbangkan contoh ini:
class Book {
private String name;
}
class BookWithColor extends Book {
private String color;
}
atau
class Book {
private String name;
private String color;
//when this is null then it is "Book" otherwise "BookWithColor"
}
atau
class Book {
private String name;
private Optional<String> color;
//when isPresent() is false it is "Book" otherwise "BookWithColor"
}
Kode yang tergantung pada 3 opsi ini adalah:
if (book instanceof BookWithColor) { ((BookWithColor)book).getColor(); }
atau
if (book.getColor() != null) { book.getColor(); }
atau
if (book.getColor().isPresent()) { book.getColor(); }
Pendekatan pertama terlihat lebih alami bagi saya, tetapi mungkin itu kurang mudah dibaca karena perlunya melakukan casting. Apakah ada cara lain untuk mencapai perilaku ini?
java
inheritance
class
null
Bojan VukasovicTest
sumber
sumber
Jawaban:
Itu tergantung keadaan. Contoh spesifik tidak realistis karena Anda tidak akan memiliki subclass yang dipanggil
BookWithColor
dalam program nyata.Tetapi secara umum, properti yang hanya masuk akal untuk subclass tertentu seharusnya hanya ada di subclass tersebut.
Sebagai contoh jika
Book
memilikiPhysicalBook
danDigialBook
sebagai keturunan, makaPhysicalBook
mungkin memilikiweight
properti, danDigitalBook
sebuahsizeInKb
properti. TetapiDigitalBook
tidak akan memilikiweight
dan sebaliknya.Book
tidak akan memiliki properti, karena sebuah kelas seharusnya hanya memiliki properti yang dibagikan oleh semua keturunan.Contoh yang lebih baik adalah melihat kelas dari perangkat lunak nyata. The
JSlider
komponen memiliki lapanganmajorTickSpacing
. Karena hanya slider yang memiliki "ticks", bidang ini hanya masuk akal untukJSlider
dan turunannya. Akan sangat membingungkan jika komponen saudara lain sepertiJButton
punyamajorTickSpacing
lapangan.sumber
Poin penting yang tampaknya tidak disebutkan: Dalam sebagian besar bahasa, instance kelas tidak dapat mengubah kelas mana yang merupakan instance. Jadi jika Anda memiliki buku tanpa warna, dan Anda ingin menambahkan warna, Anda harus membuat objek baru jika Anda menggunakan kelas yang berbeda. Dan kemudian Anda mungkin perlu mengganti semua referensi ke objek lama dengan referensi ke objek baru.
Jika "buku tanpa warna" dan "buku dengan warna" adalah instance dari kelas yang sama, maka menambahkan warna atau menghapus warna akan jauh lebih sedikit dari masalah. (Jika antarmuka pengguna Anda menampilkan daftar "buku dengan warna" dan "buku tanpa warna" maka antarmuka pengguna harus berubah dengan jelas, tapi saya berharap itu adalah sesuatu yang perlu Anda tangani, mirip dengan "daftar merah buku "dan" daftar buku hijau ").
sumber
Pikirkan JavaBean (seperti Anda
Book
) sebagai catatan dalam database. Kolom opsional adalahnull
ketika mereka tidak memiliki nilai, tetapi sangat legal. Karena itu, opsi kedua Anda:Apakah yang paling masuk akal. 1
Berhati-hatilah dengan bagaimana Anda menggunakan
Optional
kelas. Misalnya, bukanSerializable
, yang biasanya merupakan karakteristik JavaBean. Berikut beberapa tips dari Stephen Colebourne :Oleh karena itu, dalam kelas Anda Anda harus menggunakan
null
untuk mewakili bahwa lapangan tidak ada, tapi ketikacolor
daun yangBook
(sebagai jenis kembali) itu harus dibungkus denganOptional
.Ini memberikan desain yang jelas dan kode yang lebih mudah dibaca.
1 Jika Anda bermaksud untuk
BookWithColor
merangkum seluruh "kelas" buku yang memiliki kemampuan khusus di atas buku-buku lain, maka masuk akal untuk menggunakan warisan.sumber
Optional
kelas ke Jawa untuk tujuan yang tepat ini. Mungkin bukan OO, tapi itu tentu pendapat yang valid.Anda harus menggunakan antarmuka (bukan warisan) atau semacam mekanik properti (bahkan mungkin sesuatu yang berfungsi seperti kerangka deskripsi sumber daya).
Begitu juga
atau
Klarifikasi (edit):
Cukup menambahkan bidang opsional di kelas entitas
Terutama dengan Opsional-wrapper(edit: lihat jawaban 4castle ) Saya pikir ini (menambahkan bidang dalam entitas asli) adalah cara yang layak untuk menambahkan properti baru dalam skala kecil. Masalah terbesar dengan pendekatan ini adalah bahwa itu mungkin bekerja melawan kopling rendah.Bayangkan kelas buku Anda didefinisikan dalam proyek khusus untuk model domain Anda. Sekarang Anda menambahkan proyek lain yang menggunakan model domain untuk melakukan tugas khusus. Tugas ini membutuhkan properti tambahan di kelas buku. Entah Anda berakhir dengan warisan (lihat di bawah) atau Anda harus mengubah model domain umum untuk memungkinkan tugas baru. Dalam kasus terakhir Anda mungkin berakhir dengan banyak proyek yang semuanya tergantung pada sifat mereka sendiri ditambahkan ke kelas buku sedangkan kelas buku itu sendiri dengan cara tergantung pada proyek-proyek ini, karena Anda tidak dapat memahami kelas buku tanpa proyek tambahan.
Mengapa warisan bermasalah ketika datang ke cara untuk memberikan properti tambahan?
Ketika saya melihat contoh kelas Anda "Buku", saya berpikir tentang objek domain yang seringkali memiliki banyak bidang dan subtipe opsional. Bayangkan saja Anda ingin menambahkan properti untuk buku yang menyertakan CD. Sekarang ada empat jenis buku: buku, buku dengan warna, buku dengan CD, buku dengan warna dan CD. Anda tidak dapat menggambarkan situasi ini dengan warisan di Jawa.
Dengan antarmuka, Anda menghindari masalah ini. Anda dapat dengan mudah menyusun properti dari kelas buku tertentu dengan antarmuka. Delegasi dan komposisi akan membuatnya mudah untuk mendapatkan kelas yang Anda inginkan. Dengan warisan Anda biasanya berakhir dengan beberapa properti opsional di kelas yang hanya ada karena kelas saudara membutuhkannya.
Bacaan lebih lanjut mengapa pewarisan seringkali merupakan ide bermasalah:
Mengapa warisan umumnya dipandang sebagai hal yang buruk oleh para pendukung OOP
JavaWorld: Mengapa meluas itu jahat
Masalah dengan mengimplementasikan seperangkat antarmuka untuk menyusun seperangkat properti
Ketika Anda menggunakan antarmuka untuk ekstensi semuanya baik-baik saja selama Anda hanya memiliki satu set kecil dari mereka. Terutama ketika model objek Anda digunakan dan diperluas oleh pengembang lain, misalnya di perusahaan Anda, jumlah antarmuka akan bertambah. Dan akhirnya Anda akhirnya membuat "antarmuka properti" resmi baru yang menambahkan metode yang sudah digunakan rekan kerja Anda dalam proyek mereka untuk pelanggan X untuk kasus penggunaan yang sama sekali tidak terkait - Ugh.
sunting: Aspek penting lainnya disebutkan oleh gnasher729 . Anda sering ingin secara dinamis menambahkan properti opsional ke objek yang ada. Dengan pewarisan atau antarmuka Anda harus membuat ulang seluruh objek dengan kelas lain yang jauh dari opsional.
Ketika Anda mengharapkan ekstensi ke model objek Anda sedemikian rupa, Anda akan lebih baik dengan secara eksplisit memodelkan kemungkinan ekstensi dinamis . Saya mengusulkan sesuatu seperti di atas di mana setiap "ekstensi" (dalam hal ini properti) memiliki kelasnya sendiri. Kelas berfungsi sebagai namespace dan pengidentifikasi untuk ekstensi. Ketika Anda mengatur konvensi penamaan paket sesuai dengan cara ini memungkinkan jumlah tak terbatas ekstensi tanpa mencemari namespace untuk metode di kelas entitas asli.
Dalam pengembangan game, Anda sering menghadapi situasi di mana Anda ingin membuat perilaku dan data dalam banyak variasi. Inilah sebabnya mengapa arsitektur pola entitas-komponen-sistem menjadi cukup populer di kalangan pengembangan game. Ini juga merupakan pendekatan menarik yang mungkin ingin Anda lihat, ketika Anda mengharapkan banyak ekstensi untuk model objek Anda.
sumber
PropertiesHolder
mungkin kelas abstrak. Tapi apa yang Anda sarankan untuk implementasigetProperty
ataugetPropertyValue
? Data masih harus disimpan dalam semacam bidang contoh di suatu tempat. Atau apakah Anda menggunakan aCollection<Property>
untuk menyimpan properti alih-alih menggunakan bidang?Book
perluasanPropertiesHolder
untuk alasan kejelasan. ThePropertiesHolder
biasanya harus menjadi anggota di semua kelas yang membutuhkan fungsi ini. Implementasinya akan mengadakanMap<Class<? extends Property<?>>,Property<?>>
atau sesuatu seperti ini. Ini adalah bagian buruk dari implementasi tetapi memberikan kerangka kerja yang sangat bagus yang bahkan bekerja dengan JAXB dan JPA.Jika
Book
kelas dapat diturunkan tanpa properti warna seperti di bawah inimaka pewarisan itu masuk akal.
sumber
color
bersifat pribadi, maka setiap kelas turunan seharusnya tidak perlu khawatircolor
. Cukup tambahkan beberapa konstruktor keBook
diabaikan itucolor
sama sekali dan itu akan default untuknull
.