Ini mungkin pertanyaan OOP umum. Saya ingin melakukan perbandingan generik antara antarmuka dan kelas abstrak berdasarkan penggunaannya.
Kapan seseorang ingin menggunakan antarmuka dan kapan seseorang ingin menggunakan kelas abstrak ?
Ini mungkin pertanyaan OOP umum. Saya ingin melakukan perbandingan generik antara antarmuka dan kelas abstrak berdasarkan penggunaannya.
Kapan seseorang ingin menggunakan antarmuka dan kapan seseorang ingin menggunakan kelas abstrak ?
Jawaban:
Saya menulis artikel tentang itu:
Meringkas:
Ketika kita berbicara tentang kelas abstrak, kita mendefinisikan karakteristik tipe objek; menentukan apa objek itu .
Ketika kita berbicara tentang antarmuka dan mendefinisikan kemampuan yang kita janjikan untuk diberikan, kita berbicara tentang membuat kontrak tentang apa yang bisa dilakukan objek.
Interfaces do not express something like "a Doberman is a type of dog and every dog can walk" but more like "this thing can walk"
. Terima kasih
Use abstract classes and inheritance if you can make the statement “A is a B”. Use interfaces if you can make the statement “A is capable of [doing] as”
Kelas abstrak dapat memiliki status atau fungsi bersama. Antarmuka hanya janji untuk menyediakan status atau fungsi. Kelas abstrak yang baik akan mengurangi jumlah kode yang harus ditulis ulang karena fungsionalitas atau statusnya dapat dibagikan. Antarmuka tidak memiliki informasi yang ditentukan untuk dibagikan
Secara pribadi, saya hampir tidak pernah memiliki kebutuhan untuk menulis kelas abstrak.
Sering kali saya melihat kelas abstrak digunakan (salah), itu karena penulis kelas abstrak menggunakan pola "Templat metode".
Masalah dengan "Templat metode" adalah bahwa itu hampir selalu agak masuk kembali - kelas "turunan" tahu tidak hanya metode "abstrak" dari kelas dasarnya yang diimplementasikan, tetapi juga tentang metode publik dari kelas dasar , meskipun seringkali tidak perlu memanggil mereka.
Contoh (terlalu disederhanakan):
abstract class QuickSorter
{
public void Sort(object[] items)
{
// implementation code that somewhere along the way calls:
bool less = compare(x,y);
// ... more implementation code
}
abstract bool compare(object lhs, object rhs);
}
Jadi di sini, penulis kelas ini telah menulis algoritma umum dan bermaksud agar orang menggunakannya dengan "mengkhususkan" itu dengan menyediakan "kait" mereka sendiri - dalam hal ini, metode "bandingkan".
Jadi penggunaan yang dimaksud adalah seperti ini:
class NameSorter : QuickSorter
{
public bool compare(object lhs, object rhs)
{
// etc.
}
}
Masalahnya adalah Anda terlalu banyak menyatukan dua konsep:
Dalam kode di atas, secara teoritis, penulis metode "bandingkan" dapat kembali memanggil metode superclass "Sort" ... meskipun dalam praktiknya mereka tidak akan pernah mau atau perlu melakukan ini.
Harga yang Anda bayar untuk kopling yang tidak dibutuhkan ini adalah sulit untuk mengubah superclass, dan dalam kebanyakan bahasa OO, tidak mungkin untuk mengubahnya saat runtime.
Metode alternatif adalah menggunakan pola desain "Strategi" sebagai gantinya:
interface IComparator
{
bool compare(object lhs, object rhs);
}
class QuickSorter
{
private readonly IComparator comparator;
public QuickSorter(IComparator comparator)
{
this.comparator = comparator;
}
public void Sort(object[] items)
{
// usual code but call comparator.Compare();
}
}
class NameComparator : IComparator
{
bool compare(object lhs, object rhs)
{
// same code as before;
}
}
Jadi perhatikan sekarang: Yang kita miliki hanyalah antarmuka, dan implementasi konkret dari antarmuka tersebut. Dalam praktiknya, Anda tidak benar-benar membutuhkan hal lain untuk melakukan desain OO tingkat tinggi.
Untuk "menyembunyikan" fakta bahwa kami telah menerapkan "pengurutan nama" dengan menggunakan kelas "QuickSort" dan "NameComparator", kami mungkin masih menulis metode pabrik di suatu tempat:
ISorter CreateNameSorter()
{
return new QuickSorter(new NameComparator());
}
Setiap kali Anda memiliki kelas abstrak, Anda dapat melakukan ini ... bahkan ketika ada hubungan re-entran alami antara kelas dasar dan turunan, biasanya membayar untuk membuatnya eksplisit.
Satu pemikiran terakhir: Semua yang kami lakukan di atas adalah "menulis" fungsi "NameSorting" dengan menggunakan fungsi "QuickSort" dan fungsi "NameComparison" ... dalam bahasa pemrograman fungsional, gaya pemrograman ini menjadi lebih alami, dengan kode lebih sedikit.
Jika Anda melihat java sebagai bahasa OOP,
" antarmuka tidak menyediakan implementasi metode " tidak lagi berlaku dengan peluncuran Java 8. Sekarang java menyediakan implementasi dalam antarmuka untuk metode standar.
Secara sederhana, saya ingin menggunakan
antarmuka: Untuk menerapkan kontrak dengan beberapa objek yang tidak terkait. Ini memberikan kemampuan " HAS A ".
kelas abstrak: Untuk menerapkan perilaku yang sama atau berbeda di antara beberapa objek terkait. Itu membangun hubungan " IS A ".
Situs web Oracle menyediakan perbedaan utama antara interface
dan abstract
kelas.
Pertimbangkan menggunakan kelas abstrak jika:
Pertimbangkan menggunakan antarmuka jika:
Serializable
antarmuka.Contoh:
Kelas abstrak ( IS A relation)
Pembaca adalah kelas abstrak.
BufferedReader adalah aReader
FileReader adalah aReader
FileReader
dan BufferedReader
digunakan untuk tujuan bersama: Membaca data, dan mereka dihubungkan melalui Reader
kelas.
Antarmuka ( kemampuan HAS A )
Serializable adalah antarmuka.
Asumsikan bahwa Anda memiliki dua kelas dalam aplikasi Anda, yang mengimplementasikan Serializable
antarmuka
Employee implements Serializable
Game implements Serializable
Di sini Anda tidak dapat membuat hubungan apa pun melalui Serializable
antarmuka antara Employee
dan Game
, yang dimaksudkan untuk tujuan yang berbeda. Keduanya mampu serialisasi negara dan perbandingan berakhir di sana.
Lihatlah tulisan-tulisan ini:
Bagaimana seharusnya saya menjelaskan perbedaan antara Interface dan kelas Abstrak?
OK, saya sendiri baru saja "ngobrol" - ini dia istilah awam (silakan koreksi saya kalau saya salah) - Saya tahu topik ini terlalu keras, tetapi seseorang mungkin akan menemukannya suatu hari nanti ...
Kelas abstrak memungkinkan Anda untuk membuat cetak biru, dan memungkinkan Anda untuk MENGONSTRUKSI (mengimplementasikan) properti dan metode yang Anda ingin SEMUA turunannya miliki.
Antarmuka di sisi lain hanya memungkinkan Anda untuk menyatakan bahwa Anda ingin properti dan / atau metode dengan nama yang diberikan ada di semua kelas yang mengimplementasikannya - tetapi tidak menentukan bagaimana Anda harus mengimplementasikannya. Juga, sebuah kelas dapat mengimplementasikan antarmuka BANYAK, tetapi hanya dapat memperluas kelas SATU Abstrak. Antarmuka lebih merupakan alat arsitektur tingkat tinggi (yang menjadi lebih jelas jika Anda mulai memahami pola desain) - Abstrak memiliki kaki di kedua kubu dan dapat melakukan beberapa pekerjaan kotor juga.
Mengapa menggunakan satu di atas yang lain? Yang pertama memungkinkan untuk definisi yang lebih konkret tentang keturunan - yang terakhir memungkinkan untuk polimorfisme yang lebih besar . Poin terakhir ini penting bagi pengguna akhir / pembuat kode, yang dapat memanfaatkan informasi ini untuk mengimplementasikan AP I (nterface) dalam berbagai kombinasi / bentuk yang sesuai dengan kebutuhan mereka.
Saya pikir ini adalah momen "bohlam" bagi saya - pikirkan antarmuka lebih sedikit dari perspektif penulis dan lebih dari itu dari setiap koder yang datang kemudian dalam rantai yang menambahkan implementasi ke proyek, atau memperluas API.
Dua sen saya:
Antarmuka pada dasarnya mendefinisikan kontrak, yang harus dipatuhi oleh setiap kelas pelaksana (mengimplementasikan anggota antarmuka). Itu tidak mengandung kode apa pun.
Di sisi lain, kelas abstrak dapat berisi kode, dan mungkin ada beberapa metode yang ditandai sebagai abstrak yang harus diterapkan oleh kelas pewarisan.
Situasi langka yang saya gunakan kelas abstrak adalah ketika saya memiliki beberapa fungsi default yang mewarisi kelas mungkin tidak menarik dalam mengesampingkan, katakanlah kelas dasar abstrak, yang mewarisi beberapa kelas khusus.
Contoh (yang sangat dasar!): Pertimbangkan kelas dasar yang disebut Nasabah yang memiliki metode abstrak seperti CalculatePayment()
, CalculateRewardPoints()
dan beberapa metode non-abstrak seperti GetName()
, SavePaymentDetails()
.
Kelas-kelas khusus menyukai RegularCustomer
dan GoldCustomer
akan mewarisi dari Customer
kelas dasar dan mengimplementasikan logika mereka sendiri CalculatePayment()
dan CalculateRewardPoints()
metode, tetapi menggunakan kembali GetName()
dan SavePaymentDetails()
metode.
Anda dapat menambahkan lebih banyak fungsi ke kelas abstrak (metode non-abstrak) tanpa memengaruhi kelas anak yang menggunakan versi yang lebih lama. Sedangkan menambahkan metode ke antarmuka akan mempengaruhi semua kelas yang mengimplementasikannya karena mereka sekarang perlu mengimplementasikan anggota antarmuka yang baru ditambahkan.
Kelas abstrak dengan semua anggota abstrak akan mirip dengan antarmuka.
Kapan melakukan hal yang sangat sederhana jika Anda memiliki konsep yang jelas dalam pikiran Anda.
Kelas abstrak dapat diturunkan sedangkan Antarmuka dapat Diimplementasikan. Ada beberapa perbedaan di antara keduanya. Saat Anda mendapatkan kelas abstrak, hubungan antara kelas turunan dan kelas dasar adalah hubungan 'adalah a'. misalnya, Anjing adalah Hewan, Domba adalah Hewan yang berarti bahwa kelas Berasal mewarisi beberapa properti dari kelas dasar.
Sedangkan untuk implementasi antarmuka, hubungannya "bisa". misalnya, Anjing bisa menjadi anjing mata-mata. Seekor anjing bisa menjadi anjing sirkus. Seekor anjing bisa menjadi anjing ras. Yang berarti Anda menerapkan metode tertentu untuk mendapatkan sesuatu.
Saya harap saya jelas.
1.Jika Anda membuat sesuatu yang menyediakan fungsionalitas umum untuk kelas yang tidak terkait, gunakan antarmuka.
2.Jika Anda membuat sesuatu untuk objek yang terkait erat dalam hierarki, gunakan kelas abstrak.
Saya menulis artikel kapan harus menggunakan kelas abstrak dan kapan harus menggunakan antarmuka. Ada lebih banyak perbedaan di antara mereka selain "satu IS-A ... dan satu CAN-DO ...". Bagi saya, itu adalah jawaban kalengan. Saya menyebutkan beberapa alasan kapan harus menggunakan keduanya. Semoga ini bisa membantu.
Saya pikir cara yang paling ringkas untuk mengatakannya adalah sebagai berikut:
Properti bersama => kelas abstrak.
Fungsionalitas bersama => antarmuka.
Dan untuk membuatnya lebih ringkas ...
Contoh Kelas Abstrak:
public abstract class BaseAnimal
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
}
public class Dog : BaseAnimal
{
public Dog() : base(4) { }
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
}
Karena hewan memiliki properti bersama - jumlah kaki dalam kasus ini - masuk akal untuk membuat kelas abstrak yang berisi properti bersama ini. Ini juga memungkinkan kami untuk menulis kode umum yang beroperasi pada properti itu. Sebagai contoh:
public static int CountAllLegs(List<BaseAnimal> animals)
{
int legCount = 0;
foreach (BaseAnimal animal in animals)
{
legCount += animal.NumberOfLegs;
}
return legCount;
}
Contoh Antarmuka:
public interface IMakeSound
{
void MakeSound();
}
public class Car : IMakeSound
{
public void MakeSound() => Console.WriteLine("Vroom!");
}
public class Vuvuzela : IMakeSound
{
public void MakeSound() => Console.WriteLine("VZZZZZZZZZZZZZ!");
}
Perhatikan di sini bahwa Vuvuzelas dan Cars adalah hal yang sama sekali berbeda, tetapi mereka memiliki fungsi yang sama: membuat suara. Jadi, sebuah antarmuka masuk akal di sini. Selanjutnya, ini akan memungkinkan pemrogram untuk mengelompokkan hal-hal yang membuat suara bersama di bawah antarmuka umum - IMakeSound
dalam hal ini. Dengan desain ini, Anda dapat menulis kode berikut:
List<IMakeSound> soundMakers = new List<ImakeSound>();
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Car());
soundMakers.Add(new Vuvuzela());
soundMakers.Add(new Vuvuzela());
foreach (IMakeSound soundMaker in soundMakers)
{
soundMaker.MakeSound();
}
Bisakah Anda tahu apa yang akan dihasilkan?
Terakhir, Anda dapat menggabungkan keduanya.
Contoh Gabungan:
public interface IMakeSound
{
void MakeSound();
}
public abstract class BaseAnimal : IMakeSound
{
public int NumberOfLegs { get; set; }
protected BaseAnimal(int numberOfLegs)
{
NumberOfLegs = numberOfLegs;
}
public abstract void MakeSound();
}
public class Cat : BaseAnimal
{
public Cat() : base(4) { }
public override void MakeSound() => Console.WriteLine("Meow!");
}
public class Human : BaseAnimal
{
public Human() : base(2) { }
public override void MakeSound() => Console.WriteLine("Hello, world!");
}
Di sini, kami membutuhkan semua BaseAnimal
suara, tetapi kami belum tahu implementasinya. Dalam kasus seperti itu, kita bisa abstrak implementasi antarmuka dan mendelegasikan implementasinya ke subkelasnya.
Satu poin terakhir, ingat bagaimana dalam contoh kelas abstrak kami dapat beroperasi pada properti bersama dari objek yang berbeda dan dalam contoh antarmuka kami dapat menjalankan fungsi bersama dari objek yang berbeda? Dalam contoh terakhir ini, kita bisa melakukan keduanya.
Kapan lebih suka kelas abstrak daripada antarmuka?
Kapan lebih suka antarmuka daripada kelas abstrak?
Kelas mungkin mewarisi dari hanya satu kelas dasar, jadi jika Anda ingin menggunakan kelas abstrak untuk memberikan polimorfisme ke sekelompok kelas, mereka semua harus mewarisi dari kelas itu. Kelas abstrak juga dapat menyediakan anggota yang telah diterapkan. Oleh karena itu, Anda dapat memastikan sejumlah fungsionalitas identik dengan kelas abstrak, tetapi tidak bisa dengan antarmuka.
Berikut adalah beberapa rekomendasi untuk membantu Anda memutuskan apakah akan menggunakan antarmuka atau kelas abstrak untuk memberikan polimorfisme untuk komponen Anda.
Disalin dari:
http://msdn.microsoft.com/en-us/library/scsyfw1d%28v=vs.71%29.aspx
Pertimbangkan menggunakan kelas abstrak jika salah satu dari pernyataan ini berlaku untuk situasi Anda:
Pertimbangkan menggunakan antarmuka jika salah satu dari pernyataan ini berlaku untuk situasi Anda:
Jawabannya bervariasi antar bahasa. Sebagai contoh, di Java suatu kelas dapat mengimplementasikan (mewarisi dari) beberapa antarmuka tetapi hanya mewarisi dari satu kelas abstrak. Jadi antarmuka memberi Anda lebih banyak fleksibilitas. Tapi ini tidak benar di C ++.
Bagi saya, saya akan menggunakan antarmuka dalam banyak kasus. Tapi saya lebih suka kelas abstrak dalam beberapa kasus.
Kelas-kelas di OO umumnya mengacu pada implementasi. Saya menggunakan kelas abstrak ketika saya ingin memaksa beberapa detail implementasi untuk anak-anak lain saya pergi dengan antarmuka.
Tentu saja, kelas abstrak berguna tidak hanya dalam memaksa implementasi tetapi juga berbagi beberapa detail spesifik di antara banyak kelas terkait.
Gunakan kelas abstrak jika Anda ingin memberikan beberapa implementasi dasar.
di java Anda dapat mewarisi dari satu (abstrak) kelas untuk "menyediakan" fungsionalitas dan Anda dapat mengimplementasikan banyak antarmuka untuk "memastikan" fungsionalitas
Murni berdasarkan pewarisan, Anda akan menggunakan Abstrak di mana Anda mendefinisikan dengan jelas hubungan abstrak yang abstrak (yaitu hewan -> kucing) dan / atau membutuhkan warisan properti virtual atau non-publik, terutama keadaan bersama (yang tidak bisa didukung oleh Antarmuka ).
Anda harus mencoba dan menyukai komposisi (melalui injeksi ketergantungan) daripada warisan di mana Anda bisa, dan perhatikan bahwa Antarmuka menjadi kontrak mendukung pengujian unit, pemisahan masalah dan (beragam bahasa) pewarisan berganda dengan cara yang tidak bisa dilakukan oleh abstrak.
Salah satu lokasi yang menarik di mana antarmuka lebih baik daripada kelas abstrak adalah ketika Anda perlu menambahkan fungsionalitas tambahan ke sekelompok objek (terkait atau tidak terkait). Jika Anda tidak bisa memberi mereka kelas abstrak dasar (misalnya, mereka sealed
sudah atau sudah memiliki orangtua), Anda bisa memberi mereka antarmuka dummy (kosong), dan kemudian cukup menulis metode ekstensi untuk antarmuka itu.
Ini bisa menjadi panggilan yang sangat sulit untuk ...
Satu pointer yang dapat saya berikan: Objek dapat mengimplementasikan banyak antarmuka, sedangkan objek hanya dapat mewarisi satu kelas dasar (dalam bahasa OO modern seperti c #, saya tahu C ++ memiliki banyak pewarisan - tetapi bukankah itu disukai?)
Kelas abstrak dapat memiliki implementasi.
Antarmuka tidak memiliki implementasi, itu hanya mendefinisikan semacam kontrak.
Mungkin juga ada beberapa perbedaan tergantung bahasa: misalnya C # tidak memiliki banyak pewarisan, tetapi beberapa antarmuka dapat diimplementasikan dalam suatu kelas.
Aturan jempol dasar adalah: Untuk "Nouns" gunakan kelas Abstrak dan untuk "Verbs" gunakan antarmuka
Misalnya: car
adalah kelas abstrak dan drive
, kita bisa menjadikannya antarmuka.
drive
di dalam mobil - yaitu kelas abstrak.