Antarmuka - Apa gunanya?


269

Alasan untuk antarmuka benar-benar menghindari saya. Dari apa yang saya mengerti, ini adalah semacam pekerjaan untuk multi-inheritance tidak ada yang tidak ada di C # (atau jadi saya diberitahu).

Yang saya lihat adalah, Anda menetapkan beberapa anggota dan fungsi, yang kemudian harus didefinisikan ulang di kelas lagi. Sehingga membuat antarmuka menjadi mubazir. Rasanya seperti sintaksis ... yah, sampah bagi saya (Tolong jangan tersinggung berarti. Sampah seperti dalam hal-hal yang tidak berguna).

Dalam contoh yang diberikan di bawah ini diambil dari thread antarmuka C # berbeda pada stack overflow, saya hanya akan membuat kelas dasar yang disebut Pizza bukan antarmuka.

contoh mudah (diambil dari kontribusi stack overflow yang berbeda)

public interface IPizza
{
    public void Order();
}

public class PepperoniPizza : IPizza
{
    public void Order()
    {
        //Order Pepperoni pizza
    }
}

public class HawaiiPizza : IPizza
{
    public void Order()
    {
        //Order HawaiiPizza
    }
}

Saya merasa ada duplikat dari pertanyaan ini di sini pada SO, tetapi mereka semua tampaknya hanya menjelaskan bagian kontrak dari sebuah antarmuka jadi saya tidak yakin mereka berlaku.
Lasse V. Karlsen

7
mencoba menjadi pengguna yang baik dan rapi, saya cenderung mencari jawaban saya terlebih dahulu di berbagai forum sebelum saya memposting sesuatu. Sayangnya, kebanyakan dari mereka mulai pada tahap berikutnya dan sisanya tidak membantu. Saya sudah berjuang dengan dasar "Mengapa melakukannya?" sepertinya bagi saya seperti tidak perlu rumit itu. Btw. Terima kasih untuk semua jawaban yang sangat cepat. Saya harus mencernanya terlebih dahulu, tetapi saya pikir sekarang saya memiliki gagasan yang cukup bagus tentang hal itu. Sepertinya saya selalu melihatnya dari sudut yang berbeda. Terima kasih banyak atas bantuan Anda.
Nebelhom


1
Antarmuka juga membantu membangun pewarisan seperti untuk structtipe.
ja72

3
Hmm, OP bertanya "Dari apa yang saya mengerti, antarmuka adalah semacam pekerjaan untuk multi-inheritance tidak ada yang tidak ada di C #. (Selain itu, dalam contoh pizza buku teks yang dikutip) Saya hanya akan gunakan kelas dasar sebagai ganti antarmuka ". Dan kemudian sebagian besar jawaban memberi contoh yang dapat diimplementasikan oleh kelas dasar (abstrak), atau memberi contoh untuk menunjukkan bagaimana antarmuka diperlukan untuk skenario multi-warisan. Semua jawaban itu bagus, tetapi bukankah mereka hanya mengulangi sesuatu yang sudah diketahui OP? Tidak heran OP akhirnya memilih jawaban tanpa contoh. LOL
RayLuo

Jawaban:


176

Intinya adalah bahwa antarmuka merupakan kontrak . Seperangkat metode publik yang harus dimiliki oleh setiap kelas pelaksana. Secara teknis, antarmuka hanya mengatur sintaksis, yaitu metode apa yang ada, argumen apa yang mereka dapatkan dan apa yang mereka kembalikan. Biasanya mereka merangkum semantik juga, meskipun itu hanya dengan dokumentasi.

Anda kemudian dapat memiliki implementasi antarmuka yang berbeda dan menukarnya sesuka hati. Dalam contoh Anda, karena setiap instance pizza adalah IPizzaAnda dapat menggunakan di IPizzamana pun Anda menangani instance dari jenis pizza yang tidak dikenal. Contoh apa pun yang jenisnya diwarisi dari IPizzadijamin dapat dipesan, karena memiliki Order()metode.

Python tidak diketik secara statis, oleh karena itu tipe disimpan dan melihat saat runtime. Jadi, Anda dapat mencoba memanggil Order()metode pada objek apa pun. Runtime senang selama objek memiliki metode seperti itu dan mungkin hanya mengangkat bahu dan berkata "Meh." Jika tidak. Tidak demikian halnya dalam C #. Compiler bertanggung jawab untuk membuat panggilan yang benar dan jika hanya memiliki beberapa acak objectkompiler belum tahu apakah instance selama runtime akan memiliki metode itu. Dari sudut pandang kompiler itu tidak valid karena tidak dapat memverifikasinya. (Anda dapat melakukan hal-hal seperti itu dengan refleksi atau dynamickata kunci, tapi itu agak jauh sekarang, saya kira.)

Perhatikan juga bahwa antarmuka dalam arti biasa tidak harus berupa C # interface, bisa juga kelas abstrak atau bahkan kelas normal (yang bisa berguna jika semua subclass perlu berbagi beberapa kode umum - dalam kebanyakan kasus Namun, interfacecukuplah).


1
+1, walaupun saya tidak akan mengatakan bahwa antarmuka (dalam arti kontrak) dapat berupa abstrak atau kelas normal.
Groo

3
Saya akan menambahkan bahwa Anda tidak dapat berharap untuk memahami antarmuka dalam beberapa menit. Saya pikir itu tidak masuk akal untuk memahami antarmuka jika Anda tidak memiliki tahun pengalaman pemrograman berorientasi objek. Anda dapat menambahkan beberapa tautan ke buku. Saya akan menyarankan: Ketergantungan Injeksi dalam. NET yang benar-benar seberapa dalam lubang rabit berjalan, bukan hanya pengantar yang lembut.
Knut

2
Ah, ada masalah saya tidak tahu DI. Tapi saya kira masalah utama penanya adalah mengapa mereka diperlukan ketika di Python semuanya bekerja tanpa. Itulah yang coba diberikan paragraf terbesar dari jawaban saya. Saya pikir tidak perlu menggali setiap pola dan praktik yang menggunakan antarmuka di sini.
Joey

1
Nah, maka pertanyaan Anda menjadi: "Mengapa menggunakan bahasa yang diketik secara statis ketika bahasa yang diketik secara dinamis lebih mudah diprogram?". Meskipun saya bukan ahli untuk menjawab pertanyaan itu, saya berani mengatakan bahwa kinerja adalah masalah yang menentukan. Ketika panggilan ke objek python dibuat, proses harus memutuskan pada saat run time apakah objek itu memiliki metode yang disebut "Order", sedangkan jika Anda membuat panggilan ke objek C #, sudah ditetapkan bahwa ia mengimplementasikan metode itu, dan panggilan dapat dilakukan ke alamat seperti itu.
Boluc Papuccuoglu

3
@BolucPapuccuoglu: Selain itu, dengan objek yang diketik secara statis jika ada yang tahu fooimplementasinya IWidget, maka seorang programmer yang melihat panggilan untuk foo.woozle()dapat melihat dokumentasi IWidgetdan mengetahui apa yang seharusnya dilakukan metode itu . Pemrogram mungkin tidak memiliki cara untuk mengetahui dari mana kode untuk implementasi aktual akan berasal, tetapi jenis apa pun yang mematuhi IWidgetkontrak antarmuka akan menerapkan foodengan cara yang konsisten dengan kontrak itu. Sebaliknya, dalam bahasa yang dinamis, tidak akan ada titik rujukan yang jelas untuk apa yang foo.woozle()seharusnya.
supercat

440

Tidak ada yang benar-benar menjelaskan secara sederhana bagaimana antarmuka berguna, jadi saya akan mencobanya (dan mencuri sedikit ide dari jawaban Shamim).

Mari kita mengambil ide layanan pemesanan pizza. Anda dapat memiliki beberapa jenis pizza dan tindakan umum untuk setiap pizza adalah menyiapkan pesanan dalam sistem. Setiap pizza harus disiapkan tetapi masing-masing pizza disiapkan secara berbeda . Misalnya, ketika pizza kerak isian dipesan sistem mungkin harus memverifikasi bahan-bahan tertentu yang tersedia di restoran dan menyisihkannya yang tidak diperlukan untuk pizza hidangan dalam.

Saat menulis ini dalam kode, secara teknis Anda bisa melakukannya

public class Pizza()
{
    public void Prepare(PizzaType tp)
    {
        switch (tp)
        {
            case PizzaType.StuffedCrust:
                // prepare stuffed crust ingredients in system
                break;

            case PizzaType.DeepDish:
                // prepare deep dish ingredients in system
                break;

            //.... etc.
        }
    }
}

Namun, pizza parabola (dalam istilah C #) mungkin memerlukan properti yang berbeda untuk ditetapkan dalam Prepare()metode dari kerak yang diisi, dan dengan demikian Anda berakhir dengan banyak properti opsional, dan kelas tidak skala dengan baik (bagaimana jika Anda menambahkan baru jenis pizza).

Cara yang tepat untuk menyelesaikan ini adalah dengan menggunakan antarmuka. Antarmuka menyatakan bahwa semua Pizza dapat disiapkan, tetapi setiap pizza dapat disiapkan secara berbeda. Jadi, jika Anda memiliki antarmuka berikut:

public interface IPizza
{
    void Prepare();
}

public class StuffedCrustPizza : IPizza
{
    public void Prepare()
    {
        // Set settings in system for stuffed crust preparations
    }
}

public class DeepDishPizza : IPizza
{
    public void Prepare()
    {
        // Set settings in system for deep dish preparations
    }
}

Sekarang kode penanganan pesanan Anda tidak perlu tahu persis jenis pizza apa yang dipesan untuk menangani bahan. Itu hanya memiliki:

public PreparePizzas(IList<IPizza> pizzas)
{
    foreach (IPizza pizza in pizzas)
        pizza.Prepare();
}

Meskipun setiap jenis pizza disiapkan secara berbeda, bagian kode ini tidak harus peduli dengan jenis pizza yang kita hadapi, ia hanya tahu bahwa itu dipanggil untuk pizza dan oleh karena itu setiap panggilan untuk Prepareakan secara otomatis menyiapkan setiap pizza dengan benar berdasarkan jenisnya, meskipun koleksinya memiliki banyak jenis pizza.


11
+1, saya menyukai contoh menggunakan Daftar untuk menunjukkan penggunaan antarmuka.
danielunderwood

21
Jawaban yang bagus, tetapi mungkin mengubahnya untuk menjelaskan mengapa antarmuka dalam kasus ini lebih baik daripada hanya menggunakan kelas abstrak (untuk contoh sederhana seperti itu, kelas abstrak memang mungkin lebih baik?)
Jez

3
Ini jawaban terbaik. Ada sedikit kesalahan ketik atau mungkin Anda tinggal menyalin dan menempelkan kode. Di antarmuka C # Anda tidak mendeklarasikan pengubah akses seperti public. Jadi di dalam antarmuka seharusnyavoid Prepare();
Dush

26
Saya tidak melihat bagaimana ini menjawab pertanyaan. Contoh ini bisa saja dilakukan dengan mudah dengan kelas dasar dan metode abstrak, yang persis seperti yang ditunjukkan pertanyaan awal.
Jamie Kitson

4
Antarmuka tidak benar-benar masuk ke hak mereka sampai Anda memiliki banyak dari mereka berkeliling. Anda hanya dapat mewarisi dari satu kelas, abstrak atau tidak, tetapi Anda dapat mengimplementasikan banyak antarmuka berbeda dalam satu kelas yang Anda inginkan. Tiba-tiba Anda tidak perlu pintu di kusen pintu; apa pun yang bisa dilakukan IOpenable, baik itu pintu, jendela, kotak surat, apa saja.
mtnielsen

123

Bagi saya, ketika memulai, intinya hanya menjadi jelas ketika Anda berhenti memandanginya sebagai hal yang membuat kode Anda lebih mudah / lebih cepat untuk ditulis - ini bukan tujuan mereka. Mereka memiliki sejumlah kegunaan:

(Ini akan kehilangan analogi pizza, karena tidak mudah memvisualisasikan penggunaan ini)

Katakanlah Anda membuat game sederhana di layar dan ia akan memiliki makhluk yang berinteraksi dengan Anda.

A: Mereka dapat membuat kode Anda lebih mudah dipelihara di masa depan dengan memperkenalkan kopling longgar antara ujung depan dan implementasi ujung belakang Anda.

Anda dapat menulis ini sebagai permulaan, karena hanya akan ada troll:

// This is our back-end implementation of a troll
class Troll
{
    void Walk(int distance)
    {
        //Implementation here
    }
}

Paling depan:

function SpawnCreature()
{
    Troll aTroll = new Troll();

    aTroll.Walk(1);
}

Dua minggu kemudian, pemasaran memutuskan Anda juga membutuhkan Orc, ketika mereka membaca tentang mereka di twitter, jadi Anda harus melakukan sesuatu seperti:

class Orc
{
    void Walk(int distance)
    {
        //Implementation (orcs are faster than trolls)
    }
}

Paling depan:

void SpawnCreature(creatureType)
{
    switch(creatureType)
    {
         case Orc:

           Orc anOrc = new Orc();
           anORc.Walk();

          case Troll:

            Troll aTroll = new Troll();
             aTroll.Walk();
    }
}

Dan Anda dapat melihat bagaimana ini mulai menjadi berantakan. Anda dapat menggunakan antarmuka di sini sehingga ujung depan Anda akan ditulis satu kali dan (inilah bagian penting) yang diuji, dan Anda kemudian dapat menyambungkan item ujung belakang lebih lanjut sesuai kebutuhan:

interface ICreature
{
    void Walk(int distance)
}

public class Troll : ICreature
public class Orc : ICreature 

//etc

Ujung depan adalah:

void SpawnCreature(creatureType)
{
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    creature.Walk();
}

Ujung depan sekarang hanya peduli dengan antarmuka ICreature - tidak peduli tentang implementasi internal troll atau orc, tetapi hanya pada kenyataan bahwa mereka menerapkan ICreature.

Poin penting yang perlu diperhatikan ketika melihat ini dari sudut pandang ini adalah Anda juga bisa dengan mudah menggunakan kelas makhluk abstrak, dan dari perspektif ini, ini memiliki efek yang sama .

Dan Anda dapat mengekstrak ciptaan ke pabrik:

public class CreatureFactory {

 public ICreature GetCreature(creatureType)
 {
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    return creature;
  }
}

Dan ujung depan kita kemudian akan menjadi:

CreatureFactory _factory;

void SpawnCreature(creatureType)
{
    ICreature creature = _factory.GetCreature(creatureType);

    creature.Walk();
}

Ujung depan sekarang bahkan tidak harus memiliki referensi ke perpustakaan tempat Troll dan Orc diimplementasikan (asalkan pabrik berada di perpustakaan terpisah) - tidak perlu tahu apa-apa tentang mereka sama sekali.

B: Katakanlah Anda memiliki fungsionalitas yang hanya dimiliki oleh beberapa makhluk dalam struktur data Anda yang homogen , misalnya

interface ICanTurnToStone
{
   void TurnToStone();
}

public class Troll: ICreature, ICanTurnToStone

Ujung depan bisa jadi:

void SpawnCreatureInSunlight(creatureType)
{
    ICreature creature;

    switch(creatureType)
    {
         case Orc:

           creature = new Orc();

          case Troll:

            creature = new Troll();
    }

    creature.Walk();

    if (creature is ICanTurnToStone)
    {
       (ICanTurnToStone)creature.TurnToStone();
    }
}

C: Penggunaan untuk injeksi ketergantungan

Sebagian besar kerangka kerja ketergantungan injeksi lebih mudah untuk dikerjakan ketika ada hubungan yang sangat longgar antara kode ujung depan dan implementasi ujung belakang. Jika kami mengambil contoh pabrik kami di atas dan meminta pabrik kami mengimplementasikan antarmuka:

public interface ICreatureFactory {
     ICreature GetCreature(string creatureType);
}

Ujung depan kami kemudian dapat menyuntikkan ini (misalnya, pengontrol API MVC) melalui konstruktor (biasanya):

public class CreatureController : Controller {

   private readonly ICreatureFactory _factory;

   public CreatureController(ICreatureFactory factory) {
     _factory = factory;
   }

   public HttpResponseMessage TurnToStone(string creatureType) {

       ICreature creature = _factory.GetCreature(creatureType);

       creature.TurnToStone();

       return Request.CreateResponse(HttpStatusCode.OK);
   }
}

Dengan kerangka kerja DI kami (mis. Ninject atau Autofac), kami dapat mengaturnya sehingga pada saat runtime instance CreatureFactory akan dibuat setiap kali ICreatureFactory diperlukan dalam sebuah konstruktor - ini membuat kode kami bagus dan sederhana.

Ini juga berarti bahwa ketika kita menulis unit test untuk controller kita, kita dapat memberikan ICreatureFactory yang diejek (mis. Jika implementasi konkret membutuhkan akses DB, kita tidak ingin unit test kita bergantung pada hal itu) dan dengan mudah menguji kode pada controller kita. .

D: Ada kegunaan lain misalnya Anda memiliki dua proyek A dan B yang karena alasan 'warisan' tidak terstruktur dengan baik, dan A memiliki referensi ke B.

Anda kemudian menemukan fungsionalitas dalam B yang perlu memanggil metode yang sudah dalam A. Anda tidak dapat melakukannya menggunakan implementasi konkret saat Anda mendapatkan referensi melingkar.

Anda dapat memiliki antarmuka yang dinyatakan dalam B yang diterapkan oleh kelas di A. Metode Anda dalam B dapat diberikan instance kelas yang mengimplementasikan antarmuka tanpa masalah, meskipun objek konkret adalah tipe dalam A.


6
Bukankah itu menjengkelkan ketika Anda menemukan jawaban yang mencoba mengatakan siapa Anda sebenarnya, tetapi apakah itu jauh lebih baik - stackoverflow.com/a/93998/117215
Paddy

18
Berita baiknya sekarang adalah halaman yang sudah mati dan milik Anda tidak :). Contoh yang baik!
Sealer_05

1
Diskusi C Anda sedikit kehilangan saya. Tapi, saya suka diskusi A dan B Anda karena keduanya pada dasarnya menjelaskan bagaimana antarmuka dapat digunakan untuk menyediakan jenis fungsionalitas umum di beberapa kelas. Area antarmuka yang masih kabur bagi saya adalah bagaimana antarmuka digunakan untuk kopling yang lebih longgar. Mungkin itu yang sedang dibahas dalam diskusi C Anda? Jika demikian, saya rasa saya perlu contoh yang lebih rinci :)
user2075599

1
Menurut saya dalam contoh Anda bahwa ICreature akan lebih cocok untuk menjadi kelas dasar abstrak (Creature?) Untuk Troll dan Orc. Maka logika umum antara makhluk bisa diimplementasikan di sana. Yang artinya Anda tidak memerlukan antarmuka ICreature sama sekali ...
Skarsnik

1
@ Skarsnik - cukup, ini adalah tentang catatan ini: "Poin penting untuk diperhatikan ketika melihat ini dari sudut pandang ini adalah bahwa Anda juga bisa dengan mudah menggunakan kelas makhluk abstrak, dan dari perspektif ini, ini memiliki hal yang sama efek."
Paddy

33

Berikut adalah contoh Anda yang dijelaskan kembali:

public interface IFood // not Pizza
{
    public void Prepare();

}

public class Pizza : IFood
{
    public void Prepare() // Not order for explanations sake
    {
        //Prepare Pizza
    }
}

public class Burger : IFood
{
    public void Prepare()
    {
        //Prepare Burger
    }
}

27

Contoh di atas tidak masuk akal. Anda bisa menyelesaikan semua contoh di atas menggunakan kelas (kelas abstrak jika Anda ingin berperilaku hanya sebagai kontrak ):

public abstract class Food {
    public abstract void Prepare();
}

public class Pizza : Food  {
    public override void Prepare() { /* Prepare pizza */ }
}

public class Burger : Food  {
    public override void Prepare() { /* Prepare Burger */ }
}

Anda mendapatkan perilaku yang sama dengan antarmuka. Anda dapat membuat List<Food>dan mengulangi bahwa tanpa mengetahui kelas apa yang duduk di atas.

Contoh yang lebih memadai adalah multiple inheritance:

public abstract class MenuItem {
    public string Name { get; set; }
    public abstract void BringToTable();
}

// Notice Soda only inherits from MenuItem
public class Soda : MenuItem {
    public override void BringToTable() { /* Bring soda to table */ }
}


// All food needs to be cooked (real food) so we add this
// feature to all food menu items
public interface IFood {
    void Cook();
}

public class Pizza : MenuItem, IFood {
    public override void BringToTable() { /* Bring pizza to table */ }
    public void Cook() { /* Cook Pizza */ }
}

public class Burger : MenuItem, IFood {
    public override void BringToTable() { /* Bring burger to table */ }
    public void Cook() { /* Cook Burger */ }
}

Kemudian Anda bisa menggunakan semuanya sebagai MenuItemdan tidak peduli bagaimana mereka menangani setiap pemanggilan metode.

public class Waiter {
    public void TakeOrder(IEnumerable<MenuItem> order) 
    {
        // Cook first
        // (all except soda because soda is not IFood)
        foreach (var food in order.OfType<IFood>())
            food.Cook();

        // Bring them all to the table
        // (everything, including soda, pizza and burger because they're all menu items)
        foreach (var menuItem in order)
            menuItem.BringToTable();
    }
}

2
Saya harus menggulir ke bawah sini untuk menemukan jawaban yang benar-benar menjawab pertanyaan "mengapa tidak menggunakan kelas abstrak alih-alih antarmuka?". Tampaknya itu benar-benar hanya menangani kekurangan beberapa warisan dari c #.
SyntaxRules

2
Ya ini penjelasan terbaik untuk saya. Satu-satunya yang jelas menjelaskan mengapa antarmuka bisa lebih berguna daripada kelas abstrak. (yaitu: pizza adalah MenuItem DAN juga Makanan sementara dengan kelas abstrak hanya bisa satu atau lain tetapi tidak keduanya)
Maxter

25

Penjelasan sederhana dengan analogi

Masalah untuk Dipecahkan: Apa tujuan dari polimorfisme?

Analogi: Jadi saya orang asing di lokasi konstruksi.

Pedagang berjalan di lokasi konstruksi sepanjang waktu. Saya tidak tahu siapa yang akan berjalan melewati pintu-pintu itu. Tetapi pada dasarnya saya memberi tahu mereka apa yang harus dilakukan.

  1. Kalau tukang kayu saya katakan: bangun perancah kayu.
  2. Jika itu tukang ledeng, saya katakan: "Pasang pipa"
  3. Jika itu adalah tukang listrik, saya katakan, "Cabut kabelnya, dan ganti dengan yang serat optik".

Masalah dengan pendekatan di atas adalah bahwa saya harus: (i) tahu siapa yang berjalan di pintu itu, dan tergantung pada siapa itu, saya harus memberi tahu mereka apa yang harus dilakukan. Itu berarti saya harus tahu segalanya tentang perdagangan tertentu. Ada biaya / manfaat yang terkait dengan pendekatan ini:

Implikasi mengetahui apa yang harus dilakukan:

  • Ini berarti jika kode tukang kayu berubah dari: BuildScaffolding()ke BuildScaffold()(yaitu sedikit perubahan nama) maka saya juga harus mengubah kelas panggilan (yaitu Forepersonkelas) juga - Anda harus membuat dua perubahan pada kode alih-alih (pada dasarnya ) hanya satu. Dengan polimorfisme Anda (pada dasarnya) hanya perlu melakukan satu perubahan untuk mencapai hasil yang sama.

  • Kedua, Anda tidak perlu selalu bertanya: siapa Anda? ok lakukan ini ... siapa kamu? ok lakukan itu ..... polimorfisme - itu KERING kode itu, dan sangat efektif dalam situasi tertentu:

  • dengan polimorfisme, Anda dapat dengan mudah menambahkan kelas pedagang tambahan tanpa mengubah kode apa pun yang ada. (Yaitu prinsip desain SOLID kedua: Prinsip buka-tutup).

Solusinya

Bayangkan sebuah skenario di mana, tidak peduli siapa yang berjalan di pintu, saya dapat mengatakan: "Bekerja ()" dan mereka melakukan pekerjaan hormat yang menjadi spesialisasi mereka: tukang ledeng akan berurusan dengan pipa, dan tukang listrik akan berurusan dengan kabel.

Manfaat dari pendekatan ini adalah: (i) Saya tidak perlu tahu persis siapa yang berjalan melalui pintu itu - yang perlu saya ketahui adalah bahwa mereka akan menjadi semacam tradie dan mereka dapat melakukan pekerjaan, dan kedua , (ii) saya tidak perlu tahu apa-apa tentang perdagangan tertentu itu. Tradie akan membereskannya.

Jadi alih-alih ini:

If(electrician) then  electrician.FixCablesAndElectricity() 

if(plumber) then plumber.IncreaseWaterPressureAndFixLeaks() 

Saya dapat melakukan sesuatu seperti ini:

ITradesman tradie = Tradesman.Factory(); // in reality i know it's a plumber, but in the real world you won't know who's on the other side of the tradie assignment.

tradie.Work(); // and then tradie will do the work of a plumber, or electrician etc. depending on what type of tradesman he is. The foreman doesn't need to know anything, apart from telling the anonymous tradie to get to Work()!!

Apa untungnya?

Keuntungannya adalah jika persyaratan pekerjaan spesifik tukang kayu berubah dll, maka juru kunci tidak perlu mengubah kode-nya - dia tidak perlu tahu atau peduli. Yang penting adalah bahwa tukang kayu tahu apa yang dimaksud dengan Work (). Kedua, jika pekerja konstruksi jenis baru datang ke lokasi kerja, maka mandor tidak perlu tahu apa-apa tentang perdagangan - semua mandor peduli jika pekerja konstruksi (tukang las, tukang las, tukang kaca, dll.) Dapat menyelesaikan beberapa Pekerjaan ().


Ilustrasi Masalah dan Solusi (Dengan dan Tanpa Antarmuka):

Tanpa antarmuka (Contoh 1):

Contoh 1: tanpa antarmuka

Tanpa antarmuka (Contoh 2):

Contoh 2: tanpa antarmuka

Dengan antarmuka:

Contoh 3: Manfaat Menggunakan Antarmuka

Ringkasan

Antarmuka memungkinkan Anda membuat orang melakukan pekerjaan yang ditugaskan kepadanya, tanpa Anda memiliki pengetahuan tentang siapa mereka sebenarnya atau apa yang bisa mereka lakukan secara spesifik. Hal ini memungkinkan Anda untuk dengan mudah menambahkan tipe baru (perdagangan) tanpa mengubah kode yang ada (baik secara teknis Anda memang mengubahnya sedikit), dan itulah manfaat nyata dari pendekatan OOP vs metodologi pemrograman yang lebih fungsional.

Jika Anda tidak memahami salah satu di atas atau jika tidak jelas tanyakan dalam komentar dan saya akan mencoba membuat jawabannya lebih baik.


4
Ditambah satu untuk pria yang mulai bekerja di hoodie.
Yusha

11

Dengan tidak adanya bebek mengetik seperti yang Anda bisa gunakan dalam Python, C # bergantung pada antarmuka untuk memberikan abstraksi. Jika dependensi suatu kelas adalah tipe konkret, Anda tidak bisa meneruskan tipe apa pun - menggunakan antarmuka yang bisa Anda lewati dengan tipe apa pun yang mengimplementasikan antarmuka.


3
+1 Benar, jika Anda mengetik bebek, Anda tidak perlu antarmuka. Tapi mereka memberlakukan keamanan tipe yang lebih kuat.
Groo

11

Contoh Pizza buruk karena Anda harus menggunakan kelas abstrak yang menangani pemesanan, dan pizza hanya mengganti jenis pizza, misalnya.

Anda menggunakan antarmuka ketika Anda memiliki properti bersama, tetapi kelas Anda mewarisi dari tempat yang berbeda, atau ketika Anda tidak memiliki kode umum yang bisa Anda gunakan. Sebagai contoh, ini adalah hal-hal yang dapat dibuang IDisposable, Anda tahu itu akan dibuang, Anda hanya tidak tahu apa yang akan terjadi ketika itu dibuang.

Antarmuka hanyalah kontrak yang memberi tahu Anda beberapa hal yang dapat dilakukan objek, parameter apa dan jenis pengembalian apa yang diharapkan.


10

Pertimbangkan kasus di mana Anda tidak mengontrol atau memiliki kelas dasar.

Ambil kontrol visual misalnya, dalam .NET untuk Winforms mereka semua mewarisi dari Kontrol kelas dasar, yang sepenuhnya ditentukan dalam kerangka .NET.

Mari kita asumsikan Anda dalam bisnis membuat kontrol khusus. Anda ingin membuat tombol baru, kotak teks, tampilan daftar, kisi, yang lainnya dan Anda ingin semuanya memiliki fitur tertentu yang unik untuk rangkaian kontrol Anda.

Misalnya Anda mungkin ingin cara umum untuk menangani tema, atau cara umum untuk menangani pelokalan.

Dalam hal ini Anda tidak dapat "hanya membuat kelas dasar" karena jika Anda melakukannya, Anda harus menerapkan kembali semua yang berhubungan dengan kontrol.

Sebaliknya Anda akan turun dari Button, TextBox, ListView, GridView, dll. Dan menambahkan kode Anda.

Tapi ini menimbulkan masalah, bagaimana Anda sekarang dapat mengidentifikasi kontrol mana yang "milikmu", bagaimana Anda bisa membangun beberapa kode yang mengatakan "untuk semua kontrol pada formulir yang menjadi milikku, atur tema menjadi X".

Masukkan antarmuka.

Antarmuka adalah cara untuk melihat objek, untuk menentukan bahwa objek mematuhi kontrak tertentu.

Anda akan membuat "YourButton", turun dari Button, dan menambahkan dukungan untuk semua antarmuka yang Anda butuhkan.

Ini memungkinkan Anda untuk menulis kode seperti berikut:

foreach (Control ctrl in Controls)
{
    if (ctrl is IMyThemableControl)
        ((IMyThemableControl)ctrl).SetTheme(newTheme);
}

Ini tidak akan mungkin tanpa antarmuka, alih-alih Anda harus menulis kode seperti ini:

foreach (Control ctrl in Controls)
{
    if (ctrl is MyThemableButton)
        ((MyThemableButton)ctrl).SetTheme(newTheme);
    else if (ctrl is MyThemableTextBox)
        ((MyThemableTextBox)ctrl).SetTheme(newTheme);
    else if (ctrl is MyThemableGridView)
        ((MyThemableGridView)ctrl).SetTheme(newTheme);
    else ....
}

Ya, saya tahu, Anda tidak harus menggunakan "is" dan kemudian membuang, kesampingkan itu.
Lasse V. Karlsen

4
mendesah aku tahu, tapi yang ini insidental di sini.
Lasse V. Karlsen

7

Dalam hal ini, Anda bisa (dan mungkin akan) mendefinisikan kelas dasar Pizza dan mewarisinya. Namun, ada dua alasan di mana Antarmuka memungkinkan Anda melakukan hal-hal yang tidak dapat dicapai dengan cara lain:

  1. Sebuah kelas dapat mengimplementasikan banyak antarmuka. Itu hanya mendefinisikan fitur yang harus dimiliki kelas. Menerapkan berbagai antarmuka berarti bahwa suatu kelas dapat memenuhi beberapa fungsi di tempat yang berbeda.

  2. Antarmuka dapat didefinisikan dalam ruang lingkup yang lebih tinggi dari kelas atau pemanggil. Ini berarti Anda dapat memisahkan fungsionalitas, memisahkan ketergantungan proyek, dan menjaga fungsionalitas dalam satu proyek atau kelas, dan implementasi ini di tempat lain.

Salah satu implikasi dari 2 adalah bahwa Anda dapat mengubah kelas yang sedang digunakan, hanya membutuhkannya mengimplementasikan antarmuka yang sesuai.


6

Pertimbangkan Anda tidak dapat menggunakan banyak pewarisan dalam C #, dan kemudian lihat kembali pertanyaan Anda.



4

Jika saya bekerja pada API untuk menggambar bentuk, saya mungkin ingin menggunakan DirectX atau panggilan grafik, atau OpenGL. Jadi, saya akan membuat antarmuka, yang akan abstrak implementasi saya dari apa yang Anda panggil.

Jadi Anda memanggil metode pabrik: MyInterface i = MyGraphics.getInstance(). Kemudian, Anda memiliki kontrak, sehingga Anda tahu fungsi apa yang dapat Anda harapkan MyInterface. Jadi, Anda dapat menelepon i.drawRectangleatau i.drawCubemengetahui bahwa jika Anda menukar satu pustaka dengan pustaka lain, bahwa fungsinya didukung.

Ini menjadi lebih penting jika Anda menggunakan Dependency Injection, seperti yang Anda bisa, dalam file XML, menukar implementasi.

Jadi, Anda mungkin memiliki satu perpustakaan crypto yang dapat diekspor yang untuk penggunaan umum, dan yang lain hanya untuk dijual ke perusahaan-perusahaan Amerika, dan perbedaannya adalah Anda mengubah file konfigurasi, dan sisanya dari program ini tidak berubah.

Ini digunakan banyak dengan koleksi di. NET, karena Anda hanya harus menggunakan, misalnya, Listvariabel, dan jangan khawatir apakah itu ArrayList atau LinkedList.

Selama Anda membuat kode untuk antarmuka maka pengembang dapat mengubah implementasi aktual dan sisa program tidak berubah.

Ini juga berguna ketika pengujian unit, karena Anda dapat mencemooh seluruh antarmuka, jadi, saya tidak harus pergi ke database, tetapi untuk implementasi mencemooh yang hanya mengembalikan data statis, jadi saya dapat menguji metode saya tanpa khawatir jika database sedang dalam pemeliharaan atau tidak.


4

Antarmuka benar-benar sebuah kontrak yang harus diikuti oleh kelas-kelas pelaksana, ini sebenarnya merupakan dasar untuk hampir semua pola desain yang saya tahu.

Dalam contoh Anda, antarmuka dibuat karena dengan demikian segala sesuatu yang IS A Pizza, yang berarti mengimplementasikan antarmuka Pizza, dijamin telah diterapkan

public void Order();

Setelah kode yang disebutkan, Anda dapat memiliki sesuatu seperti ini:

public void orderMyPizza(IPizza myPizza) {
//This will always work, because everyone MUST implement order
      myPizza.order();
}

Dengan cara ini Anda menggunakan polimorfisme dan yang Anda pedulikan adalah objek Anda merespons pesanan ().


4

Saya melakukan pencarian untuk kata "komposisi" di halaman ini dan tidak melihatnya sekali. Jawaban ini sangat banyak selain jawaban yang disebutkan di atas.

Salah satu alasan yang sangat penting untuk menggunakan antarmuka dalam Proyek Berorientasi Objek adalah bahwa mereka memungkinkan Anda untuk memilih komposisi daripada warisan. Dengan mengimplementasikan antarmuka, Anda dapat memisahkan implementasi dari berbagai algoritma yang Anda terapkan padanya.

Tutorial "Pola Penghias" yang luar biasa ini oleh Derek Banas (yang - cukup lucu - juga menggunakan pizza sebagai contoh) adalah ilustrasi yang bermanfaat:

https://www.youtube.com/watch?v=j40kRwSm4VE


2
Saya sangat terkejut bahwa ini bukan jawaban terbaik. Antarmuka dalam semua kegunaannya, adalah penggunaan terbesar dalam komposisi.
Maciej Sitko

3

Antarmuka adalah untuk menerapkan koneksi antara kelas yang berbeda. misalnya, Anda memiliki kelas untuk mobil dan pohon;

public class Car { ... }

public class Tree { ... }

Anda ingin menambahkan fungsionalitas yang dapat dibakar untuk kedua kelas. Tetapi setiap kelas memiliki cara mereka sendiri untuk membakar. jadi Anda hanya membuat;

public class Car : IBurnable
{
public void Burn() { ... }
}

public class Tree : IBurnable
{
public void Burn() { ... }
}

2
Pertanyaan yang menghantui saya dalam contoh-contoh seperti ini adalah: Mengapa ini membantu? Bisakah saya meneruskan argumen tipe IBurnable ke metode dan semua kelas dengan antarmuka IBurnable dapat ditangani? Sama seperti contoh Pizza yang saya temukan. Sangat menyenangkan Anda bisa melakukan ini, tetapi saya tidak melihat manfaatnya. Bisakah Anda memberikan contoh (karena saya benar-benar tebal saat ini) atau memberikan contoh yang tidak akan berhasil dengan itu (sekali lagi, karena saya merasa sangat tebal saat ini). Terima kasih banyak.
Nebelhom

1
Setuju. Antarmuka = ​​"Dapat melakukan". Kelas / Abstrak Calss = "Is A"
Peter.Wang

3

Anda akan mendapatkan antarmuka, saat Anda membutuhkannya :) Anda dapat mempelajari contoh, tetapi Anda membutuhkan Aha! efek untuk benar-benar mendapatkannya.

Sekarang Anda tahu antarmuka apa, hanya kode tanpa mereka. Cepat atau lambat Anda akan mengalami masalah, di mana penggunaan antarmuka akan menjadi hal yang paling wajar untuk dilakukan.


3

Saya terkejut bahwa tidak banyak posting berisi satu alasan paling penting untuk sebuah antarmuka: Pola Desain . Ini adalah gambaran yang lebih besar dalam menggunakan kontrak, dan meskipun itu adalah sintaksis dekorasi untuk kode mesin (jujur, kompiler mungkin hanya mengabaikannya), abstraksi dan antarmuka sangat penting untuk OOP, pemahaman manusia, dan arsitektur sistem yang kompleks.

Mari kita memperluas analogi pizza untuk mengatakan hidangan lengkap 3 fledge. Kami masih akan memiliki Prepare()antarmuka inti untuk semua kategori makanan kami, tetapi kami juga akan memiliki deklarasi abstrak untuk pilihan hidangan (starter, main, dessert), dan properti yang berbeda untuk jenis makanan (gurih / manis, vegetarian / non-vegetarian, bebas gluten dll).

Berdasarkan spesifikasi ini, kita dapat menerapkan pola Pabrik Abstrak untuk membuat konsep seluruh proses, tetapi menggunakan antarmuka untuk memastikan bahwa hanya fondasi yang konkret. Segala sesuatu yang lain bisa menjadi fleksibel atau mendorong polimorfisme, namun mempertahankan enkapsulasi antara berbagai kelas Courseyang mengimplementasikan ICourseantarmuka.

Jika saya punya lebih banyak waktu, saya ingin membuat contoh lengkap dari ini, atau seseorang dapat memperluas ini untuk saya, tetapi secara ringkas, antarmuka C # akan menjadi alat terbaik dalam merancang sistem jenis ini.


1
Jawaban ini layak mendapat lebih banyak poin! Antarmuka bersinar ketika digunakan dalam pola yang dirancang misalnya Pola Negara. Lihat plus.google.com/+ZoranHorvat-Pemrograman untuk informasi tambahan
Alex Nolasco

3

Berikut ini antarmuka untuk objek yang memiliki bentuk persegi panjang:

interface IRectangular
{
    Int32 Width();
    Int32 Height();
}

Yang dibutuhkan adalah Anda menerapkan cara untuk mengakses lebar dan tinggi objek.

Sekarang mari kita mendefinisikan metode yang akan bekerja pada objek apa pun yang IRectangular:

static class Utils
{
    public static Int32 Area(IRectangular rect)
    {
        return rect.Width() * rect.Height();
    }
}

Itu akan mengembalikan area objek persegi panjang.

Mari kita implementasikan kelas SwimmingPoolyang persegi panjang:

class SwimmingPool : IRectangular
{
    int width;
    int height;

    public SwimmingPool(int w, int h)
    { width = w; height = h; }

    public int Width() { return width; }
    public int Height() { return height; }
}

Dan kelas lain Houseyang juga persegi panjang:

class House : IRectangular
{
    int width;
    int height;

    public House(int w, int h)
    { width = w; height = h; }

    public int Width() { return width; }
    public int Height() { return height; }
}

Karena itu, Anda dapat memanggil Areametode ini di rumah atau kolam renang:

var house = new House(2, 3);

var pool = new SwimmingPool(3, 4);

Console.WriteLine(Utils.Area(house));
Console.WriteLine(Utils.Area(pool));

Dengan cara ini, kelas Anda dapat "mewarisi" perilaku (metode statis) dari sejumlah antarmuka.


3

Antarmuka mendefinisikan kontrak antara penyedia fungsionalitas tertentu dan konsumen yang terkait. Ini memisahkan implementasi dari kontrak (antarmuka). Anda harus melihat arsitektur dan desain berorientasi objek. Anda mungkin ingin memulai dengan wikipedia: http://en.wikipedia.org/wiki/Interface_(computing)


3

Apa ?

Antarmuka pada dasarnya adalah kontrak yang harus diikuti oleh semua kelas yang mengimplementasikan Antarmuka. Mereka terlihat seperti kelas tetapi tidak memiliki implementasi.

Dalam C#Nama antarmuka menurut konvensi ditentukan oleh Awalan 'I' jadi jika Anda ingin memiliki antarmuka yang disebut bentuk, Anda akan mendeklarasikannya sebagaiIShapes

Sekarang kenapa ?

Improves code re-usability

Katakanlah Anda ingin menggambar Circle, Triangle. Anda dapat mengelompokkannya bersama dan memanggil mereka Shapesdan memiliki metode untuk menggambar Circledan Triangle Tetapi memiliki implementasi konkret akan menjadi ide yang buruk karena besok Anda mungkin memutuskan untuk memiliki 2 lagi Shapes Rectangle& Square. Sekarang ketika Anda menambahkannya, ada kemungkinan besar Anda dapat merusak bagian lain dari kode Anda.

Dengan Antarmuka Anda mengisolasi implementasi yang berbeda dari Kontrak


Skenario Langsung Hari 1

Anda diminta membuat Aplikasi untuk Menggambar Lingkaran dan Segitiga

interface IShapes
{
   void DrawShape();
   
 }

class Circle : IShapes
{
    
    public void DrawShape()
    {
        Console.WriteLine("Implementation to Draw a Circle");
    }
}

Class Triangle: IShapes
{
     public void DrawShape()
    {
        Console.WriteLine("Implementation to draw a Triangle");
    }
}
static void Main()
{
     List <IShapes> shapes = new List<IShapes>();
        shapes.Add(new Circle());
        shapes.Add(new Triangle());

        foreach(var shape in shapes)
        {
            shape.DrawShape();
        }
}

Skenario Langsung Hari 2

Jika Anda diminta menambahkan Squaredan Rectanglememasukkannya, yang harus Anda lakukan adalah membuat implentasinya class Square: IShapesdan Mainmenambahkan ke daftarshapes.Add(new Square());


Mengapa Anda menambahkan jawaban ke pertanyaan berusia 6 tahun dengan lusinan jawaban lainnya yang dibatalkan ratusan kali? Tidak ada apa pun di sini yang belum dikatakan.
Jonathon Reinhart

@ Jonathon Reinhart, ya, saya pikir begitu, tapi kemudian saya memikirkan contoh ini dan cara menjelaskannya akan membantu terkait dengan beberapa tubuh lebih baik daripada yang lain.
Clint

2

Ada banyak jawaban bagus di sini tetapi saya ingin mencoba dari sudut pandang yang sedikit berbeda.

Anda mungkin akrab dengan prinsip-prinsip SOLID desain berorientasi objek. Singkatnya:

S - Prinsip Tanggung Jawab Tunggal O - Prinsip Terbuka / Tertutup L - Prinsip Pengganti Liskov I - Prinsip Segregasi Antarmuka D - Prinsip Pembalikan Ketergantungan

Mengikuti prinsip-prinsip SOLID membantu menghasilkan kode yang bersih, berfaktor dengan baik, kohesif, dan secara longgar digabungkan. Mengingat bahwa:

"Manajemen ketergantungan adalah tantangan utama dalam perangkat lunak di setiap skala" (Donald Knuth)

maka apa pun yang membantu pengelolaan ketergantungan adalah kemenangan besar. Antarmuka dan Prinsip Ketergantungan Inversi benar-benar membantu memisahkan kode dari ketergantungan pada kelas-kelas yang konkret, sehingga kode dapat ditulis dan dipertimbangkan dalam hal perilaku daripada implementasi. Ini membantu memecah kode menjadi komponen-komponen yang dapat dikomposisikan pada saat runtime daripada waktu kompilasi dan juga berarti komponen-komponen itu dapat dengan mudah dicolokkan masuk dan keluar tanpa harus mengubah sisa kode.

Antarmuka membantu khususnya dengan Prinsip Ketergantungan Inversi, di mana kode dapat dipecah menjadi kumpulan layanan, dengan masing-masing layanan dijelaskan oleh antarmuka. Layanan kemudian dapat "disuntikkan" ke dalam kelas saat runtime dengan mengirimkannya sebagai parameter konstruktor. Teknik ini benar-benar menjadi penting jika Anda mulai menulis unit test dan menggunakan pengembangan yang digerakkan oleh tes. Cobalah! Anda akan dengan cepat memahami bagaimana antarmuka membantu memecah kode menjadi potongan-potongan yang dapat dikelola yang dapat diuji secara terpisah.


1

Tujuan utama antarmuka adalah membuat kontrak antara Anda dan kelas lain yang mengimplementasikan antarmuka yang membuat kode Anda dipisahkan dan memungkinkan pengembangan.


1

Ini adalah contoh yang sangat bagus.

Lain, dalam hal pernyataan beralih, Anda tidak lagi perlu mempertahankan dan beralih setiap kali Anda ingin melakukan tugas dengan cara tertentu.

Dalam contoh pizza Anda, jika ingin membuat pizza, antarmuka adalah semua yang Anda butuhkan, dari sana setiap pizza menangani sendiri logikanya.

Ini membantu mengurangi kopling dan kompleksitas siklomatik. Anda harus tetap menerapkan logika tetapi akan ada lebih sedikit Anda harus melacak dalam gambar yang lebih luas.

Untuk setiap pizza, Anda kemudian dapat melacak informasi khusus untuk pizza itu. Apa yang dimiliki pizza lain tidak masalah karena hanya pizza lain yang perlu diketahui.


1

Cara paling sederhana untuk berpikir tentang antarmuka adalah mengenali apa arti warisan. Jika kelas CC mewarisi kelas C, itu berarti keduanya:

  1. Kelas CC dapat menggunakan anggota C kelas publik atau yang dilindungi seolah-olah mereka sendiri, dan dengan demikian hanya perlu mengimplementasikan hal-hal yang tidak ada di kelas induk.
  2. Referensi ke CC dapat diteruskan atau ditugaskan ke rutin atau variabel yang mengharapkan referensi ke C.

Kedua fungsi warisan itu dalam beberapa hal bersifat independen; meskipun warisan berlaku keduanya secara bersamaan, juga dimungkinkan untuk menerapkan yang kedua tanpa yang pertama. Ini berguna karena mengizinkan objek untuk mewarisi anggota dari dua atau lebih kelas yang tidak terkait jauh lebih rumit daripada membiarkan satu jenis hal dapat disubstitusikan untuk beberapa jenis.

Antarmuka agak seperti kelas dasar abstrak, tetapi dengan perbedaan utama: objek yang mewarisi kelas dasar tidak dapat mewarisi kelas lain. Sebaliknya, suatu objek dapat mengimplementasikan antarmuka tanpa mempengaruhi kemampuannya untuk mewarisi kelas yang diinginkan atau mengimplementasikan antarmuka lain.

Salah satu fitur bagus dari ini (kurang dimanfaatkan dalam .net framework, IMHO) adalah bahwa mereka memungkinkan untuk menunjukkan secara deklaratif hal-hal yang dapat dilakukan objek. Beberapa objek, misalnya, akan menginginkan objek sumber data dari mana mereka dapat mengambil sesuatu dengan indeks (seperti yang mungkin dengan Daftar), tetapi mereka tidak perlu menyimpan apa pun di sana. Rutinitas lain akan membutuhkan objek penyimpanan data di mana mereka dapat menyimpan hal-hal tidak dengan indeks (seperti dengan Collection.Add), tetapi mereka tidak perlu membaca apa pun kembali. Beberapa tipe data akan memungkinkan akses menurut indeks, tetapi tidak akan mengizinkan penulisan; yang lain akan mengizinkan penulisan, tetapi tidak akan mengizinkan akses berdasarkan indeks. Beberapa, tentu saja, akan memungkinkan keduanya.

Jika ReadableByIndex dan Appendable adalah kelas dasar yang tidak terkait, tidak mungkin untuk menentukan tipe yang dapat diteruskan ke hal-hal yang mengharapkan ReadableByIndex dan hal-hal yang mengharapkan suatu Appendable. Seseorang dapat mencoba mengurangi ini dengan memiliki ReadableByIndex atau Appendable yang berasal dari yang lain; kelas turunan harus menyediakan anggota publik untuk kedua tujuan, tetapi memperingatkan bahwa beberapa anggota publik mungkin tidak benar-benar berfungsi. Beberapa kelas dan antarmuka Microsoft melakukan itu, tetapi itu agak menjijikkan. Pendekatan yang lebih bersih adalah memiliki antarmuka untuk tujuan yang berbeda, dan kemudian memiliki objek yang mengimplementasikan antarmuka untuk hal-hal yang sebenarnya dapat mereka lakukan. Jika seseorang memiliki antarmuka IReadableByIndex dan antarmuka lainnya IAppendable,


1

Antarmuka juga dapat dirantai daisy untuk membuat antarmuka lain. Kemampuan ini untuk mengimplementasikan beberapa Antarmuka memberi pengembang keuntungan dari menambahkan fungsionalitas ke kelas mereka tanpa harus mengubah fungsionalitas kelas saat ini (Prinsip SOLID)

O = "Kelas harus terbuka untuk ekstensi tetapi ditutup untuk modifikasi"


1

Bagi saya kelebihan / manfaat antarmuka adalah lebih fleksibel daripada kelas abstrak. Karena Anda hanya dapat mewarisi 1 kelas abstrak tetapi Anda dapat mengimplementasikan banyak antarmuka, perubahan pada sistem yang mewarisi kelas abstrak di banyak tempat menjadi bermasalah. Jika diwarisi di 100 tempat, perubahan memerlukan perubahan ke semua 100. Tapi, dengan antarmuka, Anda dapat menempatkan perubahan baru di antarmuka baru dan cukup gunakan antarmuka itu di tempat yang diperlukan (Antarmuka Seq. Dari SOLID). Selain itu, penggunaan memori tampaknya akan kurang dengan antarmuka sebagai objek dalam contoh antarmuka digunakan hanya sekali dalam memori meskipun berapa banyak tempat mengimplementasikan antarmuka.


1

Antarmuka digunakan untuk mendorong konsistensi, dengan cara yang digabungkan secara longgar yang membuatnya berbeda dengan kelas abstrak yang digabungkan dengan erat. Itulah sebabnya juga biasanya didefinisikan sebagai kontrak. Kelas mana pun yang mengimplementasikan antarmuka harus mematuhi "aturan / sintaksis" didefinisikan oleh antarmuka dan tidak ada elemen konkret di dalamnya.

Saya hanya akan memberikan contoh yang didukung oleh grafik di bawah ini.

Bayangkan di sebuah pabrik ada 3 jenis mesin. Mesin persegi panjang, mesin segitiga dan mesin poligon. Waktu kompetitif dan Anda ingin merampingkan pelatihan operator. Anda hanya ingin melatih mereka dalam satu metodologi memulai dan menghentikan mesin sehingga Anda memiliki tombol mulai hijau dan tombol berhenti merah. Jadi sekarang di 3 mesin yang berbeda Anda memiliki cara yang konsisten untuk memulai dan menghentikan 3 jenis mesin. Sekarang bayangkan mesin ini adalah kelas dan kelas perlu memiliki metode mulai dan berhenti, bagaimana Anda akan mendorong konsistensi di kelas-kelas ini yang bisa sangat berbeda? Antarmuka adalah jawabannya.

masukkan deskripsi gambar di sini

Contoh sederhana untuk membantu Anda memvisualisasikan, orang mungkin bertanya mengapa tidak menggunakan kelas abstrak? Dengan antarmuka, objek tidak harus terkait atau diwariskan secara langsung dan Anda masih dapat mendorong konsistensi di berbagai kelas.

public interface IMachine
{
    bool Start();
    bool Stop();
}

public class Car : IMachine
{
    public bool Start()
    {
        Console.WriteLine("Car started");
        return true;
    }

    public bool Stop()
    {
        Console.WriteLine("Car stopped");
        return false;
    }
}

public class Tank : IMachine
{
    public bool Start()
    {
        Console.WriteLine("Tank started");
        return true;
    }

    public bool Stop()
    {
        Console.WriteLine("Tank stopped");
        return false;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var car = new Car();
        car.Start();
        car.Stop();

        var tank = new Tank();
        tank.Start();
        tank.Stop();

    }
}

1
class Program {
    static void Main(string[] args) {
        IMachine machine = new Machine();
        machine.Run();
        Console.ReadKey();
    }

}

class Machine : IMachine {
    private void Run() {
        Console.WriteLine("Running...");
    }
    void IMachine.Run() => Run();
}

interface IMachine
{
    void Run();
}

Izinkan saya menggambarkan ini dengan perspektif yang berbeda. Mari kita buat cerita sesuai dengan contoh yang saya tunjukkan di atas;

Program, Mesin, dan Mesin adalah aktor dari kisah kami. Program ingin dijalankan tetapi tidak memiliki kemampuan dan Machine tahu cara menjalankannya. Machine dan IMachine adalah teman baik tetapi Program tidak berbicara dengan Machine. Jadi Program dan IMachine membuat kesepakatan dan memutuskan bahwa IMachine akan memberi tahu Program bagaimana menjalankan dengan melihat Mesin (seperti reflektor).

Dan Program mempelajari cara menjalankannya dengan bantuan IMachine.

Antarmuka menyediakan komunikasi dan mengembangkan proyek yang digabungkan secara longgar.

PS: Saya sudah menggunakan metode kelas beton sebagai privat. Tujuan saya di sini adalah untuk mencapai secara longgar digabungkan dengan mencegah mengakses properti dan metode kelas beton, dan hanya menyisakan cara untuk menjangkau mereka melalui antarmuka. (Jadi saya mendefinisikan metode antarmuka secara eksplisit).


1

Saya tahu saya sangat terlambat .. (hampir sembilan tahun), tetapi jika seseorang ingin penjelasan kecil maka Anda dapat melakukannya untuk ini:

Dengan kata-kata sederhana, Anda menggunakan Antarmuka ketika Anda tahu apa yang bisa dilakukan Obyek atau fungsi apa yang akan kami terapkan pada objek .. Contoh Masukkan, Perbarui, dan Hapus.

interface ICRUD{
      void InsertData(); // will insert data
      void UpdateData(); // will update data
      void DeleteData(); // will delete data
}

Catatan Penting: Antarmuka SELALU bersifat publik.

Semoga ini membantu.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.