Bagaimana Anda mengembangkan & versi antarmuka?

22

Katakanlah Anda memiliki antarmuka IFoo:

public interface IFoo {
    void Bar(string s);
    int Quux(object o);
}

Di versi 2 API Anda, Anda perlu menambahkan metode Glargke antarmuka ini. Bagaimana Anda melakukannya tanpa memutus pengguna API yang ada dan mempertahankan kompatibilitas mundur? Ini terutama ditujukan pada .NET, tetapi juga dapat diterapkan pada kerangka & bahasa lain.

thecoop
sumber
Anda dapat menambahkan tanpa masalah. Masalahnya muncul ketika Anda mengubah / menghapus sesuatu yang sudah ada di sana.
Rig
1
@Rig: Di C # setidaknya, Anda akan mendapatkan kesalahan kompilasi jika Anda menambahkan metode ke antarmuka dan tidak menambahkannya ke kelas yang mengimplementasikan antarmuka itu.
Malice
Ya benar. Saya berpikir lebih banyak dari skenario kelas pengguna yang mungkin memiliki kekacauan minimal dibandingkan dengan mengubah metode tanda tangan atau menghapus satu. Jadi saya kira itu bisa menyebabkan beberapa pekerjaan jika Anda perlu menambahkan antarmuka Anda.
Rig

Jawaban:

9

Di versi 2 API Anda, Anda perlu menambahkan metode Glargke antarmuka ini.

Mengapa?

Antarmuka yang ditentukan untuk digunakan dengan API memiliki dua peran yang sama sekali berbeda:

  1. Pembalikan ketergantungan - antarmuka seperti itu dikonsumsi oleh API Anda. Mereka mengizinkan kode klien untuk membuat plugin, dll.
  2. Abstraksi - antarmuka seperti itu dikembalikan oleh API Anda dan menyembunyikan detail implementasi objek yang dikembalikan.

Sekarang untuk versi tertentu dari API, antarmuka yang sama dapat bertindak sebagai keduanya. Namun, dalam versi yang akan datang, ini dapat dipisahkan.

  1. Anda ingin mengekstraksi lebih banyak informasi dari antarmuka yang Anda konsumsi. Untuk meningkatkan kinerja, atau menambah fleksibilitas atau apa pun. Tetapkan antarmuka baru, mungkin berasal dari yang lama, dan buat metode terpisah untuk menggunakannya. AFAIK sebagian besar .NET memungkinkan metode overloading, jadi ini bisa terjadi tanpa menambahkan banyak kekacauan.
  2. Anda ingin "mengembalikan lebih banyak", yaitu abstraksi objek "lebih kaya" dari API Anda. Di sini Anda memiliki dua pilihan:

    • Anda dapat beranggapan, bahwa kode klien tidak akan memiliki implementator antarmuka sendiri. Berdasarkan asumsi ini, aman untuk menambahkan ekstensi Anda ke antarmuka yang ada.
    • Tentukan antarmuka baru, jika mungkin berasal dari yang sebelumnya. Jika derivasi seperti itu tidak mungkin, buat metode terpisah untuk meminta contoh antarmuka baru atau gunakan komposisi:

      interface MyNewInterface extends MyOldInterface { 
           FancyNewInterface getFancyShit();
      }
      
back2dos
sumber
15

DirectX menambahkan nomor versi ke antarmuka-nya. Dalam kasus Anda, solusinya akan seperti

public interface IFoo2 : IFoo
{
    void Glarg();
}

API masih akan merujuk ke IFoo, dan ke IFoo2 hanya dalam metode dll di mana fungsi IFoo2 diperlukan.

Implementasi API harus memeriksa metode yang ada (= versi 1) apakah objek parameter IFoo benar-benar mengimplementasikan IFoo2, apakah semantik metode berbeda untuk IFoo2.

devio
sumber
3

Menambahkan metode baru (atau metode) ke API Anda harus dilakukan sedemikian rupa sehingga tidak memiliki efek samping pada API yang ada. Yang paling penting, seseorang yang terus menggunakan API lama seolah-olah API baru itu tidak ada, harus tidak terpengaruh olehnya. Menggunakan API lama seharusnya juga tidak memiliki efek samping yang tidak diharapkan pada API baru.

Jika ada metode yang ada di API digantikan oleh yang baru, jangan langsung menghapusnya. Tandai mereka sudah usang dan berikan penjelasan tentang apa yang harus digunakan. Itu memberi pengguna peringatan kode Anda bahwa versi masa depan mungkin tidak lagi mendukungnya alih-alih melanggar kode mereka tanpa peringatan.

Jika API baru dan lama tidak kompatibel dan tidak dapat hidup bersama tanpa efek samping yang tidak diinginkan, pisahkan dan dokumentasikan bahwa jika API baru akan diadopsi, API lama harus dihentikan sepenuhnya. Ini kurang diinginkan karena akan selalu ada seseorang yang mencoba menggunakan keduanya dan menjadi frustrasi ketika tidak berhasil.

Karena Anda bertanya tentang .NET secara khusus, Anda mungkin ingin membaca artikel ini tentang penghentian di .NET, yang terhubung ke ObsoleteAttribute(digunakan dalam contoh berikut):

using System;

public sealed class App {
   static void Main() {      
      // The line below causes the compiler to issue a warning:
      // 'App.SomeDeprecatedMethod()' is obsolete: 'Do not call this method.'
      SomeDeprecatedMethod();
   }

   // The method below is marked with the ObsoleteAttribute. 
   // Any code that attempts to call this method will get a warning.
   [Obsolete("Do not call this method.")]
   private static void SomeDeprecatedMethod() { }
}
Gyan alias Gary Buyn
sumber
2

Perubahan antarmuka publik melibatkan kerusakan. Strategi umum adalah melakukan ini hanya pada versi utama dan setelah periode pembekuan (sehingga tidak terjadi sesuka hati). Anda dapat melarikan diri tanpa melanggar klien Anda jika Anda menambahkan Anda tambahan ke antarmuka baru (dan implementasi Anda dapat memberikan keduanya pada kelas yang sama). Itu tidak ideal, dan jika Anda terus melakukannya Anda akan berantakan.

Dengan jenis modifikasi lain (menghapus metode, mengubah tanda tangan), Anda mandek.

Tamás Szelei
sumber
2
Anda dapat secara preemptive memesan awalan untuk nama metode yang akan datang dan memperingatkan semua pengguna bahwa mereka tidak boleh menggunakan namespace itu, tetapi bahkan itu membuat API tidak elegan. Secara umum, orangtua benar sekali: penghapusan (dan sering penambahan) metode akan memecah pengguna yang ada, dan tidak ada yang dapat Anda lakukan kecuali merencanakannya dengan bijak.
Kilian Foth
1

Antarmuka adalah kontrak, oleh karena itu seharusnya tidak memiliki versi. Apa yang terjadi jika seorang pemain sepak bola mendapat kontrak baru? Apakah yang lama masih berlaku? Tidak. Jika seseorang mengubah antarmuka, kontrak berubah dan kontrak (antarmuka) sebelumnya tidak lagi valid.

Meskipun Anda bisa menggunakan strategi IFoo2, pada akhirnya itu akan menjadi berantakan ketika Anda memiliki:

  • IFoo2
  • IFoo3
  • IFoo4
  • dll.

Yuck.

API berbeda. Saya memberi perpustakaan kode untuk digunakan. Bulan depan saya memberi Anda perpustakaan yang diperbarui. Seperti yang dikatakan poster lain, jangan melanggar apa yang sudah saya gunakan, cukup tambahkan fungsionalitas / metode baru.

Jika Anda ingin membuat versi sesuatu, gunakan kelas abtract daripada antarmuka.

Jon Raynor
sumber