Fitur baru sering menuntut member tambahan pada interface yang sudah lama dipakai. Menambahkan method biasa memaksa setiap class yang mengimplementasikan interface ikut mengisinya, termasuk class yang tidak membutuhkan fitur tersebut. Sejak C# 8, interface dapat menyediakan badan bawaan lewat default interface methods: method tetap dideklarasikan di interface, tetapi implementer bebas memakai bawaan atau menyediakan versinya sendiri.

Menambahkan Method dengan Badan Bawaan #

Kita perbarui kontrak IKirimEmail dari bab Multiple Interface Implementation dengan method konfirmasi yang memiliki badan:

csharp
public interface IKirimEmail
{
    void Kirim(string tujuan, string isi);

    void KirimKonfirmasi(string tujuan, int jumlah)
    {
        Kirim(tujuan, $"Pembayaran Rp{jumlah} berhasil diproses. Terima kasih.");
    }
}

Method Kirim tetap tanpa badan sehingga setiap implementer wajib mengisinya. Method KirimKonfirmasi memiliki badan bawaan yang memanggil Kirim, sehingga class yang sudah ada tidak perlu berubah sama sekali.

Perhatikan bahwa badan bawaan tetap boleh memanggil member abstrak milik interface yang sama, seperti Kirim pada contoh. Pola ini sering disebut menyerupai trait: interface menyediakan kerangka alur yang sudah jadi, dan implementer hanya mengisi bagian yang memang berbeda antar class.

Memakai Bawaan dan Menyediakan Versi Sendiri #

Class berikut memakai badan bawaan apa adanya, sedangkan class kedua menyediakan versi sendiri:

csharp
public class EmailService : IKirimEmail
{
    public void Kirim(string tujuan, string isi)
    {
        Console.WriteLine($"Email terkirim ke {tujuan}: {isi}");
    }
}

public class EmailMarketing : IKirimEmail
{
    public void Kirim(string tujuan, string isi)
    {
        Console.WriteLine($"Email ke {tujuan}: {isi}");
    }

    public void KirimKonfirmasi(string tujuan, int jumlah)
    {
        Kirim(tujuan, $"[Promo] Pembayaran Rp{jumlah} berhasil. Cek kupon Anda.");
    }
}

Pemanggil memakai keduanya melalui variabel bertipe interface:

csharp
IKirimEmail layanan = new EmailService();
layanan.KirimKonfirmasi("[email protected]", 150000);

IKirimEmail promo = new EmailMarketing();
promo.KirimKonfirmasi("[email protected]", 150000);

Output yang Dihasilkan:

text
Email terkirim ke [email protected]: Pembayaran Rp150000 berhasil diproses. Terima kasih.
Email ke [email protected]: [Promo] Pembayaran Rp150000 berhasil. Cek kupon Anda.

Mari kita bedah contoh di atas:

  1. EmailService — class ini hanya mengisi Kirim; panggilan KirimKonfirmasi menjalankan badan bawaan dari interface, yang kemudian memanggil Kirim milik object.
  2. EmailMarketing — class ini menyediakan KirimKonfirmasi versi sendiri, dan versi tersebut menggantikan badan bawaan.
  3. promo.KirimKonfirmasi(...) — runtime memilih versi yang dimiliki object yang sedang ditunjuk, bukan selalu versi bawaan.
  4. layanan.KirimKonfirmasi(...) — pemanggilan melalui variabel IKirimEmail adalah satu-satunya cara mengakses default method dari luar interface.
text
error CS1061: 'EmailService' does not contain a definition for 'KirimKonfirmasi' and no accessible extension method 'KirimKonfirmasi' accepting a first argument of type 'EmailService' could be found (are you missing a using directive or an assembly reference?)

Badan bawaan pada interface juga membuka pintu versioning: kemampuan baru dapat ditambahkan bertahap tanpa memaksa seluruh implementer lama ikut berubah.