Haruskah antarmuka memperluas (dan dengan demikian mewarisi metode) antarmuka lainnya

13

Meskipun ini adalah pertanyaan umum, ini juga khusus untuk masalah yang saya alami saat ini. Saat ini saya memiliki antarmuka yang ditentukan dalam solusi saya yang disebut

public interface IContextProvider
{
   IDataContext { get; set; }
   IAreaContext { get; set; }
}

Antarmuka ini sering digunakan di seluruh program dan karenanya saya memiliki akses mudah ke objek yang saya butuhkan. Namun pada tingkat yang cukup rendah dari bagian dari program saya, saya perlu akses ke kelas lain yang akan menggunakan IAreaContext dan melakukan beberapa operasi dari itu. Jadi saya telah membuat antarmuka pabrik lain untuk melakukan kreasi yang disebut:

public interface IEventContextFactory 
{
   IEventContext CreateEventContext(int eventId);
}

Saya memiliki kelas yang mengimplementasikan IContextProvider dan disuntikkan menggunakan NinJect. Masalah yang saya miliki adalah bahwa area di mana saya perlu menggunakan IEventContextFactory ini hanya memiliki akses ke IContextProvider dan menggunakan kelas lain yang membutuhkan antarmuka baru ini. Saya tidak ingin harus instantiate implementasi IEventContextFactory ini pada tingkat rendah dan lebih suka bekerja dengan antarmuka IEventContextFactory di seluruh. Namun saya juga tidak mau harus menyuntikkan parameter lain melalui konstruktor hanya untuk diteruskan ke kelas yang membutuhkannya yaitu

// example of problem
public class MyClass
{
    public MyClass(IContextProvider context, IEventContextFactory event)
    {
       _context = context;
       _event = event;
    }

    public void DoSomething() 
    {
       // the only place _event is used in the class is to pass it through
       var myClass = new MyChildClass(_event);
       myClass.PerformCalculation();      
    }
}

Jadi pertanyaan utama saya adalah, apakah ini dapat diterima atau bahkan praktik umum atau baik untuk melakukan sesuatu seperti ini (antarmuka memperluas antarmuka lainnya):

public interface IContextProvider : IEventContextFactory

atau haruskah saya mempertimbangkan alternatif yang lebih baik untuk mencapai apa yang saya butuhkan. Jika saya belum memberikan informasi yang cukup untuk memberi saran, beri tahu saya dan saya dapat memberikan lebih banyak.

dreza
sumber
2
Saya akan menulis ulang judul pertanyaan Anda sebagai "Haruskah antarmuka mewarisi antarmuka" karena tidak ada antarmuka yang benar-benar mengimplementasikan apa pun.
Jesse C. Slicer
2
Bahasa yang biasa adalah bahwa antarmuka "memperluas" induknya, dan (dengan melakukan itu) "mewarisi" metode antarmuka induk, jadi saya akan merekomendasikan menggunakan "memperluas" di sini.
Theodore Murdock
Antarmuka dapat memperpanjang orang tua, jadi mengapa itu menjadi praktik yang buruk? Jika satu antarmuka secara sah merupakan super-set antarmuka lain daripada tentu saja harus diperluas.
Robin Winslow

Jawaban:

10

Sementara antarmuka hanya menggambarkan perilaku tanpa implementasi apa pun, mereka masih dapat berpartisipasi dalam hierarki warisan. Sebagai contoh, bayangkan Anda memiliki, katakanlah, gagasan umum tentang a Collection. Antarmuka ini akan memberikan beberapa hal yang umum untuk semua koleksi, seperti metode untuk beralih ke anggota. Kemudian Anda dapat berpikir tentang beberapa koleksi objek yang lebih spesifik seperti a Listatau a Set. Masing-masing mewarisi semua persyaratan perilaku Collection, tetapi menambahkan persyaratan tambahan khusus untuk jenis koleksi tertentu.

Setiap kali masuk akal untuk membuat hierarki warisan untuk antarmuka, maka Anda harus melakukannya. Jika dua antarmuka akan selalu ditemukan bersama, maka mereka mungkin harus digabung.

Zoe
sumber
6

Ya, tetapi hanya jika itu masuk akal. Tanyakan kepada diri Anda pertanyaan-pertanyaan berikut dan jika satu atau lebih berlaku maka itu dapat diterima untuk mewarisi antarmuka dari yang lain.

  1. Apakah antarmuka A memperluas antarmuka B secara logis, misalnya IList menambahkan fungsionalitas ke ICollection.
  2. Apakah antarmuka Suatu kebutuhan untuk mendefinisikan perilaku yang sudah ditentukan oleh antarmuka lain, misalnya mengimplementasikan IDisposable jika memiliki sumber daya yang perlu dirapikan atau IEnumerable jika dapat diulangi.
  3. Apakah setiap implementasi antarmuka A memerlukan perilaku yang ditentukan oleh antarmuka B?

Dalam contoh Anda, saya tidak berpikir itu menjawab ya untuk semua itu. Anda mungkin lebih baik menambahkannya sebagai properti lain:

public interface IContextProvider
{
   IDataContext DataContext { get; set; }
   IAreaContext AreaContext { get; set; }
   IEventContextFactory EventContextFactory { get; set; }
}
Trevor Pilley
sumber
Bagaimana membuatnya menjadi properti lebih baik daripada memperluas antarmuka, atau hanya cara lain untuk menguliti kucing untuk berbicara?
dreza
1
Perbedaannya adalah jika Anda melakukan IContextProviderperluasan IEventContextFactory, maka ContextProviderimplementasinya juga perlu mengimplementasikan metode itu. Jika Anda menambahkannya sebagai properti, Anda mengatakan bahwa a ContextProvidermemiliki IEventContextFactorytetapi sebenarnya tidak tahu detail implementasi.
Trevor Pilley
4

Ya, boleh saja memperpanjang satu antarmuka dari yang lain.

Pertanyaan apakah itu hal yang benar untuk dilakukan dalam kasus tertentu tergantung pada apakah ada hubungan "is-a" yang sebenarnya di antara keduanya.

Apa yang diperlukan untuk hubungan "is-a" yang valid?

Pertama, harus ada perbedaan nyata antara kedua antarmuka, yaitu, harus ada contoh yang mengimplementasikan antarmuka induk tetapi bukan antarmuka anak. Jika tidak ada dan tidak akan ada contoh yang tidak mengimplementasikan kedua antarmuka, maka kedua antarmuka tersebut seharusnya digabungkan.

Kedua, itu harus menjadi kasus bahwa setiap instance dari antarmuka anak harus selalu merupakan instance dari orang tua: tidak ada logika (masuk akal) di mana Anda akan membuat instance dari antarmuka anak yang tidak akan masuk akal sebagai contoh dari antarmuka induk.

Jika keduanya benar, maka pewarisan adalah hubungan yang tepat untuk kedua antarmuka.

Theodore Murdock
sumber
Saya pikir memiliki kelas yang hanya menggunakan antarmuka dasar dan bukan yang diturunkan tidak perlu. Misalnya, antarmuka IReadOnlyListdapat bermanfaat, bahkan jika semua kelas daftar saya menerapkan IList(yang berasal dari IReadOnlyList).
svick
1

Jika satu antarmuka selalu ingin meminta / menyediakan yang lain, maka silakan. Saya telah melihat ini dalam beberapa kasus dengan IDisposibleyang masuk akal. Namun secara umum, Anda harus memastikan bahwa setidaknya satu antarmuka berperilaku lebih seperti sifat daripada tanggung jawab.

Sebagai contoh, tidak jelas. Jika keduanya selalu bersama, cukup buat satu antarmuka. Jika yang satu selalu membutuhkan yang lain, saya akan khawatir tentang "X dan 1" jenis warisan yang sangat sering mengarah ke beberapa tanggung jawab, dan / atau masalah abstraksi bocor lainnya.

Telastyn
sumber
Terima kasih untuk itu. Apa yang Anda maksudkan dengan berperilaku lebih seperti sifat daripada tanggung jawab?
dreza
@dreza maksud saya tanggung jawab seperti dalam SRP di SOLID, dan sifat seperti sesuatu seperti sifat Scala. Sebagian besar konsep tentang bagaimana model memodelkan masalah Anda. Apakah itu mewakili bagian utama dari masalah atau pandangan ke bagian umum dari jenis yang berbeda?
Telastyn