Selama implementasi pertama saya memperluas kerangka koleksi Java, saya cukup terkejut melihat bahwa antarmuka koleksi berisi metode yang dinyatakan sebagai opsional. Pelaksana diharapkan untuk membuang UnsupportedOperationExceptions jika tidak didukung. Ini langsung mengejutkan saya sebagai pilihan desain API yang buruk.
Setelah membaca banyak buku "Efektif Java" karya Joshua Bloch yang luar biasa, dan kemudian mengetahui bahwa ia mungkin bertanggung jawab atas keputusan-keputusan ini, tampaknya tidak sesuai dengan prinsip-prinsip yang dianut dalam buku ini. Saya akan berpikir mendeklarasikan dua antarmuka: Koleksi, dan MutableCollection yang memperluas Koleksi dengan metode "opsional" akan menyebabkan kode klien yang jauh lebih dapat dikelola.
Ada ringkasan yang sangat baik dari masalah di sini .
Apakah ada alasan bagus mengapa metode opsional dipilih daripada implementasi dua antarmuka?
sumber
Jawaban:
The FAQ memberikan jawabannya. Singkatnya, mereka melihat ledakan kombinatorial potensial dari antarmuka yang diperlukan dengan tampilan yang dapat dimodifikasi, tidak dapat dimodifikasi, delete-only, add-only, fixed-length, immutable (untuk threading), dan seterusnya untuk setiap set kemungkinan metode metode opsi yang diterapkan.
sumber
const
kata kunci seperti C ++.vector<int>, const vector<int>, vector<const int>, const vector<const int>
. Sejauh ini bagus, tetapi kemudian Anda mencoba mengimplementasikan grafik, dan Anda ingin membuat struktur grafik konstan, tetapi atribut simpul dapat dimodifikasi, dll.can
metode yang akan menguji apakah operasi itu mungkin? Itu akan membuat antarmuka yang sederhana dan cepat.Bagiku itu terdengar seperti yang
Interface Segregation Principle
belum dieksplorasi dengan baik seperti sekarang; cara melakukan hal-hal (yaitu antarmuka Anda mencakup semua operasi yang mungkin dan Anda memiliki metode "merosot" yang membuang pengecualian untuk yang tidak Anda butuhkan) populer sebelum SOLID dan ISP menjadi standar de-facto untuk kode kualitas.sumber
Count
koleksi tanpa harus khawatir tentang jenis item apa yang dikandungnya), tetapi dalam kerangka kerja berbasis-tipe seperti Java tidak menjadi masalah.Sementara beberapa orang mungkin membenci "metode opsional", mereka mungkin dalam banyak kasus menawarkan semantik yang lebih baik daripada antarmuka yang sangat terpisah. Di antara hal-hal lain, mereka memungkinkan untuk kemungkinan bahwa suatu objek dapat memperoleh kemampuan atau karakteristik dalam masa hidupnya, atau bahwa suatu objek (terutama objek pembungkus) mungkin tidak tahu kapan dibangun kemampuan apa yang harus dilaporkan.
Walaupun saya tidak akan menyebut paragraf kelas koleksi Java dengan desain yang bagus, saya menyarankan kerangka kerja koleksi yang baik harus menyertakan sejumlah besar metode opsional pada dasarnya dengan cara menanyakan koleksi tentang karakteristik dan kemampuannya . Desain seperti itu akan memungkinkan kelas pembungkus tunggal untuk digunakan dengan berbagai macam koleksi tanpa secara tidak sengaja mengaburkan kemampuan koleksi yang mungkin dimiliki. Jika metode tidak opsional, maka perlu memiliki kelas pembungkus yang berbeda untuk setiap kombinasi fitur yang mungkin didukung oleh koleksi, atau jika beberapa pembungkus tidak dapat digunakan dalam beberapa situasi.
Misalnya, jika koleksi mendukung penulisan item dengan indeks, atau menambahkan item di akhir, tetapi tidak mendukung memasukkan item di tengah, maka kode yang ingin merangkumnya dalam pembungkus yang akan mencatat semua tindakan yang dilakukan di atasnya akan memerlukan versi pembungkus logging yang menyediakan kombinasi tepat dari kemampuan yang didukung, atau jika tidak ada yang tersedia harus menggunakan pembungkus yang mendukung baik ditambahkan atau ditulis-oleh-indeks tetapi tidak keduanya. Namun, jika antarmuka pengumpulan terpadu menyediakan ketiga metode sebagai "opsional", tetapi kemudian menyertakan metode untuk menunjukkan metode opsional mana yang dapat digunakan, maka kelas pembungkus tunggal dapat menangani koleksi yang mengimplementasikan kombinasi fitur apa pun. Ketika ditanya fitur apa yang didukungnya, pembungkus dapat dengan mudah melaporkan apa pun yang didukung oleh koleksi yang dienkapsulasi.
Perhatikan bahwa keberadaan "kemampuan opsional" mungkin dalam beberapa kasus memungkinkan koleksi agregat untuk mengimplementasikan fungsi tertentu dengan cara yang jauh lebih efisien daripada yang mungkin jika kemampuan didefinisikan oleh keberadaan implementasi. Sebagai contoh, anggaplah sebuah
concatenate
metode digunakan untuk membentuk koleksi komposit dari dua lainnya, yang pertama adalah ArrayList dengan 1.000.000 elemen dan yang terakhir adalah koleksi dua puluh elemen yang hanya dapat diulang dari awal. Jika koleksi komposit diminta untuk elemen 1.000.013 (indeks 1.000.012), itu bisa menanyakan ArrayList berapa banyak item yang terkandung di dalamnya (yaitu 1.000.000), kurangi dari indeks yang diminta (menghasilkan 12), baca dan lewati dua belas elemen dari yang kedua koleksi, dan lalu kembalikan elemen berikutnya.Dalam situasi seperti itu, meskipun koleksi komposit tidak akan memiliki cara instan untuk mengembalikan item dengan indeks, meminta koleksi komposit untuk item 1.000.013 masih akan jauh lebih cepat daripada membaca 1.000.013 item dari itu secara individual dan mengabaikan semua kecuali yang terakhir satu.
sumber
AsXXX
metode di antarmuka dasar yang akan mengembalikan objek yang dipanggil jika itu mengimplementasikan antarmuka itu, mengembalikan objek pembungkus yang mendukung antarmuka itu jika memungkinkan, atau melempar pengecualian jika tidak. Misalnya, sebuahImmutableCollection
antarmuka mungkin memerlukan kontrak ...Saya akan mengaitkannya dengan pengembang asli hanya tidak tahu lebih baik saat itu. Kami telah melangkah jauh dalam desain OO sejak 1998 atau lebih ketika Java 2 dan Koleksi pertama kali dirilis. Apa yang tampak seperti desain buruk yang jelas sekarang tidak begitu jelas pada hari-hari awal OOP.
Tapi itu mungkin dilakukan untuk mencegah pengecoran ekstra. Jika itu adalah antarmuka kedua Anda harus membuat instance koleksi Anda untuk memanggil metode-metode opsional yang juga agak jelek. Seperti sekarang Anda akan segera menangkap UnsupportedOperationException dan memperbaiki kode Anda. Tetapi jika ada dua antarmuka Anda harus menggunakan instanceof dan casting di semua tempat. Mungkin mereka menganggap itu sebagai tradeoff yang valid. Juga kembali di Jawa 2 hari awal contoh sangat disukai karena kinerja yang lambat, mereka mungkin telah mencoba untuk mencegah penggunaannya yang berlebihan.
Tentu saja ini semua spekulasi liar, saya ragu kita bisa menjawab ini dengan pasti kecuali salah satu arsitek koleksi asli berdebat.
sumber
Collection
dan bukan aMutableCollection
, itu adalah tanda yang jelas bahwa itu tidak dimaksudkan untuk dimodifikasi. Saya tidak tahu mengapa ada orang yang perlu membuangnya. Memiliki antarmuka terpisah berarti Anda akan mendapatkan kesalahan semacam itu pada waktu kompilasi alih-alih mendapatkan pengecualian pada saat dijalankan. Semakin awal Anda mendapatkan kesalahan, semakin baik.const
objek, langsung memberi tahu pengguna bahwa objek tidak dapat dimodifikasi.