Saya telah melihat diskusi di pertanyaan ini mengenai bagaimana kelas yang mengimplementasikan dari antarmuka akan dipakai. Dalam kasus saya, saya sedang menulis sebuah program yang sangat kecil di Jawa yang menggunakan instance dari TreeMap
, dan menurut pendapat semua orang di sana, itu harus dipakai seperti:
Map<X> map = new TreeMap<X>();
Dalam program saya, saya memanggil fungsi map.pollFirstEntry()
, yang tidak dideklarasikan di Map
antarmuka (dan beberapa lainnya yang ada di Map
antarmuka juga). Saya telah berhasil melakukan ini dengan melakukan casting ke TreeMap<X>
mana saja saya memanggil metode ini seperti:
someEntry = ((TreeMap<X>) map).pollFirstEntry();
Saya memahami keuntungan dari pedoman inisialisasi seperti dijelaskan di atas untuk program besar, namun untuk program yang sangat kecil di mana objek ini tidak akan diteruskan ke metode lain, saya akan berpikir itu tidak perlu. Namun, saya menulis kode sampel ini sebagai bagian dari aplikasi pekerjaan, dan saya tidak ingin kode saya terlihat buruk atau berantakan. Apa yang akan menjadi solusi paling elegan?
EDIT: Saya ingin menunjukkan bahwa saya lebih tertarik pada praktik pengkodean luas yang baik daripada penerapan fungsi tertentu TreeMap
. Seperti yang telah ditunjukkan beberapa jawaban (dan saya telah menandai sebagai jawaban yang pertama melakukannya), tingkat abstraksi yang lebih tinggi mungkin harus digunakan, tanpa kehilangan fungsionalitas.
sumber
Jawaban:
"Memprogram ke antarmuka" tidak berarti "menggunakan versi yang paling abstrak yang mungkin". Dalam hal ini semua orang hanya akan menggunakan
Object
.Artinya adalah bahwa Anda harus mendefinisikan program Anda terhadap abstraksi serendah mungkin tanpa kehilangan fungsionalitas . Jika Anda memerlukan
TreeMap
maka Anda perlu mendefinisikan kontrak menggunakan aTreeMap
.sumber
TreeMap
sebagai contoh. "Program ke antarmuka" tidak boleh dianggap sebagai antarmuka atau kelas abstrak - implementasi juga dapat dianggap sebagai antarmuka.public interface
daripada 'metode publik implementasi' .TreeMap
lebihSortedMap
atauNavigableMap
Jika Anda masih ingin menggunakan antarmuka, Anda bisa menggunakan
tidak perlu selalu menggunakan antarmuka tetapi sering ada titik di mana Anda ingin mengambil tampilan yang lebih umum yang memungkinkan Anda untuk mengganti implementasi (mungkin untuk pengujian) dan ini mudah jika semua referensi ke objek diabstraksikan sebagai jenis antarmuka.
sumber
Poin di balik pengkodean ke antarmuka daripada implementasi adalah untuk menghindari bocornya detail implementasi yang jika tidak akan menghambat program Anda.
Pertimbangkan situasi di mana versi asli kode Anda menggunakan
HashMap
dan memaparkannya.Ini berarti bahwa setiap perubahan
getFoo()
adalah perubahan API yang melanggar dan akan membuat orang yang menggunakannya tidak senang. Jika semua yang Anda jamin adalahfoo
Peta, Anda harus mengembalikannya.Ini memberi Anda fleksibilitas untuk mengubah cara kerja kode Anda. Anda menyadari bahwa
foo
sebenarnya perlu menjadi Peta yang mengembalikan hal-hal dalam urutan tertentu.Dan itu tidak merusak apa pun untuk sisa kode.
Anda nantinya dapat memperkuat kontrak yang diberikan kelas tanpa melanggar apa pun juga.
Ini menggali prinsip substitusi Liskov
Karena NavigableMap adalah subtipe Peta, penggantian ini dapat dilakukan tanpa mengubah program.
Mengekspos tipe implementasi menyulitkan untuk mengubah cara kerja program secara internal ketika perubahan perlu dilakukan. Ini adalah proses yang menyakitkan dan berkali-kali menciptakan solusi buruk yang hanya berfungsi untuk menimbulkan lebih banyak rasa sakit pada pembuat kode nanti (Saya melihat Anda pembuat kode sebelumnya yang terus menyeret data antara LinkedHashMap dan TreeMap karena suatu alasan - percayalah, setiap kali saya lihat namamu di svn menyalahkan aku khawatir).
Anda masih ingin menghindari bocornya jenis implementasi. Sebagai contoh, Anda mungkin ingin mengimplementasikan ConcurrentSkipListMap sebagai gantinya karena beberapa karakteristik kinerja atau Anda hanya suka
java.util.concurrent.ConcurrentSkipListMap
daripadajava.util.TreeMap
dalam pernyataan impor atau apa pun.sumber
Setuju dengan jawaban lain bahwa Anda harus menggunakan kelas (atau antarmuka) paling umum yang sebenarnya Anda perlukan sesuatu, dalam hal ini TreeMap (atau seperti seseorang yang disarankan NavigableMap). Tapi saya ingin menambahkan bahwa ini pastinya lebih baik daripada casting di mana-mana, yang akan menjadi bau yang jauh lebih besar dalam hal apapun. Lihat /programming/4167304/why-should-casting-be-avoided karena beberapa alasan.
sumber
Ini tentang mengomunikasikan maksud Anda tentang bagaimana objek harus digunakan. Misalnya, jika metode Anda mengharapkan
Map
objek dengan urutan iterasi yang dapat diprediksi :Kemudian, jika Anda benar-benar perlu memberi tahu penelepon tentang metode di atas bahwa ia juga mengembalikan
Map
objek dengan urutan iterasi yang dapat diprediksi , karena ada harapan seperti itu untuk beberapa alasan:Tentu saja, penelepon masih dapat memperlakukan objek kembali
Map
seperti itu, tetapi itu di luar ruang lingkup metode Anda:Mengambil langkah mundur
Saran umum pengkodean ke antarmuka (umumnya) dapat diterapkan, karena biasanya antarmuka yang memberikan jaminan apa yang harus dapat dilakukan objek, alias kontrak . Banyak pemula mulai dengan
HashMap<K, V> map = new HashMap<>()
dan mereka disarankan untuk menyatakan bahwa sebagaiMap
, karenaHashMap
tidak menawarkan apa pun lebih dari apa yangMap
seharusnya dilakukan. Dari ini, mereka kemudian akan dapat memahami (semoga) mengapa metode mereka harus mengambilMap
alih - alih aHashMap
, dan ini memungkinkan mereka menyadari fitur warisan di OOP.Mengutip hanya satu baris dari entri Wikipedia prinsip favorit semua orang yang terkait dengan topik ini:
Dengan kata lain, menggunakan
Map
deklarasi bukan karena masuk akal 'secara sintaksis', melainkan doa pada objek hanya harus peduli bahwa itu adalah jenisMap
.Kode pembersih
Saya menemukan bahwa ini memungkinkan saya untuk menulis kode pembersih juga, terutama ketika datang ke unit testing. Membuat
HashMap
dengan entri uji baca-saja tunggal membutuhkan lebih dari satu baris (tidak termasuk penggunaan inisialisasi penjepit ganda), ketika saya dapat dengan mudah menggantinya denganCollections.singletonMap()
.sumber