Pemahaman saya saat ini tentang implementasi Warisan adalah bahwa seseorang hanya perlu memperluas kelas jika ada hubungan IS-A . Jika kelas induk lebih lanjut dapat memiliki tipe anak yang lebih spesifik dengan fungsionalitas yang berbeda tetapi akan berbagi elemen umum yang diabstraksi dalam induk.
Saya mempertanyakan pemahaman itu karena apa yang direkomendasikan oleh profesor Jawa saya untuk kita lakukan. Dia merekomendasikan untuk JSwing
aplikasi yang sedang kita buat di kelas
Satu harus memperpanjang semua JSwing
kelas ( JFrame
, JButton
, JTextBox
, dll) ke dalam kelas kustom terpisah dan menentukan kustomisasi terkait GUI di dalamnya (seperti ukuran komponen, komponen label, dll)
Sejauh ini bagus, tetapi ia lebih lanjut menyarankan bahwa setiap JButton harus memiliki kelas ekstensi kustom sendiri meskipun satu-satunya faktor pembeda adalah label mereka.
Untuk misalnya Jika GUI memiliki dua tombol, Oke dan Batalkan . Dia merekomendasikan mereka harus diperpanjang seperti di bawah ini:
class OkayButton extends JButton{
MainUI mui;
public OkayButton(MainUI mui) {
setSize(80,60);
setText("Okay");
this.mui = mui;
mui.add(this);
}
}
class CancelButton extends JButton{
MainUI mui;
public CancelButton(MainUI mui) {
setSize(80,60);
setText("Cancel");
this.mui = mui;
mui.add(this);
}
}
Seperti yang Anda lihat, satu-satunya perbedaan adalah setText
fungsi.
Jadi apakah ini praktik standar?
Btw, kursus di mana ini dibahas disebut Praktik Pemrograman Terbaik di Jawa
[Balas dari Prof]
Jadi saya membahas masalah dengan profesor dan mengangkat semua poin yang disebutkan dalam jawaban.
Pembenarannya adalah bahwa subclassing menyediakan kode yang dapat digunakan kembali sambil mengikuti standar desain GUI. Misalnya jika pengembang telah menggunakan tombol Okay
dan kustom Cancel
di satu Window, akan lebih mudah untuk menempatkan tombol yang sama di Windows lain juga.
Saya mendapatkan alasan saya kira, tapi tetap saja hanya mengeksploitasi warisan dan membuat kode rapuh.
Kemudian, setiap pengembang sengaja bisa memanggil setText
pada Okay
tombol dan mengubahnya. Subkelas hanya menjadi gangguan dalam kasus itu.
JButton
dan panggil metode publik dalam konstruktornya, ketika Anda bisa membuatJButton
dan memanggil metode publik yang sama di luar kelas?Jawaban:
Ini melanggar Prinsip Substitusi Liskov karena
OkayButton
tidak dapat diganti di tempat yangButton
diharapkan. Misalnya, Anda dapat mengubah label tombol apa saja sesuka Anda. Tetapi melakukan itu denganOkayButton
melanggar invarian internalnya.Ini adalah penyalahgunaan klasik warisan untuk penggunaan kembali kode. Gunakan metode pembantu sebagai gantinya.
Alasan lain untuk tidak melakukan ini adalah bahwa ini hanyalah cara berbelit-belit untuk mencapai hal yang sama dengan kode linier.
sumber
OkayButton
memiliki invarian yang Anda pikirkan. TheOkayButton
dapat mengklaim tidak memiliki invarian tambahan, itu hanya dapat menganggap teks sebagai default dan bermaksud untuk sepenuhnya mendukung modifikasi pada teks. Masih bukan ide yang baik, tetapi bukan karena alasan itu.activateButton(Button btn)
mengharapkan sebuah tombol, Anda dapat dengan mudah memberikannyaOkayButton
. Prinsipnya tidak berarti bahwa ia harus memiliki fungsionalitas yang persis sama, karena ini akan membuat warisan menjadi sangat tidak berguna.setText(button, "x")
karena itu melanggar invarian (diasumsikan). Memang benar bahwa OkayButton tertentu ini mungkin tidak memiliki invarian yang menarik, jadi saya kira saya mengambil contoh yang buruk. Tapi mungkin saja. Misalnya, bagaimana jika penangan klik mengatakanif (myText == "OK") ProcessOK(); else ProcessCancel();
? Maka teks adalah bagian dari invarian.Benar-benar mengerikan dalam segala hal yang mungkin. Paling- paling , gunakan fungsi pabrik untuk menghasilkan JButtons. Anda hanya boleh mewarisi dari mereka jika Anda memiliki beberapa kebutuhan ekstensi serius.
sumber
JButton
,JCheckBox
,JRadioButton
, hanya konsesi untuk pengembang menjadi akrab dengan toolkit lain, seperti AWT polos, di mana jenis tersebut standar. Pada prinsipnya, mereka semua dapat ditangani oleh kelas tombol tunggal. Kombinasi dari model dan delegasi UI yang membuat perbedaan nyata.Ini adalah cara menulis kode Swing yang sangat tidak standar. Biasanya, Anda jarang membuat subkelas komponen Swing UI, paling umum JFrame (untuk mengatur windows anak dan pengendali acara), tetapi bahkan subklas tidak diperlukan dan tidak disarankan oleh banyak orang. Memberikan kustomisasi teks ke tombol dan sebagainya biasanya dilakukan dengan memperluas kelas AbstractAction (atau antarmuka Action yang disediakannya). Ini dapat memberikan teks, ikon, dan kustomisasi visual lain yang diperlukan dan menautkannya ke kode aktual yang diwakilinya. Ini adalah cara yang jauh lebih baik untuk menulis kode UI daripada contoh yang Anda tunjukkan.
(Ngomong-ngomong, google scholar belum pernah mendengar makalah yang Anda kutip - apakah Anda memiliki referensi yang lebih tepat?)
sumber
JPanel
. Ini sebenarnya lebih berguna untuk subkelas dari ituJFrame
, karena panel hanyalah wadah abstrak.IMHO, Praktik Pemrograman Terbaik di Jawa didefinisikan oleh buku Joshua Bloch, "Java Efektif." Sangat bagus bahwa guru Anda memberi Anda latihan OOP dan penting untuk belajar membaca dan menulis gaya pemrograman orang lain. Tetapi di luar buku Josh Bloch, pendapat cukup beragam tentang praktik terbaik.
Jika Anda akan memperpanjang kelas ini, Anda mungkin juga memanfaatkan warisan. Buat kelas MyButton untuk mengelola kode umum dan sub-kelas untuk bagian variabel:
Kapan ini ide yang bagus? Saat Anda menggunakan tipe yang Anda buat! Misalnya, jika Anda memiliki fungsi untuk membuat jendela sembulan dan tanda tangannya adalah:
Jenis yang baru saja Anda buat tidak ada gunanya sama sekali. Tapi:
Sekarang Anda telah membuat sesuatu yang bermanfaat.
Compiler memverifikasi bahwa showPopUp mengambil OkButton dan CancelButton. Seseorang yang membaca kode tahu bagaimana fungsi ini dimaksudkan untuk digunakan karena dokumentasi semacam ini akan menyebabkan kesalahan waktu kompilasi jika keluar dari tanggal. Ini adalah manfaat UTAMA. Studi empiris 1 atau 2 dari manfaat keselamatan jenis menemukan bahwa pemahaman kode manusia adalah satu-satunya manfaat yang dapat diukur.
Ini juga mencegah kesalahan saat Anda membalik urutan tombol yang Anda lewati ke fungsi. Kesalahan ini sulit dikenali, tetapi juga jarang, jadi ini adalah manfaat MINOR. Ini digunakan untuk menjual keamanan tipe, tetapi belum secara empiris terbukti sangat berguna.
Bentuk pertama dari fungsi ini lebih fleksibel karena akan menampilkan dua tombol. Terkadang itu lebih baik daripada membatasi tombol seperti apa yang akan diambil. Ingatlah bahwa Anda harus menguji fungsi tombol apa saja dalam lebih banyak keadaan - jika hanya berfungsi ketika Anda melewatinya dengan tepat tombol yang tepat, Anda tidak melakukan bantuan apa pun kepada siapa pun dengan berpura-pura akan mengambil tombol apa pun.
Masalah dengan Pemrograman Berorientasi Objek adalah menempatkan keranjang di depan kuda. Anda harus menulis fungsi terlebih dahulu untuk mengetahui tanda tangan apa yang perlu diketahui jika perlu membuat jenis untuk tanda tangan tersebut. Tetapi di Jawa, Anda harus membuat tipe Anda terlebih dahulu sehingga Anda dapat menulis fungsi tanda tangan.
Untuk alasan ini, Anda mungkin ingin menulis beberapa kode Clojure untuk melihat betapa hebatnya menulis fungsi Anda terlebih dahulu. Anda dapat membuat kode dengan cepat, memikirkan kompleksitas asimptotik sebanyak mungkin, dan asumsi Clojure tentang immutabilitas mencegah bug sebanyak jenis di Jawa.
Saya masih penggemar tipe statis untuk proyek besar dan sekarang menggunakan beberapa utilitas fungsional yang memungkinkan saya untuk menulis fungsi terlebih dahulu dan beri nama tipe saya nanti di Jawa . Hanya pemikiran saja.
PS Saya membuat
mui
pointer final - tidak perlu bisa berubah.sumber
Saya berani bertaruh bahwa guru Anda tidak benar-benar percaya bahwa Anda harus selalu memperluas komponen Swing untuk menggunakannya. Saya yakin mereka hanya menggunakan ini sebagai contoh untuk memaksa Anda berlatih kelas ekstensi. Saya tidak akan terlalu khawatir tentang praktik terbaik dunia nyata dulu.
Karena itu, di dunia nyata kami lebih menyukai komposisi daripada warisan .
Aturan Anda "satu harus hanya memperluas kelas jika ada hubungan IS-A" tidak lengkap. Itu harus diakhiri dengan "... dan kita perlu mengubah perilaku default kelas" dalam huruf tebal besar.
Contoh Anda tidak cocok dengan kriteria itu. Anda tidak harus
extend
denganJButton
kelas hanya untuk mengatur teks-nya. Anda tidak harusextend
denganJFrame
kelas hanya untuk menambah komponen untuk itu. Anda dapat melakukan hal-hal ini dengan baik menggunakan implementasi default mereka, jadi menambahkan warisan hanya menambahkan komplikasi yang tidak perlu.Jika saya melihat kelas yang kelas
extends
lain, saya akan bertanya-tanya apa kelas itu berubah . Jika Anda tidak mengubah apa pun, jangan buat saya melihat-lihat kelas sama sekali.Kembali ke pertanyaan Anda: kapan Anda harus memperpanjang kelas Java? Ketika Anda memiliki alasan yang sangat, sangat, sangat bagus untuk memperpanjang kelas.
Berikut adalah contoh spesifik: salah satu cara untuk melakukan lukisan kustom (untuk game atau animasi, atau hanya untuk komponen kustom) adalah dengan memperluas
JPanel
kelas. (Lebih jauh tentang itu di sini .) Anda memperpanjangJPanel
kelas karena Anda perlu mengesampingkan yangpaintComponent()
fungsi. Anda sebenarnya mengubah perilaku kelas dengan melakukan ini. Anda tidak dapat melakukan lukisan kustom denganJPanel
implementasi standar .Tetapi seperti yang saya katakan, guru Anda mungkin hanya menggunakan contoh-contoh ini sebagai alasan untuk memaksa Anda berlatih kelas tambahan.
sumber
Border
bahwa cat dekorasi tambahan. Atau buatJLabel
dan kirimkanIcon
implementasi kustom untuk itu. Tidak perlu subkelasJPanel
untuk melukis (dan mengapaJPanel
bukannyaJComponent
).