Kapan saya harus menggunakan antarmuka dan kapan saya harus menggunakan kelas dasar?
Haruskah selalu menjadi antarmuka jika saya tidak ingin benar-benar menentukan implementasi dasar dari metode?
Jika saya memiliki kelas Anjing dan Kucing. Mengapa saya ingin mengimplementasikan IPet daripada PetBase? Saya dapat memahami memiliki antarmuka untuk ISheds atau IBarks (IMakesNoise?), Karena mereka dapat ditempatkan pada hewan peliharaan dengan basis hewan peliharaan, tetapi saya tidak mengerti yang harus digunakan untuk hewan peliharaan generik.
I tend to prefer using the interface technique over the base type technique because the base type technique doesn’t allow the developer to choose the base type that works best in a particular situation.
. Saya tidak bisa memahami apa yang dimaksud dalam kutipan. Kami dapat membuat beberapa jenis dasar dan membuat jenis turunan untuk salah satu dari mereka, sehingga pengembang dapat memilih jenis dasar. Bisakah seseorang menjelaskan, apa yang saya lewatkan? Saya percaya ini bisa menjadi bagian dari pertanyaan ini. Atau haruskah saya memposting satu lagi tentang kutipan spesifik?Jawaban:
Mari ambil contoh Anda tentang kelas Anjing dan Kucing, dan mari kita ilustrasikan menggunakan C #:
Baik anjing dan kucing adalah hewan, khususnya mamalia berkaki empat (hewan terlalu umum). Mari kita asumsikan bahwa Anda memiliki kelas abstrak Mamalia, untuk keduanya:
Kelas dasar ini mungkin akan memiliki metode default seperti:
Semuanya adalah perilaku yang memiliki implementasi yang kurang lebih sama antara kedua spesies. Untuk menentukan ini, Anda harus:
Sekarang anggaplah ada mamalia lain, yang biasanya akan kita lihat di kebun binatang:
Ini akan tetap valid karena pada intinya fungsionalitas
Feed()
danMate()
masih akan sama.Namun, jerapah, badak, dan kuda nil bukanlah hewan yang bisa membuat hewan peliharaan. Di situlah antarmuka akan berguna:
Implementasi untuk kontrak di atas tidak akan sama antara kucing dan anjing; menempatkan implementasinya dalam kelas abstrak untuk diwariskan akan menjadi ide yang buruk.
Definisi Anjing dan Kucing Anda sekarang akan terlihat seperti:
Secara teoritis Anda dapat mengesampingkan mereka dari kelas dasar yang lebih tinggi, tetapi pada dasarnya antarmuka memungkinkan Anda untuk menambahkan hanya hal-hal yang Anda butuhkan ke dalam kelas tanpa perlu warisan.
Akibatnya, karena Anda biasanya hanya dapat mewarisi dari satu kelas abstrak (dalam sebagian besar bahasa OO yang diketik secara statis yaitu ... pengecualian termasuk C ++) tetapi dapat mengimplementasikan beberapa antarmuka, ini memungkinkan Anda untuk membangun objek secara ketat sesuai kebutuhan .
sumber
Yah, Josh Bloch berkata pada dirinya dalam Java 2d yang Efektif :
Lebih suka antarmuka daripada kelas abstrak
Beberapa poin utama:
Di sisi lain, antarmuka sangat sulit untuk berkembang. Jika Anda menambahkan metode ke antarmuka, itu akan merusak semua implementasinya.
PS .: Beli bukunya. Jauh lebih detail.
sumber
Antarmuka dan kelas dasar mewakili dua bentuk hubungan yang berbeda.
Warisan (kelas dasar) mewakili hubungan "is-a". Misalnya anjing atau kucing peliharaan "is-a". Hubungan ini selalu mewakili (tunggal) tujuan kelas (dalam hubungannya dengan "single prinsip tanggung jawab" ).
Antarmuka , di sisi lain, merupakan fitur tambahan dari suatu kelas. Saya menyebutnya hubungan "adalah", seperti dalam "
Foo
dapat digunakan", karenanyaIDisposable
antarmuka dalam C #.sumber
Gaya modern adalah mendefinisikan IPet dan PetBase.
Keuntungan dari antarmuka adalah bahwa kode lain dapat menggunakannya tanpa ikatan apa pun dengan kode yang dapat dieksekusi lainnya. Benar-benar "bersih." Antarmuka juga dapat dicampur.
Tetapi kelas dasar berguna untuk implementasi sederhana dan utilitas umum. Jadi sediakan kelas dasar abstrak juga untuk menghemat waktu dan kode.
sumber
Antarmuka
Kelas dasar
sumber
Secara umum, Anda harus memilih antarmuka daripada kelas abstrak. Salah satu alasan untuk menggunakan kelas abstrak adalah jika Anda memiliki implementasi umum di antara kelas-kelas konkret. Tentu saja, Anda masih harus mendeklarasikan antarmuka (IPet) dan memiliki kelas abstrak (PetBase) mengimplementasikan antarmuka itu. Menggunakan antarmuka kecil dan berbeda, Anda dapat menggunakan banyak untuk meningkatkan fleksibilitas lebih lanjut. Antarmuka memungkinkan jumlah maksimum fleksibilitas dan portabilitas tipe lintas batas. Saat melewati referensi melintasi batas, selalu melewati antarmuka dan bukan tipe konkret. Ini memungkinkan pihak penerima untuk menentukan implementasi konkret dan memberikan fleksibilitas maksimum. Ini benar-benar benar ketika memprogram dengan gaya TDD / BDD.
Geng Empat menyatakan dalam buku mereka "Karena warisan memperlihatkan subkelas ke detail implementasi orang tuanya, sering dikatakan bahwa 'warisan memecah enkapsulasi". Saya percaya ini benar.
sumber
Ini cukup .NET spesifik, tetapi buku Kerangka Desain Pedoman berpendapat bahwa di kelas umum memberikan lebih banyak fleksibilitas dalam kerangka kerja yang berkembang. Setelah antarmuka dikirimkan, Anda tidak mendapatkan kesempatan untuk mengubahnya tanpa melanggar kode yang menggunakan antarmuka itu. Namun dengan kelas, Anda dapat memodifikasinya dan tidak merusak kode yang menautkannya. Selama Anda melakukan modifikasi yang tepat, yang mencakup penambahan fungsionalitas baru, Anda akan dapat memperluas dan mengembangkan kode Anda.
Krzysztof Cwalina mengatakan di halaman 81:
Yang sedang berkata pasti ada tempat untuk antarmuka. Sebagai pedoman umum selalu menyediakan implementasi kelas dasar abstrak antarmuka jika tidak lain sebagai contoh cara untuk mengimplementasikan antarmuka. Dalam kasus terbaik, kelas dasar akan menghemat banyak pekerjaan.
sumber
Juan,
Saya suka menganggap antarmuka sebagai cara untuk mengkarakterisasi kelas. Kelas jenis anjing tertentu, misalnya YorkshireTerrier, mungkin merupakan turunan dari kelas induk anjing, tetapi ia juga mengimplementasikan IFurry, IStubby, dan IYippieDog. Jadi kelas mendefinisikan apa itu kelas tetapi antarmuka memberi tahu kita tentang hal itu.
Keuntungan dari ini adalah memungkinkan saya untuk, misalnya, mengumpulkan semua IYippieDog dan membuangnya ke dalam koleksi Ocean saya. Jadi sekarang saya dapat menjangkau satu set objek tertentu dan menemukan yang memenuhi kriteria yang saya lihat tanpa memeriksa kelas terlalu dekat.
Saya menemukan bahwa antarmuka benar-benar harus mendefinisikan sub-set perilaku publik suatu kelas. Jika ia mendefinisikan semua perilaku publik untuk semua kelas yang mengimplementasikannya maka biasanya tidak perlu ada. Mereka tidak memberi tahu saya apa pun yang bermanfaat.
Namun pemikiran ini bertentangan dengan gagasan bahwa setiap kelas harus memiliki antarmuka dan Anda harus kode ke antarmuka. Tidak apa-apa, tetapi Anda berakhir dengan banyak antarmuka satu-ke-satu ke kelas dan itu membuat segalanya membingungkan. Saya mengerti bahwa idenya adalah tidak benar-benar memerlukan biaya apa pun untuk dilakukan dan sekarang Anda dapat bertukar barang dengan mudah. Namun, saya jarang melakukannya. Sebagian besar waktu saya hanya memodifikasi kelas yang ada di tempat dan memiliki masalah yang sama persis seperti yang selalu saya lakukan jika antarmuka publik kelas itu perlu diubah, kecuali saya sekarang harus mengubahnya di dua tempat.
Jadi, jika Anda berpikir seperti saya, Anda pasti akan mengatakan bahwa Kucing dan Anjing itu dapat disetel. Ini adalah karakterisasi yang cocok dengan keduanya.
Bagian lain dari ini adalah apakah mereka harus memiliki kelas dasar yang sama? Pertanyaannya adalah apakah mereka perlu diperlakukan secara luas sebagai hal yang sama. Tentu saja mereka berdua Hewan, tetapi apakah itu sesuai dengan bagaimana kita akan menggunakannya bersama-sama.
Katakanlah saya ingin mengumpulkan semua kelas hewan dan menaruhnya di wadah bahtera saya.
Atau apakah mereka perlu mamalia? Mungkin kita membutuhkan semacam pabrik susu hewan silang?
Apakah mereka bahkan perlu dihubungkan bersama sama sekali? Apakah cukup dengan hanya mengetahui bahwa keduanya dapat disetel?
Saya sering merasakan keinginan untuk mendapatkan seluruh hirarki kelas ketika saya benar-benar hanya membutuhkan satu kelas. Saya melakukannya untuk mengantisipasi suatu hari nanti saya mungkin membutuhkannya dan biasanya saya tidak pernah melakukannya. Bahkan ketika saya melakukannya, saya biasanya menemukan saya harus melakukan banyak hal untuk memperbaikinya. Itu karena kelas pertama yang saya buat bukanlah Anjing, saya tidak seberuntung itu, melainkan Platypus. Sekarang seluruh hierarki kelas saya didasarkan pada kasus aneh dan saya memiliki banyak kode yang terbuang.
Anda mungkin juga menemukan di beberapa titik bahwa tidak semua Kucing dapat IP (seperti yang tidak berambut). Sekarang Anda bisa memindahkan Antarmuka itu ke semua kelas turunan yang cocok. Anda akan menemukan bahwa perubahan yang jauh lebih sedikit melanggar yaitu Kucing yang tiba-tiba tidak lagi berasal dari PettableBase.
sumber
Berikut adalah definisi dasar dan kelas antarmuka dan dasar:
Bersulang
sumber
Saya sarankan menggunakan komposisi alih-alih pewarisan bila memungkinkan. Gunakan antarmuka tetapi gunakan objek anggota untuk implementasi basis. Dengan begitu, Anda dapat mendefinisikan pabrik yang membuat objek Anda agar berperilaku dengan cara tertentu. Jika Anda ingin mengubah perilaku maka Anda membuat metode pabrik baru (atau pabrik abstrak) yang menciptakan berbagai jenis sub-objek.
Dalam beberapa kasus, Anda mungkin menemukan bahwa objek utama Anda tidak memerlukan antarmuka sama sekali, jika semua perilaku yang dapat diubah didefinisikan dalam objek pembantu.
Jadi, alih-alih IPet atau PetBase, Anda mungkin berakhir dengan Pet yang memiliki parameter IFurBehavior. Parameter IFurBehavior diatur oleh metode CreateDog () dari PetFactory. Parameter inilah yang dipanggil untuk metode shed ().
Jika Anda melakukan ini, Anda akan menemukan kode Anda jauh lebih fleksibel dan sebagian besar objek sederhana Anda berurusan dengan perilaku seluruh sistem yang sangat dasar.
Saya merekomendasikan pola ini bahkan dalam berbagai bahasa warisan.
sumber
Ini dijelaskan dengan baik di artikel Java World ini .
Secara pribadi, saya cenderung menggunakan antarmuka untuk mendefinisikan antarmuka - yaitu bagian dari desain sistem yang menentukan bagaimana sesuatu harus diakses.
Bukan hal yang aneh bahwa saya akan memiliki kelas yang mengimplementasikan satu atau lebih antarmuka.
Kelas abstrak yang saya gunakan sebagai dasar untuk sesuatu yang lain.
Berikut ini adalah kutipan dari artikel yang disebutkan di atas artikel JavaWorld.com, penulis Tony Sintes, 04/20/01
Beberapa mengatakan Anda harus mendefinisikan semua kelas dalam hal antarmuka, tetapi saya pikir rekomendasi tampaknya agak ekstrem. Saya menggunakan antarmuka ketika saya melihat bahwa sesuatu dalam desain saya akan sering berubah.
sumber
Juga perlu diingat untuk tidak terhanyut dalam OO ( lihat blog ) dan selalu memodelkan objek berdasarkan perilaku yang diperlukan, jika Anda mendesain aplikasi di mana satu-satunya perilaku yang Anda butuhkan adalah nama generik dan spesies untuk hewan maka Anda hanya perlu satu hewan kelas dengan properti untuk nama, bukan jutaan kelas untuk setiap hewan yang mungkin ada di dunia.
sumber
Saya memiliki aturan praktis yang kasar
Fungsi: kemungkinan berbeda di semua bagian: Antarmuka.
Data, dan fungsi, sebagian besar akan sama, bagian berbeda: kelas abstrak.
Data, dan fungsionalitas, benar-benar berfungsi, jika diperluas hanya dengan sedikit perubahan: kelas biasa (konkret)
Data dan fungsionalitas, tidak ada perubahan yang direncanakan: kelas biasa (konkret) dengan pengubah akhir.
Data, dan mungkin fungsionalitas: baca-saja: anggota enum.
Ini sangat kasar dan siap dan sama sekali tidak didefinisikan secara ketat, tetapi ada spektrum dari antarmuka di mana semuanya dimaksudkan untuk diubah menjadi enums di mana semuanya diperbaiki sedikit seperti file read-only.
sumber
Antarmuka harus kecil. Sangat kecil. Jika Anda benar-benar menghancurkan objek, maka antarmuka Anda mungkin hanya akan berisi beberapa metode dan properti yang sangat spesifik.
Kelas abstrak adalah jalan pintas. Adakah hal-hal yang semua turunan dari saham PetBase yang dapat Anda kode sekali dan dilakukan? Jika ya, maka sudah waktunya untuk kelas abstrak.
Kelas abstrak juga membatasi. Sementara mereka memberi Anda jalan pintas yang bagus untuk menghasilkan objek anak, objek apa pun yang diberikan hanya dapat menerapkan satu kelas abstrak. Sering kali, saya menemukan ini sebagai batasan kelas Abstrak, dan inilah mengapa saya menggunakan banyak antarmuka.
Kelas abstrak dapat berisi beberapa antarmuka. Kelas abstrak PetBase Anda dapat menerapkan IPet (hewan peliharaan memiliki pemilik) dan IDigestion (hewan peliharaan makan, atau setidaknya mereka seharusnya). Namun, PetBase mungkin tidak akan mengimplementasikan IMammal, karena tidak semua hewan peliharaan adalah mamalia dan tidak semua mamalia adalah hewan peliharaan. Anda dapat menambahkan MammalPetBase yang memperluas PetBase dan menambahkan IMammal. FishBase dapat memiliki PetBase dan menambahkan IFish. IFish akan memiliki ISwim dan IUnderwaterBreather sebagai antarmuka.
Ya, contoh saya terlalu rumit untuk contoh sederhana, tapi itu bagian dari hal hebat tentang bagaimana antarmuka dan kelas abstrak bekerja bersama.
sumber
Sumber : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/
C # adalah bahasa yang luar biasa yang telah matang dan berevolusi selama 14 tahun terakhir. Ini bagus untuk kami para pengembang karena bahasa yang matang memberi kami sejumlah besar fitur bahasa yang dapat kami gunakan.
Namun, dengan banyak kekuatan menjadi banyak tanggung jawab. Beberapa fitur ini dapat disalahgunakan, atau terkadang sulit untuk memahami mengapa Anda memilih untuk menggunakan satu fitur di atas yang lain. Selama bertahun-tahun, fitur yang telah saya lihat banyak pengembang geluti adalah ketika memilih untuk menggunakan antarmuka atau memilih untuk menggunakan kelas abstrak. Keduanya memiliki kelebihan dan kekurangan serta waktu dan tempat yang tepat untuk menggunakannya. Tapi bagaimana cara kita memutuskan ???
Keduanya menyediakan untuk penggunaan kembali fungsionalitas umum antara jenis. Perbedaan yang paling jelas adalah bahwa antarmuka tidak memberikan implementasi untuk fungsi mereka sedangkan kelas abstrak memungkinkan Anda untuk menerapkan beberapa perilaku "dasar" atau "default" dan kemudian memiliki kemampuan untuk "menimpa" perilaku default ini dengan tipe turunan kelas jika perlu .
Ini semua baik dan bagus dan menyediakan untuk penggunaan kembali kode dan mematuhi prinsip pengembangan perangkat lunak KERING (Jangan Ulangi Diri Sendiri). Kelas abstrak sangat bagus untuk digunakan ketika Anda memiliki hubungan “adalah”.
Misalnya: Anjing jenis golden retriever “adalah”. Begitu juga pudel. Mereka berdua bisa menggonggong, seperti semua anjing bisa. Namun, Anda mungkin ingin menyatakan bahwa taman pudel sangat berbeda dari kulit anjing "default". Oleh karena itu, masuk akal bagi Anda untuk mengimplementasikan sesuatu sebagai berikut:
Seperti yang Anda lihat, ini akan menjadi cara yang bagus untuk menjaga kode Anda KERING dan memungkinkan untuk implementasi kelas dasar dipanggil ketika salah satu jenis hanya dapat mengandalkan Bark default daripada implementasi kasus khusus. Kelas-kelas seperti GoldenRetriever, Boxer, Lab semua bisa mewarisi "default" (kelas bass) Bark tanpa biaya hanya karena mereka menerapkan kelas abstrak Dog.
Tapi saya yakin Anda sudah tahu itu.
Anda di sini karena Anda ingin memahami mengapa Anda mungkin ingin memilih antarmuka daripada kelas abstrak atau sebaliknya. Nah salah satu alasan Anda mungkin ingin memilih antarmuka daripada kelas abstrak adalah ketika Anda tidak memiliki atau ingin mencegah implementasi standar. Ini biasanya karena tipe yang mengimplementasikan antarmuka tidak terkait dalam hubungan "adalah". Sebenarnya, mereka tidak harus berhubungan sama sekali kecuali untuk fakta bahwa setiap jenis "mampu" atau memiliki "kemampuan" untuk melakukan sesuatu atau memiliki sesuatu.
Sekarang apa artinya itu? Misalnya, manusia bukan bebek ... dan bebek bukan manusia. Cukup jelas. Namun, baik bebek dan manusia memiliki "kemampuan" untuk berenang (mengingat bahwa manusia lulus pelajaran berenang di kelas 1 :)). Juga, karena bebek bukan manusia atau sebaliknya, ini bukan hubungan "adalah", tetapi sebaliknya hubungan "mampu" dan kita dapat menggunakan antarmuka untuk menggambarkan bahwa:
Menggunakan antarmuka seperti kode di atas akan memungkinkan Anda untuk mengirimkan objek ke metode yang “mampu” melakukan sesuatu. Kode tidak peduli bagaimana melakukannya ... Yang ia tahu adalah ia bisa memanggil metode Swim pada objek itu dan objek itu akan tahu perilaku mana yang dijalankan pada saat run-time berdasarkan jenisnya.
Sekali lagi, ini membantu kode Anda tetap KERING sehingga Anda tidak perlu menulis beberapa metode yang memanggil objek untuk membentuk sebelumnya fungsi inti yang sama (ShowHowHumanSwims (manusia), ShowHowDuckSwims (bebek), dll.)
Menggunakan antarmuka di sini memungkinkan metode panggilan tidak perlu khawatir tentang jenis apa atau bagaimana perilaku itu diterapkan. Ia hanya tahu bahwa mengingat antarmuka, setiap objek harus menerapkan metode Swim sehingga aman untuk memanggilnya dalam kode sendiri dan memungkinkan perilaku metode Swim ditangani dalam kelasnya sendiri.
Ringkasan:
Jadi aturan utama saya adalah menggunakan kelas abstrak ketika Anda ingin menerapkan fungsionalitas "default" untuk hierarki kelas atau / dan kelas atau tipe yang Anda gunakan untuk berbagi hubungan "is a" (mis. Poodle "adalah "Jenis anjing).
Di sisi lain gunakan antarmuka ketika Anda tidak memiliki hubungan "adalah" tetapi memiliki tipe yang berbagi "kemampuan" untuk melakukan sesuatu atau memiliki sesuatu (mis. Bebek "bukan" manusia. Namun, bebek dan manusia berbagi "Kemampuan" untuk berenang).
Perbedaan lain yang perlu diperhatikan antara kelas abstrak dan antarmuka adalah bahwa kelas dapat mengimplementasikan satu ke banyak antarmuka tetapi kelas hanya bisa mewarisi dari SATU kelas abstrak (atau kelas apa pun dalam hal ini). Ya, Anda dapat membuat kelas sarang dan memiliki hierarki warisan (yang banyak dan harus dimiliki oleh banyak program) tetapi Anda tidak dapat mewarisi dua kelas dalam satu definisi kelas turunan (aturan ini berlaku untuk C #. Dalam beberapa bahasa lain Anda dapat melakukan ini, biasanya hanya karena kurangnya antarmuka dalam bahasa ini).
Ingat juga saat menggunakan antarmuka untuk mematuhi Prinsip Segregasi Antarmuka (ISP). ISP menyatakan bahwa tidak ada klien yang harus dipaksa untuk bergantung pada metode yang tidak digunakannya. Untuk alasan ini antarmuka harus difokuskan pada tugas-tugas tertentu dan biasanya sangat kecil (mis. IDisposable, IComparable).
Kiat lain adalah jika Anda mengembangkan fungsionalitas kecil dan ringkas, gunakan antarmuka. Jika Anda mendesain unit fungsional besar, gunakan kelas abstrak.
Semoga ini jelas bagi sebagian orang!
Juga jika Anda dapat memikirkan contoh yang lebih baik atau ingin menunjukkan sesuatu, silakan lakukan di komentar di bawah!
sumber
Kasus untuk Kelas Dasar atas Antarmuka dijelaskan dengan baik dalam Pedoman Pengkodean .NET Submain:
sumber
Satu perbedaan penting adalah bahwa Anda hanya dapat mewarisi satu kelas dasar, tetapi Anda dapat mengimplementasikan banyak antarmuka. Jadi Anda hanya ingin menggunakan kelas dasar jika Anda benar-benar yakin bahwa Anda tidak perlu juga mewarisi kelas dasar yang berbeda. Selain itu, jika Anda menemukan antarmuka Anda semakin besar maka Anda harus mulai mencari untuk memecahnya menjadi beberapa bagian logis yang mendefinisikan fungsi independen, karena tidak ada aturan bahwa kelas Anda tidak dapat mengimplementasikan semuanya (atau bahwa Anda dapat mendefinisikan yang berbeda antarmuka yang hanya mewarisi mereka semua untuk mengelompokkan mereka).
sumber
Ketika saya pertama kali mulai belajar tentang pemrograman berorientasi objek, saya membuat kesalahan yang mudah dan mungkin umum menggunakan pewarisan untuk berbagi perilaku umum - bahkan di mana perilaku itu tidak penting bagi sifat objek.
Untuk lebih lanjut membangun contoh yang banyak digunakan dalam pertanyaan khusus ini, ada banyak hal yang dapat dipelihara - pacar, mobil, selimut fuzzy ... - jadi saya mungkin memiliki kelas Petable yang menyediakan perilaku umum ini, dan berbagai kelas mewarisi dari itu.
Namun, menjadi petable bukan bagian dari sifat dari objek-objek ini. Ada jauh konsep yang lebih penting yang sangat penting untuk sifat mereka - pacar adalah orang, mobil adalah kendaraan darat, kucing adalah mamalia ...
Perilaku harus ditetapkan terlebih dahulu ke antarmuka (termasuk antarmuka standar kelas), dan dipromosikan ke kelas dasar hanya jika mereka (a) umum untuk kelompok kelas besar yang merupakan himpunan bagian dari kelas yang lebih besar - dalam arti yang sama seperti "cat" dan "person" adalah himpunan bagian dari "mamalia".
Tangkapannya adalah, setelah Anda memahami desain berorientasi objek cukup lebih baik daripada yang saya lakukan pada awalnya, Anda biasanya akan melakukan ini secara otomatis tanpa memikirkannya. Jadi kebenaran sederhana dari pernyataan "kode ke antarmuka, bukan kelas abstrak" menjadi sangat jelas sehingga Anda sulit percaya ada orang yang mau repot-repot mengatakannya - dan mulai mencoba membaca makna lain di dalamnya.
Hal lain yang saya tambahkan adalah bahwa jika suatu kelas adalah murni abstrak - tanpa anggota yang tidak abstrak, tidak diwariskan atau metode yang terpapar pada anak, orang tua, atau klien - maka mengapa kelas itu? Ini bisa diganti, dalam beberapa kasus dengan antarmuka dan dalam kasus lain oleh Null.
sumber
Lebih suka antarmuka daripada kelas abstrak
Dasar pemikiran, poin utama untuk dipertimbangkan [dua sudah disebutkan di sini] adalah:
Tentu saja, subjek telah dibahas panjang lebar di tempat lain [2,3].
[1] Tentu saja menambahkan lebih banyak kode, tetapi jika singkatnya adalah perhatian utama Anda, Anda mungkin harus menghindari Jawa!
[2] Joshua Bloch, Java Efektif, item 16-18.
[3] http://www.codeproject.com/KB/ar ...
sumber
Komentar sebelumnya tentang penggunaan kelas abstrak untuk implementasi umum sudah pasti. Salah satu manfaat yang belum saya lihat sebutkan adalah bahwa penggunaan antarmuka membuatnya lebih mudah untuk mengimplementasikan objek tiruan untuk tujuan pengujian unit. Mendefinisikan IPet dan PetBase seperti yang dijelaskan Jason Cohen memungkinkan Anda untuk mengejek kondisi data yang berbeda dengan mudah, tanpa overhead database fisik (hingga Anda memutuskan sudah waktunya untuk menguji hal yang sebenarnya).
sumber
Jangan gunakan kelas dasar kecuali Anda tahu artinya, dan itu berlaku dalam kasus ini. Jika itu berlaku, gunakan, jika tidak, gunakan antarmuka. Tetapi perhatikan jawaban tentang antarmuka kecil.
Warisan Publik terlalu sering digunakan dalam OOD dan mengungkapkan lebih banyak daripada yang disadari oleh sebagian besar pengembang atau yang ingin dijalani. Lihat Prinsip Substitutablity Liskov
Singkatnya, jika A "adalah" B maka A membutuhkan tidak lebih dari B dan memberikan tidak kurang dari B, untuk setiap metode yang dipaparkan.
sumber
Opsi lain yang perlu diingat adalah menggunakan hubungan "has-a", alias "diimplementasikan dalam istilah" atau "komposisi." Kadang-kadang ini adalah cara yang lebih bersih dan lebih fleksibel untuk menyusun berbagai hal daripada menggunakan warisan "is-a".
Mungkin tidak masuk akal secara logis untuk mengatakan bahwa Anjing dan Kucing sama-sama "memiliki" Hewan Peliharaan, tetapi ia menghindari jebakan warisan ganda yang umum:
Ya, contoh ini menunjukkan bahwa ada banyak duplikasi kode dan kurangnya keanggunan yang terlibat dalam melakukan hal-hal seperti ini. Tetapi orang juga harus menghargai bahwa ini membantu menjaga Anjing dan Kucing dipisahkan dari kelas Pet (dalam hal itu Anjing dan Kucing tidak memiliki akses ke anggota pribadi Pet), dan itu memberi ruang bagi Anjing dan Kucing untuk mewarisi dari sesuatu yang lain- -mungkin kelas mamalia.
Komposisi lebih disukai ketika tidak ada akses pribadi diperlukan dan Anda tidak perlu merujuk ke Dog and Cat menggunakan referensi / pointer Pet generik. Antarmuka memberi Anda kemampuan referensi umum dan dapat membantu mengurangi verbositas kode Anda, tetapi mereka juga dapat mengaburkan hal-hal ketika mereka tidak terorganisir dengan baik. Warisan berguna ketika Anda membutuhkan akses anggota pribadi, dan dalam menggunakannya Anda berkomitmen untuk sangat menggabungkan kelas Anjing dan Kucing Anda ke kelas Hewan Peliharaan Anda, yang merupakan biaya yang harus dibayar untuk membayar.
Antara warisan, komposisi, dan antarmuka tidak ada satu cara yang selalu benar, dan ada baiknya untuk mempertimbangkan bagaimana ketiga opsi dapat digunakan secara harmonis. Dari ketiganya, pewarisan biasanya merupakan opsi yang harus paling jarang digunakan.
sumber
Secara konseptual, sebuah antarmuka digunakan untuk secara formal dan semi-formal mendefinisikan serangkaian metode yang akan disediakan oleh suatu objek. Secara formal berarti serangkaian nama metode dan tanda tangan, dan semi-formal berarti dokumentasi yang dapat dibaca manusia yang terkait dengan metode tersebut.
Antarmuka hanyalah deskripsi dari API (setelah semua, API adalah singkatan dari antarmuka pemrograman aplikasi ), mereka tidak dapat berisi implementasi apa pun, dan itu tidak mungkin untuk menggunakan atau menjalankan antarmuka. Mereka hanya membuat eksplisit kontrak tentang bagaimana Anda harus berinteraksi dengan suatu objek.
Kelas menyediakan implementasi, dan mereka dapat menyatakan bahwa mereka menerapkan nol, satu atau lebih Antarmuka. Jika kelas dimaksudkan untuk diwarisi, konvensi adalah untuk mengawali nama kelas dengan "Basis".
Ada perbedaan antara kelas dasar dan kelas dasar abstrak (ABC). ABC menggabungkan antarmuka dan implementasi bersama. Abstrak di luar pemrograman komputer berarti "ringkasan", yaitu "abstrak == antarmuka". Sebuah kelas dasar abstrak kemudian dapat menggambarkan kedua interface, serta implementasi kosong, sebagian atau lengkap yang dimaksudkan untuk diwariskan.
Pendapat tentang kapan harus menggunakan antarmuka vs kelas dasar abstrak versus hanya kelas akan bervariasi liar berdasarkan pada kedua apa yang Anda berkembang, dan bahasa yang Anda berkembang di. Antarmuka sering dikaitkan hanya dengan bahasa statis diketik seperti Java atau C #, tapi bahasa yang diketik secara dinamis juga dapat memiliki antarmuka dan kelas dasar abstrak . Dalam Python misalnya, perbedaan dibuat jelas antara Kelas, yang menyatakan itu mengimplementasikan sebuah antarmuka , dan objek, yang merupakan contoh dari kelas , dan dikatakan memberikan bahwa antarmuka. Mungkin dalam bahasa yang dinamis bahwa dua objek yang keduanya merupakan instance dari kelas yang sama , dapat menyatakan bahwa mereka menyediakan antarmuka yang sama sekali berbeda . Dalam Python ini hanya mungkin untuk atribut objek, sementara metode dibagi keadaan antara semua objek dari suatu kelas . Namun, di Ruby, objek dapat memiliki metode per-instance, jadi ada kemungkinan bahwa antarmuka antara dua objek dari kelas yang sama dapat bervariasi sebanyak keinginan programmer (namun, Ruby tidak memiliki cara eksplisit untuk menyatakan Antarmuka).
Dalam bahasa dinamis, antarmuka ke suatu objek sering diasumsikan secara implisit, baik dengan mengintrospeksi objek dan menanyakan metode apa yang disediakannya ( lihat sebelum Anda melompat ) atau lebih baik dengan hanya mencoba menggunakan antarmuka yang diinginkan pada suatu objek dan menangkap pengecualian jika objek tersebut tidak menyediakan antarmuka itu ( lebih mudah untuk meminta maaf daripada izin ). Ini dapat menyebabkan "false positive" di mana keduanya antarmuka memiliki nama metode yang sama, tetapi secara semantik berbeda. Namun, manfaatnya adalah kode Anda lebih fleksibel karena Anda tidak perlu menentukan di muka untuk mengantisipasi semua kemungkinan penggunaan kode Anda.
sumber
Itu tergantung pada kebutuhan Anda. Jika IPet cukup sederhana, saya lebih suka mengimplementasikannya. Jika tidak, jika PetBase mengimplementasikan banyak fungsi yang tidak ingin Anda tiru, maka lakukanlah.
Kelemahan untuk menerapkan kelas dasar adalah persyaratan untuk
override
(ataunew
) metode yang ada. Ini membuat mereka metode virtual yang berarti Anda harus berhati-hati tentang bagaimana Anda menggunakan instance objek.Terakhir, warisan tunggal .NET membunuh saya. Contoh naif: Katakanlah Anda membuat kontrol pengguna, jadi Anda mewarisi
UserControl
. Tapi, sekarang Anda terkunci dari warisanPetBase
. Ini memaksa Anda untuk mengatur ulang, seperti membuat anggotaPetBase
kelas, sebagai gantinya.sumber
Saya biasanya tidak menerapkan baik sampai saya membutuhkannya. Saya lebih menyukai antarmuka daripada kelas abstrak karena itu memberi sedikit lebih banyak fleksibilitas. Jika ada perilaku umum di beberapa kelas yang mewarisi saya memindahkannya dan membuat kelas dasar abstrak. Saya tidak melihat perlunya keduanya, karena mereka pada dasarnya server tujuan yang sama, dan memiliki keduanya adalah bau kode yang buruk (imho) bahwa solusinya telah direkayasa berlebihan.
sumber
Mengenai C #, dalam beberapa hal antarmuka dan kelas abstrak dapat dipertukarkan. Namun, perbedaannya adalah: i) antarmuka tidak dapat mengimplementasikan kode; ii) karena hal ini, antarmuka tidak dapat memanggil stack ke subclass; dan iii) hanya kelas abstrak yang dapat diwarisi pada suatu kelas, sedangkan beberapa antarmuka dapat diimplementasikan pada suatu kelas.
sumber
Oleh def, antarmuka menyediakan lapisan untuk berkomunikasi dengan kode lain. Semua properti publik dan metode kelas secara default mengimplementasikan antarmuka implisit. Kita juga dapat mendefinisikan antarmuka sebagai peran, kapan pun kelas mana pun perlu memainkan peran itu, ia harus mengimplementasikannya sehingga memberikan berbagai bentuk implementasi tergantung pada kelas yang mengimplementasikannya. Oleh karena itu ketika Anda berbicara tentang antarmuka, Anda berbicara tentang polimorfisme dan ketika Anda berbicara tentang kelas dasar, Anda berbicara tentang warisan. Dua konsep oops !!!
sumber
Saya telah menemukan bahwa pola Interface> Abstract> Concrete bekerja pada use-case berikut:
Kelas abstrak mendefinisikan atribut default bersama dari kelas beton, namun menegakkan antarmuka. Sebagai contoh:
Sekarang, karena semua mamalia memiliki rambut dan puting (AFAIK, saya bukan ahli zoologi), kita dapat memasukkan ini ke kelas dasar abstrak
Dan kemudian kelas konkret hanya mendefinisikan bahwa mereka berjalan tegak.
Desain ini bagus ketika ada banyak kelas beton, dan Anda tidak ingin mempertahankan boilerplate hanya untuk memprogram ke antarmuka. Jika metode baru ditambahkan ke antarmuka, itu akan memecah semua kelas yang dihasilkan, jadi Anda masih mendapatkan keuntungan dari pendekatan antarmuka.
Dalam hal ini, abstraknya bisa juga konkret; Namun, penunjukan abstrak membantu untuk menekankan bahwa pola ini sedang digunakan.
sumber
Seorang pewaris kelas dasar harus memiliki hubungan "adalah". Antarmuka mewakili hubungan "mengimplementasikan a". Jadi hanya gunakan kelas dasar ketika pewaris Anda akan mempertahankan hubungan.
sumber
Gunakan Antarmuka untuk menegakkan kontrak ACROSS keluarga dari kelas yang tidak terkait. Misalnya, Anda mungkin memiliki metode akses umum untuk kelas yang mewakili koleksi, tetapi berisi data yang sangat berbeda yaitu satu kelas mungkin mewakili hasil yang ditetapkan dari kueri, sementara yang lain mungkin mewakili gambar di galeri. Selain itu, Anda dapat mengimplementasikan banyak antarmuka, sehingga memungkinkan Anda untuk memadukan (dan menandakan) kemampuan kelas.
Gunakan Warisan ketika kelas memiliki hubungan yang sama dan oleh karena itu memiliki tanda tangan struktural dan perilaku yang sama, yaitu Mobil, Sepeda Motor, Truk dan SUV adalah semua jenis kendaraan jalan yang mungkin mengandung sejumlah roda, kecepatan tertinggi
sumber