Kapan menggunakan kelas abstrak alih-alih antarmuka dengan metode ekstensi di C #?

70

"Kelas abstrak" dan "antarmuka" adalah konsep yang serupa, dengan antarmuka menjadi lebih abstrak dari keduanya. Salah satu faktor pembeda adalah bahwa kelas abstrak menyediakan implementasi metode untuk kelas turunan saat dibutuhkan. Namun dalam C #, faktor pembeda ini telah dikurangi dengan pengenalan metode ekstensi baru-baru ini, yang memungkinkan implementasi disediakan untuk metode antarmuka. Faktor pembeda lainnya adalah bahwa suatu kelas dapat mewarisi hanya satu kelas abstrak (yaitu, tidak ada pewarisan berganda), tetapi dapat menerapkan banyak antarmuka. Ini membuat antarmuka menjadi kurang ketat dan lebih fleksibel. Jadi, di C #, kapan kita harus menggunakan kelas abstrak alih-alih antarmuka dengan metode ekstensi?

Contoh penting dari model metode antarmuka + ekstensi adalah LINQ, di mana fungsionalitas kueri disediakan untuk semua jenis yang mengimplementasikan IEnumerablemelalui banyak metode ekstensi.

Gulshan
sumber
2
Dan kita bisa menggunakan 'Properties' di antarmuka juga.
Gulshan
1
Kedengarannya seperti spesifik C # daripada pertanyaan OOD umum. (Sebagian besar bahasa OO lainnya tidak memiliki metode ekstensi atau properti.)
Péter Török
4
Metode ekstensi tidak cukup memecahkan masalah yang diselesaikan oleh kelas abstrak, sehingga alasannya tidak berbeda dengan di Jawa atau bahasa pewarisan tunggal serupa lainnya.
Pekerjaan
3
Kebutuhan untuk implementasi metode kelas abstrak belum berkurang dengan metode ekstensi. Metode ekstensi tidak dapat mengakses bidang anggota pribadi, juga tidak dapat dinyatakan virtual dan ditimpa dalam kelas turunan.
zooone9243

Jawaban:

63

Ketika Anda membutuhkan bagian dari kelas untuk diimplementasikan. Contoh terbaik yang saya gunakan adalah pola metode templat .

public abstract class SomethingDoer
{
    public void Do()
    {
        this.DoThis();
        this.DoThat();
    }

    protected abstract void DoThis();
    protected abstract void DoThat();
}

Dengan demikian, Anda dapat menentukan langkah-langkah yang akan diambil ketika Do () dipanggil, tanpa mengetahui secara spesifik bagaimana mereka akan dilaksanakan. Turunan kelas harus menerapkan metode abstrak, tetapi bukan metode Do ().

Metode ekstensi tidak harus memenuhi bagian "harus menjadi bagian dari kelas" dari persamaan. Selain itu, iirc, metode ekstensi tidak bisa (nampak) seperti ruang publik.

sunting

Pertanyaannya lebih menarik daripada yang saya berikan pada awalnya. Setelah pemeriksaan lebih lanjut, Jon Skeet menjawab pertanyaan seperti ini di SO yang mendukung penggunaan antarmuka + metode ekstensi. Juga, potensi downside menggunakan refleksi terhadap hierarki objek yang dirancang dengan cara ini.

Secara pribadi, saya mengalami kesulitan melihat manfaat dari mengubah praktik yang umum saat ini, tetapi juga melihat beberapa kelemahan dalam melakukannya.

Perlu dicatat bahwa dimungkinkan untuk memprogram dengan cara ini dalam banyak bahasa melalui kelas Utility. Ekstensi hanya menyediakan gula sintaksis untuk membuat metode terlihat seperti milik kelas.

Steven Evers
sumber
Kalahkan aku untuk itu ..
Giorgi
1
Tetapi hal yang sama dapat dilakukan dengan Antarmuka dan metode ekstensi. Di sana metode yang Anda definisikan dalam kelas dasar abstrak seperti Do()akan didefinisikan sebagai metode ekstensi. Dan metode yang tersisa untuk definisi kelas turunan seperti DoThis()akan menjadi anggota Antarmuka utama. Dan dengan antarmuka, Anda dapat mendukung banyak implementasi / warisan. Dan lihat ini- odetocode.com/blogs/scott/archive/2009/10/05/…
Gulshan
@ Gulshan: Karena sesuatu dapat dilakukan dengan lebih dari satu cara tidak membuat jalan yang dipilih menjadi tidak valid Tidak ada keuntungan dalam menggunakan antarmuka. Kelas abstrak itu sendiri adalah antarmuka. Anda tidak perlu antarmuka untuk mendukung banyak implementasi.
ThomasX
Juga, mungkin ada kelemahan pada model antarmuka + ekstensi. Ambil perpustakaan Linq, misalnya. Ini bagus, tetapi jika Anda hanya perlu menggunakannya sekali dalam file kode itu, pustaka Linq lengkap akan tersedia di IntelliSense pada SETIAP IE yang dapat dihitung dalam file kode Anda. Ini dapat sangat memperlambat kinerja IDE.
KeithS
-1 karena Anda belum menunjukkan cara apa pun di mana kelas abstrak lebih cocok untuk Metode Templat daripada antarmuka
Benjamin Hodgson
25

Ini semua tentang apa yang ingin Anda modelkan:

Kelas abstrak bekerja berdasarkan pewarisan . Menjadi kelas dasar khusus, mereka memodelkan beberapa hubungan -a .

Sebagai contoh, seekor anjing adalah binatang, demikianlah yang kita miliki

class Dog : Animal { ... }

Karena tidak masuk akal untuk benar-benar membuat generik semua-hewan (seperti apa bentuknya?), Kami membuat Animalkelas dasar ini abstract- tetapi ini masih merupakan kelas dasar. Dan Dogkelas tidak masuk akal tanpa menjadi Animal.

Antarmuka di sisi lain adalah cerita yang berbeda. Mereka tidak menggunakan warisan tetapi memberikan polimorfisme (yang juga dapat diterapkan dengan warisan). Mereka tidak mencontoh hubungan is-a , tetapi lebih dari itu memang mendukung .

Ambil mis IComparable- objek yang mendukung dibandingkan dengan yang lain .

Fungsionalitas suatu kelas tidak tergantung pada antarmuka yang diterapkannya, antarmuka hanya menyediakan cara umum untuk mengakses fungsionalitas. Kita masih bisa Disposedengan Graphicsatau FileStreamtanpa menerapkannya IDisposable. The antarmuka hanya berkaitan metode bersama-sama.

Pada prinsipnya, seseorang dapat menambah dan menghapus antarmuka seperti halnya ekstensi tanpa mengubah perilaku suatu kelas, hanya memperkaya aksesnya. Anda tidak bisa mengambil kelas dasar karena objek akan menjadi tidak berarti!

Dario
sumber
Kapan Anda memilih untuk membuat abstrak kelas dasar?
Gulshan
1
@ Gulshan: Jika memang - untuk beberapa alasan - tidak masuk akal untuk benar-benar membuat turunan dari kelas dasar. Anda dapat "membuat" Chihuahua atau hiu putih, tetapi Anda tidak dapat membuat binatang tanpa spesialisasi lebih lanjut - dengan demikian Anda akan membuat Animalabstrak. Ini memungkinkan Anda untuk menulis abstractfungsi yang akan dikhususkan oleh kelas turunan. Atau lebih dalam hal pemrograman - itu mungkin tidak masuk akal untuk membuat UserControl, tetapi sebagai Button adalah sebuah UserControl, jadi kami memiliki jenis yang sama dari hubungan kelas / baseclass.
Dario
jika Anda memiliki metode abstrak, daripada Anda memiliki kelas abstrak. Dan metode abstrak yang Anda miliki ketika tidak ada nilai untuk mengimplementasikannya (seperti "pergi" untuk hewan). Dan tentu saja kadang-kadang Anda tidak ingin mengizinkan objek dari beberapa kelas umum, daripada Anda membuatnya abstrak.
Dainius
Tidak ada aturan yang mengatakan bahwa jika secara tata bahasa dan semantik bermakna untuk mengatakan "B adalah A" maka A harus menjadi kelas abstrak. Anda harus memilih antarmuka sampai Anda benar-benar tidak dapat menghindari kelas. Berbagi kode dapat dilakukan melalui komposisi antarmuka, dll. Ini membuatnya lebih mudah untuk menghindari melukis diri sendiri ke sudut dengan pohon warisan aneh.
Sara
3

Saya baru menyadari, bahwa saya belum pernah menggunakan kelas abstrak (setidaknya tidak dibuat) selama bertahun-tahun.

Kelas abstrak adalah binatang yang agak aneh antara antarmuka dan implementasi. Ada sesuatu di kelas abstrak yang menggelitik spider-sense saya. Saya berpendapat, orang tidak boleh "mengekspos" implementasi di balik antarmuka dengan cara apa pun (atau membuat tie-in).

Bahkan jika ada kebutuhan untuk perilaku umum antara kelas yang mengimplementasikan suatu antarmuka, saya akan menggunakan kelas pembantu independen, bukan kelas abstrak.

Saya akan pergi untuk antarmuka.

Maglob
sumber
2
-1: Saya tidak yakin berapa umur Anda, tetapi Anda mungkin ingin mempelajari pola desain, khususnya pola metode templat. Pola desain akan membuat Anda menjadi programmer yang lebih baik dan mereka akan mengakhiri ketidaktahuan Anda tentang kelas abstrak.
Jim G.
Sama dengan sensasi kesemutan. Kelas, fitur pertama dan terpenting dari C # dan Java, adalah fitur untuk membuat tipe dan segera merantainya untuk satu implementasi.
Jesse Millikan
2

IMHO, saya pikir ada perpaduan konsep di sini.

Kelas menggunakan jenis warisan tertentu, sedangkan antarmuka menggunakan jenis warisan yang berbeda.

Kedua jenis warisan itu penting dan bermanfaat, dan masing-masing memiliki pro dan kontra.

Mari kita mulai dari awal.

Kisah dengan antarmuka dimulai sejak lama dengan C ++. Pada pertengahan 1980-an, ketika C ++ dikembangkan, gagasan antarmuka sebagai jenis yang berbeda belum terwujud (ini akan datang tepat waktu).

C ++ hanya memiliki satu jenis warisan, jenis warisan yang digunakan oleh kelas, yang disebut warisan implementasi.

Untuk dapat menerapkan rekomendasi Buku GoF: "Untuk memprogram untuk antarmuka, dan bukan untuk implementasi", C ++ mendukung kelas abstrak dan banyak warisan implementasi untuk dapat melakukan apa yang dapat dilakukan oleh antarmuka di C # (dan berguna!) Untuk melakukan .

C # memperkenalkan jenis baru jenis, antarmuka, dan jenis baru warisan, antarmuka antarmuka, untuk menawarkan setidaknya dua cara berbeda untuk mendukung "Memprogram untuk antarmuka, dan bukan untuk implementasi", dengan satu peringatan penting: C # saja mendukung multiple inheritance dengan inheritance antarmuka (dan bukan dengan inheritance implementasi).

Jadi, alasan arsitektur di balik antarmuka di C # adalah rekomendasi GoF.

Saya harap ini bisa membantu.

Salam, Gaston

Gastón E. Nusimovich
sumber
1

Menerapkan metode ekstensi ke antarmuka berguna untuk menerapkan perilaku umum di seluruh kelas yang mungkin hanya berbagi antarmuka umum. Kelas semacam itu mungkin sudah ada dan ditutup untuk ekstensi melalui cara lain.

Untuk desain baru saya lebih suka menggunakan metode ekstensi pada antarmuka. Untuk hierarki Jenis, metode ekstensi menyediakan cara untuk memperluas perilaku tanpa harus membuka seluruh hierarki untuk modifikasi.

Namun, metode ekstensi tidak dapat mengakses implementasi pribadi, jadi di puncak hierarki, jika saya perlu merangkum implementasi swasta di belakang antarmuka publik maka kelas abstrak akan menjadi satu-satunya cara.

Ed James
sumber
Tidak, itu bukan satu-satunya cara yang aman. Ada cara lain yang lebih aman. Itu adalah metode ekstensi. Klien tidak akan terinfeksi sama sekali. Itu sebabnya saya telah menyebutkan metode antarmuka + ekstensi.
Gulshan
@ Ed James Saya pikir "kurang kuat" = "lebih fleksibel" Kapan Anda memilih kelas abstrak yang lebih kuat daripada antarmuka yang lebih fleksibel?
Gulshan
@ Ed James In asp.mvc metode ekstensi digunakan sejak awal. Apa pendapatmu tentang itu?
Gulshan
0

Saya pikir di C # multiple inheritance tidak didukung dan itu sebabnya konsep antarmuka diperkenalkan di C #.

Dalam kasus kelas abstrak, multiple inheritance juga tidak didukung. Jadi mudah untuk memutuskan apakah akan menggunakan kelas atau antarmuka abstrak.

Pemula
sumber
Lebih tepatnya, ini adalah kelas abstrak murni yang disebut "antarmuka" dalam C #.
Johan Boulé
-1

Saya sedang dalam proses mengimplementasikan kelas abstrak di proyek saya hanya karena C # tidak mendukung operator (konversi implisit, dalam kasus saya) di Antarmuka.

palswim
sumber