Seseorang menemukan frasa ini ketika membaca tentang pola desain.
Tapi saya tidak mengerti, bisakah seseorang menjelaskan ini untuk saya?
Seseorang menemukan frasa ini ketika membaca tentang pola desain.
Tapi saya tidak mengerti, bisakah seseorang menjelaskan ini untuk saya?
Jawaban:
Antarmuka hanyalah kontrak atau tanda tangan dan mereka tidak tahu apa-apa tentang implementasi.
Pengodean terhadap antarmuka berarti, kode klien selalu memegang objek Antarmuka yang disediakan oleh pabrik. Setiap instance yang dikembalikan oleh pabrik akan bertipe Interface yang harus diterapkan oleh setiap kelas kandidat pabrik. Dengan cara ini program klien tidak khawatir tentang implementasi dan tanda tangan antarmuka menentukan apa semua operasi dapat dilakukan. Ini dapat digunakan untuk mengubah perilaku suatu program pada saat run-time. Ini juga membantu Anda untuk menulis program yang jauh lebih baik dari sudut pandang pemeliharaan.
Inilah contoh dasar untuk Anda.
public enum Language
{
English, German, Spanish
}
public class SpeakerFactory
{
public static ISpeaker CreateSpeaker(Language language)
{
switch (language)
{
case Language.English:
return new EnglishSpeaker();
case Language.German:
return new GermanSpeaker();
case Language.Spanish:
return new SpanishSpeaker();
default:
throw new ApplicationException("No speaker can speak such language");
}
}
}
[STAThread]
static void Main()
{
//This is your client code.
ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
speaker.Speak();
Console.ReadLine();
}
public interface ISpeaker
{
void Speak();
}
public class EnglishSpeaker : ISpeaker
{
public EnglishSpeaker() { }
#region ISpeaker Members
public void Speak()
{
Console.WriteLine("I speak English.");
}
#endregion
}
public class GermanSpeaker : ISpeaker
{
public GermanSpeaker() { }
#region ISpeaker Members
public void Speak()
{
Console.WriteLine("I speak German.");
}
#endregion
}
public class SpanishSpeaker : ISpeaker
{
public SpanishSpeaker() { }
#region ISpeaker Members
public void Speak()
{
Console.WriteLine("I speak Spanish.");
}
#endregion
}
Ini hanyalah contoh dasar dan penjelasan aktual dari prinsip tersebut berada di luar cakupan jawaban ini.
Saya telah memperbarui contoh di atas dan menambahkan Speaker
kelas dasar abstrak . Dalam pembaruan ini, saya menambahkan fitur ke semua Pembicara ke "SayHello". Semua pembicara berbicara "Hello World". Jadi itu fitur umum dengan fungsi yang serupa. Lihat diagram kelas dan Anda akan menemukan bahwa Speaker
kelas abstrak mengimplementasikan ISpeaker
antarmuka dan menandai Speak()
sebagai abstrak yang berarti bahwa setiap implementasi Speaker bertanggung jawab untuk mengimplementasikan Speak()
metode karena bervariasi dari Speaker
ke Speaker
. Tetapi semua pembicara mengatakan "Halo" dengan suara bulat. Jadi di kelas Speaker abstrak kita mendefinisikan metode yang mengatakan "Hello World" dan setiap Speaker
implementasi akan mendapatkan SayHello()
metode tersebut.
Pertimbangkan suatu kasus di mana SpanishSpeaker
tidak dapat Mengatakan Halo sehingga dalam kasus itu Anda dapat mengganti SayHello()
metode untuk Penutur Bahasa Spanyol dan meningkatkan pengecualian yang tepat.
Harap perhatikan bahwa, kami belum melakukan perubahan pada Interface ISpeaker. Dan kode klien dan SpeakerFactory juga tetap tidak terpengaruh tidak berubah. Dan inilah yang kami capai dengan Programming-to-Interface .
Dan kita dapat mencapai perilaku ini hanya dengan menambahkan kelas dasar abstrak Speaker dan beberapa modifikasi kecil di Setiap implementasi sehingga meninggalkan program asli tidak berubah. Ini adalah fitur yang diinginkan dari aplikasi apa pun dan itu membuat aplikasi Anda mudah dipelihara.
public enum Language
{
English, German, Spanish
}
public class SpeakerFactory
{
public static ISpeaker CreateSpeaker(Language language)
{
switch (language)
{
case Language.English:
return new EnglishSpeaker();
case Language.German:
return new GermanSpeaker();
case Language.Spanish:
return new SpanishSpeaker();
default:
throw new ApplicationException("No speaker can speak such language");
}
}
}
class Program
{
[STAThread]
static void Main()
{
//This is your client code.
ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
speaker.Speak();
Console.ReadLine();
}
}
public interface ISpeaker
{
void Speak();
}
public abstract class Speaker : ISpeaker
{
#region ISpeaker Members
public abstract void Speak();
public virtual void SayHello()
{
Console.WriteLine("Hello world.");
}
#endregion
}
public class EnglishSpeaker : Speaker
{
public EnglishSpeaker() { }
#region ISpeaker Members
public override void Speak()
{
this.SayHello();
Console.WriteLine("I speak English.");
}
#endregion
}
public class GermanSpeaker : Speaker
{
public GermanSpeaker() { }
#region ISpeaker Members
public override void Speak()
{
Console.WriteLine("I speak German.");
this.SayHello();
}
#endregion
}
public class SpanishSpeaker : Speaker
{
public SpanishSpeaker() { }
#region ISpeaker Members
public override void Speak()
{
Console.WriteLine("I speak Spanish.");
}
public override void SayHello()
{
throw new ApplicationException("I cannot say Hello World.");
}
#endregion
}
List
sebagai, Anda masih bisa berasumsi bahwa akses acak cepat dengan berulang kali menelepon get(i)
.
Pikirkan sebuah antarmuka sebagai kontrak antara objek dan kliennya. Itu adalah antarmuka yang menentukan hal-hal yang dapat dilakukan objek, dan tanda tangan untuk mengakses hal-hal itu.
Implementasi adalah perilaku aktual. Katakan misalnya Anda memiliki semacam metode (). Anda dapat menerapkan QuickSort atau MergeSort. Itu seharusnya tidak menjadi masalah bagi pemanggilan kode kode klien selama antarmuka tidak berubah.
Perpustakaan seperti Java API dan .NET Framework banyak menggunakan antarmuka karena jutaan programmer menggunakan objek yang disediakan. Pembuat perpustakaan ini harus sangat berhati-hati agar mereka tidak mengubah antarmuka ke kelas-kelas di perpustakaan ini karena itu akan mempengaruhi semua programmer menggunakan perpustakaan. Di sisi lain mereka dapat mengubah implementasi sesuka mereka.
Jika, sebagai seorang programmer, Anda membuat kode terhadap implementasi maka segera setelah itu mengubah kode Anda berhenti bekerja. Jadi pikirkan manfaat antarmuka dengan cara ini:
Ini berarti Anda harus mencoba menulis kode Anda sehingga menggunakan abstraksi (kelas abstrak atau antarmuka) alih-alih implementasinya secara langsung.
Biasanya implementasi disuntikkan ke dalam kode Anda melalui konstruktor atau panggilan metode. Jadi, kode Anda tahu tentang antarmuka atau kelas abstrak dan dapat memanggil apa pun yang didefinisikan dalam kontrak ini. Sebagai objek aktual (implementasi antarmuka / kelas abstrak) digunakan, panggilan beroperasi pada objek.
Ini adalah subset dari Liskov Substitution Principle
(LSP), L dari SOLID
prinsip.
Contoh dalam .NET adalah untuk kode dengan IList
alih - alih List
atau Dictionary
, sehingga Anda bisa menggunakan kelas apa pun yang mengimplementasikan secara IList
bergantian dalam kode Anda:
// myList can be _any_ object that implements IList
public int GetListCount(IList myList)
{
// Do anything that IList supports
return myList.Count();
}
Contoh lain dari Base Class Library (BCL) adalah ProviderBase
kelas abstrak - ini menyediakan beberapa infrastruktur, dan yang penting artinya semua implementasi penyedia dapat digunakan secara bergantian jika Anda membuat kode menentangnya.
Jika Anda menulis Kelas Mobil di era Combustion-Car, maka ada kemungkinan besar Anda akan mengimplementasikan oilChange () sebagai bagian dari Kelas ini. Tetapi, ketika mobil listrik diperkenalkan, Anda akan berada dalam masalah karena tidak ada penggantian oli untuk mobil ini, dan tidak ada implementasinya.
Solusi untuk masalah ini adalah memiliki antarmuka PerformMaintenance () di kelas Mobil dan menyembunyikan detail di dalam implementasi yang sesuai. Setiap jenis mobil akan menyediakan implementasinya sendiri untuk performMaintenance (). Sebagai pemilik Mobil yang harus Anda hadapi adalah performMaintenance () dan tidak perlu khawatir beradaptasi ketika ada PERUBAHAN.
class MaintenanceSpecialist {
public:
virtual int performMaintenance() = 0;
};
class CombustionEnginedMaintenance : public MaintenanceSpecialist {
int performMaintenance() {
printf("combustionEnginedMaintenance: We specialize in maintenance of Combustion engines \n");
return 0;
}
};
class ElectricMaintenance : public MaintenanceSpecialist {
int performMaintenance() {
printf("electricMaintenance: We specialize in maintenance of Electric Cars \n");
return 0;
}
};
class Car {
public:
MaintenanceSpecialist *mSpecialist;
virtual int maintenance() {
printf("Just wash the car \n");
return 0;
};
};
class GasolineCar : public Car {
public:
GasolineCar() {
mSpecialist = new CombustionEnginedMaintenance();
}
int maintenance() {
mSpecialist->performMaintenance();
return 0;
}
};
class ElectricCar : public Car {
public:
ElectricCar() {
mSpecialist = new ElectricMaintenance();
}
int maintenance(){
mSpecialist->performMaintenance();
return 0;
}
};
int _tmain(int argc, _TCHAR* argv[]) {
Car *myCar;
myCar = new GasolineCar();
myCar->maintenance(); /* I dont know what is involved in maintenance. But, I do know the maintenance has to be performed */
myCar = new ElectricCar();
myCar->maintenance();
return 0;
}
Penjelasan tambahan: Anda adalah pemilik mobil yang memiliki beberapa mobil. Anda mengukir layanan yang ingin Anda outsourcing. Dalam kasus kami, kami ingin melakukan outsourcing pekerjaan pemeliharaan semua mobil.
Anda tidak ingin khawatir tentang mengaitkan jenis mobil dengan penyedia layanan. Anda cukup menentukan kapan Anda ingin menjadwalkan pemeliharaan dan menjalankannya. Perusahaan jasa yang tepat harus terjun dan melakukan pekerjaan pemeliharaan.
Pendekatan alternatif.
Anda memohon pekerjaan dan melakukannya sendiri. Di sini Anda akan melakukan pekerjaan pemeliharaan yang sesuai.
Apa kelemahan dari pendekatan kedua? Anda mungkin bukan ahli dalam menemukan cara terbaik untuk melakukan perawatan. Tugas Anda adalah mengendarai mobil dan menikmatinya. Tidak berada dalam bisnis mempertahankannya.
Apa kelemahan dari pendekatan pertama? Ada biaya overhead untuk menemukan perusahaan, dll. Kecuali jika Anda adalah perusahaan rental mobil, itu mungkin tidak sepadan dengan usaha.
Pernyataan ini tentang kopling. Salah satu alasan potensial untuk menggunakan pemrograman berorientasi objek adalah penggunaan kembali. Jadi misalnya Anda dapat membagi algoritme Anda di antara dua objek yang berkolaborasi A dan B. Ini mungkin berguna untuk pembuatan nanti algoritma lain, yang mungkin menggunakan kembali satu atau yang lain dari dua objek. Namun, ketika benda-benda itu berkomunikasi (mengirim pesan - metode panggilan), mereka menciptakan ketergantungan satu sama lain. Tetapi jika Anda ingin menggunakan satu tanpa yang lain, Anda perlu menentukan apa yang harus dilakukan beberapa objek lain C lakukan untuk objek A jika kita mengganti B. Deskripsi tersebut disebut antarmuka. Ini memungkinkan objek A untuk berkomunikasi tanpa perubahan dengan objek yang berbeda bergantung pada antarmuka. Pernyataan yang Anda sebutkan mengatakan bahwa jika Anda berencana untuk menggunakan kembali beberapa bagian dari suatu algoritma (atau lebih umum program), Anda harus membuat antarmuka dan mengandalkan mereka,
Seperti yang orang lain katakan, itu berarti bahwa kode panggilan Anda hanya harus tahu tentang orangtua yang abstrak, BUKAN kelas pelaksana aktual yang akan melakukan pekerjaan.
Apa yang membantu untuk memahami ini adalah MENGAPA Anda harus selalu memprogram ke antarmuka. Ada banyak alasan, tetapi dua yang paling mudah dijelaskan adalah
1) Pengujian.
Katakanlah saya memiliki seluruh kode basis data dalam satu kelas. Jika program saya tahu tentang kelas beton, saya hanya dapat menguji kode saya dengan benar-benar menjalankannya terhadap kelas itu. Saya menggunakan -> berarti "berbicara dengan".
WorkerClass -> DALClass Namun, mari kita tambahkan antarmuka ke dalam campuran.
WorkerClass -> IDAL -> DALClass.
Jadi DALClass mengimplementasikan antarmuka IDAL, dan kelas pekerja HANYA memanggil melalui ini.
Sekarang jika kita ingin menulis tes untuk kode, kita bisa membuat kelas sederhana yang hanya bertindak seperti database.
WorkerClass -> IDAL -> IFakeDAL.
2) Penggunaan Kembali
Mengikuti contoh di atas, katakanlah kita ingin pindah dari SQL Server (yang menggunakan DALClass konkret kami) ke MonogoDB. Ini akan membutuhkan pekerjaan besar, tetapi BUKAN jika kita sudah memprogram ke antarmuka. Dalam hal ini kami hanya menulis kelas DB baru, dan berubah (melalui pabrik)
WorkerClass -> IDAL -> DALClass
untuk
WorkerClass -> IDAL -> MongoDBClass