Haruskah semua metode publik dalam kelas abstrak ditandai virtual?

12

Saya baru-baru ini harus memperbarui kelas dasar abstrak pada beberapa OSS yang saya gunakan sehingga lebih dapat diuji dengan membuatnya virtual (saya tidak dapat menggunakan antarmuka karena menggabungkan dua). Ini membuat saya berpikir apakah saya harus menandai semua metode yang saya butuhkan virtual, atau jika saya harus menandai setiap metode publik / properti virtual. Saya umumnya setuju dengan Roy Osherove bahwa setiap metode harus dibuat virtual, tetapi saya menemukan artikel ini yang membuat saya berpikir apakah ini perlu atau tidak . Saya akan membatasi ini ke kelas abstrak untuk kesederhanaan, namun (apakah semua metode publik konkrit harus virtual masih bisa diperdebatkan, saya yakin).

Saya bisa melihat di mana Anda mungkin ingin mengizinkan sub-kelas untuk menggunakan metode, tetapi tidak ingin itu menimpa implementasi. Namun, selama Anda percaya bahwa Prinsip Pergantian Liskov akan diikuti, lalu mengapa Anda tidak membiarkannya diganti? Dengan menandainya sebagai abstrak, Anda memaksakan sebuah override tertentu, jadi, menurut saya semua metode publik di dalam kelas abstrak memang harus ditandai virtual.

Namun, saya ingin bertanya jika ada sesuatu yang mungkin tidak saya pikirkan. Haruskah semua metode publik dalam kelas abstrak dibuat virtual?

Justin Pihony
sumber
Ini dapat membantu: stackoverflow.com/questions/391483/…
NoChance
1
Mungkin Anda harus melihat mengapa dalam C # defaultnya bukan virtual tetapi di Jawa. Alasan mengapa mungkin akan memberi Anda wawasan tentang jawabannya.
Daniel Little

Jawaban:

9

Namun, selama Anda percaya bahwa Prinsip Pergantian Liskov akan diikuti, lalu mengapa Anda tidak membiarkannya diganti?

Sebagai contoh, karena saya ingin implementasi kerangka suatu algoritma diperbaiki, dan hanya memungkinkan bagian-bagian tertentu untuk didefinisikan kembali oleh subkelas. Ini secara luas dikenal sebagai pola Metode Templat (penekanan di bawah oleh saya):

Metode template dengan demikian mengelola gambaran yang lebih besar dari semantik tugas, dan detail implementasi yang lebih baik dari pemilihan dan urutan metode. Gambar yang lebih besar ini memanggil metode abstrak dan non-abstrak untuk tugas yang dihadapi. Metode non-abstrak sepenuhnya dikendalikan oleh metode templat tetapi metode abstrak, diimplementasikan dalam subclass, memberikan kekuatan ekspresif pola dan tingkat kebebasan. Beberapa atau semua metode abstrak dapat dikhususkan dalam subkelas, memungkinkan penulis subkelas untuk memberikan perilaku tertentu dengan modifikasi minimal ke semantik yang lebih besar. Metode template (yang non-abstrak) tetap tidak berubah dalam pola ini, memastikan bahwa metode non-abstrak bawahan dan metode abstrak dipanggil dalam urutan yang dimaksudkan semula.

Memperbarui

Beberapa contoh nyata proyek yang telah saya kerjakan:

  1. berkomunikasi dengan sistem mainframe lama melalui berbagai "layar". Setiap layar memiliki banyak bidang, dengan nama tetap, posisi dan panjang, berisi bit data tertentu. Permintaan mengisi bidang tertentu dengan data tertentu. Respons mengembalikan data dalam satu atau beberapa bidang lainnya. Setiap transaksi mengikuti logika dasar yang sama, tetapi detailnya berbeda di setiap layar. Kami menggunakan Metode Templat di beberapa proyek yang berbeda untuk menerapkan kerangka tetap logika penanganan layar, sambil memungkinkan subkelas untuk menentukan detail khusus layar.
  2. mengekspor / mengimpor data konfigurasi dalam tabel DB ke / dari file Excel. Sekali lagi, skema dasar pemrosesan file Excel dan memasukkan / memperbarui catatan DB, atau membuang catatan ke Excel adalah sama untuk setiap tabel, tetapi detail setiap tabel berbeda. Jadi Metode Templat adalah pilihan yang sangat jelas untuk menghilangkan duplikasi kode dan membuat kode lebih mudah dipahami dan dipelihara.
  3. Membuat dokumen PDF. Setiap dokumen memiliki struktur yang sama, tetapi isinya berbeda setiap kali, tergantung banyak faktor. Sekali lagi, Metode Templat memudahkan untuk memisahkan kerangka tetap dari algoritme pembangkitan dari perincian khusus kasus yang dapat diubah. Faktanya. bahkan berlaku pada beberapa level di sini, karena dokumen terdiri dari beberapa bagian , yang masing-masing terdiri dari nol atau lebih bidang . Metode Templat diterapkan pada 3 tingkatan berbeda di sini.

Dalam dua kasus pertama, implementasi warisan asli menggunakan Strategi , menghasilkan banyak kode duplikat, yang tentu saja selama bertahun-tahun tumbuh perbedaan halus di sana-sini, mengandung banyak bug yang digandakan atau sedikit berbeda, dan sangat sulit untuk dipelihara. Refactoring ke Metode Templat (dan beberapa perangkat tambahan lainnya, seperti menggunakan anotasi Java) mengurangi ukuran kode sekitar 40-70%.

Ini hanya contoh terbaru yang muncul di benak saya. Saya bisa mengutip lebih banyak kasus dari hampir setiap proyek yang saya kerjakan sejauh ini.

Péter Török
sumber
Cukup mengutip GoF bukanlah jawaban. Anda harus memberikan alasan aktual untuk melakukan hal seperti itu.
DeadMG
@DeadMG, saya telah menggunakan Metode Templat secara teratur selama karir saya, jadi saya pikir sudah jelas bahwa ini adalah pola yang sangat praktis dan berguna (karena sebagian besar pola GoF adalah ... ini bukan latihan akademis teoretis, tetapi dikumpulkan dari pengalaman dunia nyata). Tetapi ternyata tidak semua orang setuju dengan itu ... jadi saya menambahkan beberapa contoh nyata untuk jawaban saya.
Péter Török
2

Sangat masuk akal, dan kadang-kadang diinginkan untuk memiliki metode non-virtual dalam kelas dasar abstrak; hanya karena kelas abstrak tidak selalu berarti setiap bagiannya harus dapat digunakan secara polimorfik.

Misalnya, Anda mungkin ingin menggunakan idiom 'Non virtual polymorphism', di mana suatu fungsi disebut secara polimorfis dari fungsi anggota non-virtual, untuk memastikan bahwa prasyarat atau postkondisi tertentu terpenuhi sebelum fungsi virtual disebut

class MyAbstractBaseClass
{
protected:
    virtual void OverrideMe() = 0;
public:
    void CallMeFirst();

    void CallMe()
    {
        CallMeFirst();
        OverrideMe();
    }
};
Ben Cottrell
sumber
Saya berpendapat bahwa, selama keseluruhan perilaku tetap sama, maka ini bisa dibuat virtual. Anda dapat mengisi kondisi sebelum / sesudah menggunakan implementasi yang berbeda (database vs di-memori). Kalau tidak, itu harus menjadi fungsi pribadi?
Justin Pihony
2

Cukup untuk sebuah kelas mengandung SATU metode virtual, agar kelas menjadi abstrak. Anda mungkin ingin memperhatikan metode mana yang Anda inginkan virtual dan yang tidak, sesuai dengan jenis polimorfisme yang Anda rencanakan untuk digunakan.

appoll
sumber
1

Tanyakan pada diri sendiri apa gunanya metode non-virtual di kelas abstrak. Metode seperti itu harus memiliki implementasi agar bermanfaat. Tetapi jika kelas memiliki implementasi, apakah masih bisa disebut kelas abstrak? Bahkan jika bahasa / kompiler memungkinkan, apakah masuk akal? Secara pribadi, saya kira tidak. Anda akan memiliki kelas normal dengan metode abstrak yang diharapkan dapat diterapkan oleh keturunan.

Bahasa utama saya adalah Delphi, bukan C #. Dalam Delphi, jika Anda menandai metode abstrak, Anda juga harus menandainya virtual atau kompiler mengeluh. Belum mengikuti perubahan bahasa terbaru terlalu dekat, tetapi jika kelas abstrak ada di atau akan datang ke Delphi, saya akan berharap kompiler mengeluh tentang metode non-virtual, metode pribadi apa pun dan tentang implementasi metode apa pun untuk kelas yang ditandai abstrak di tingkat kelas.

Marjan Venema
sumber
2
Saya pikir masuk akal untuk memiliki kelas abstrak yang memiliki beberapa metode abstrak, beberapa metode virtual dan beberapa metode non-virtual. Saya pikir itu selalu tergantung pada apa yang ingin Anda lakukan.
svick
3
Pertama saya akan membahas C # q Anda. Metode abstrak dalam C # secara virtual dan tidak dapat memiliki implementasi. Mengenai poin pertama Anda, kelas abstrak di C # dapat, dan harus memiliki implementasi semacam (jika tidak, Anda hanya perlu menggunakan antarmuka). Maksud dari suatu kelas yang abstrak adalah bahwa itu HARUS subkelas, namun mengandung logika yang (secara teoritis) semua subkelas akan digunakan. Ini mengurangi duplikasi kode. Yang saya tanyakan adalah apakah implementasi tersebut harus ditutup agar tidak ditimpa (pada dasarnya mengatakan bahwa cara dasar adalah satu-satunya cara).
Justin Pihony
@JustinPihony: terima kasih. Di Delphi, ketika Anda menandai metode abstrak, kompiler akan mengeluh jika Anda memberikan implementasi untuk itu. Menarik bagaimana berbagai bahasa menerapkan konsep secara berbeda dan karenanya menciptakan harapan yang berbeda pada penggunanya.
Marjan Venema
@vick: ya itu masuk akal, saya tidak akan menyebutnya kelas abstrak, tapi kelas dengan metode abstrak ... Tapi saya kira itu mungkin saja interpretasi saya.
Marjan Venema
@MarjanVenema, tapi itu bukan terminologi penggunaan C #. Jika Anda menandai metode apa pun di kelas abstract, Anda juga harus menandai seluruh kelas abstract.
svick
0

Namun, selama Anda percaya bahwa Prinsip Pergantian Liskov akan diikuti, lalu mengapa Anda tidak membiarkannya diganti?

Anda tidak membuat metode tertentu virtual karena Anda tidak percaya ini menjadi masalahnya. Lebih lanjut, dengan membuat metode tertentu bukan virtual, Anda memberi sinyal kepada pewaris metode apa yang harus diterapkan.

Secara pribadi, saya akan sering membuat kelebihan metode yang ada untuk kenyamanan bukan virtual sehingga pengguna kelas dapat memiliki default yang konsisten dan pelaksana bahkan tidak dapat membuat kesalahan dengan melanggar perilaku tersirat itu.

Telastyn
sumber