Antarmuka kosong umumnya menganggap praktik buruk, sejauh yang saya tahu - terutama di mana hal-hal seperti atribut didukung oleh bahasa.
Namun, apakah sebuah antarmuka dianggap 'kosong' jika ia mewarisi dari antarmuka lain?
interface I1 { ... }
interface I2 { ... } //unrelated to I1
interface I3
: I1, I2
{
// empty body
}
Apa pun yang mengimplementasikan I3
perlu diimplementasikan I1
dan I2
, dan objek dari kelas berbeda yang mewarisi I3
kemudian dapat digunakan secara bergantian (lihat di bawah), jadi apakah itu benar untuk memanggil I3
kosong ? Jika demikian, apa yang akan menjadi cara yang lebih baik untuk arsitektur ini?
// with I3 interface
class A : I3 { ... }
class B : I3 { ... }
class Test {
void foo() {
I3 something = new A();
something = new B();
something.SomeI1Function();
something.SomeI2Function();
}
}
// without I3 interface
class A : I1, I2 { ... }
class B : I1, I2 { ... }
class Test {
void foo() {
I1 something = new A();
something = new B();
something.SomeI1Function();
something.SomeI2Function(); // we can't do this without casting first
}
}
foo
? (Jika jawabannya "ya", silakan saja!)var : I1, I2 something = new A();
untuk variabel lokal akan lebih baik (jika kita mengabaikan poin baik yang diangkat dalam jawaban Konrad)Jawaban:
"Bundling"
I1
danI2
menjadiI3
penawaran yang bagus (?) Shortcut, atau semacam gula sintaks untuk manapun Anda ingin baik untuk dilaksanakan.Masalah dengan pendekatan ini adalah bahwa Anda tidak dapat menghentikan pengembang lain dari penerapan
I1
danI2
berdampingan secara eksplisit , tetapi tidak bekerja dua arah - meskipun: I3
setara: I1, I2
,: I1, I2
tidak setara: I3
. Bahkan jika mereka identik secara fungsional, kelas yang mendukung keduanyaI1
danI2
tidak akan terdeteksi sebagai pendukungI3
.Ini berarti Anda menawarkan dua cara untuk mencapai hal yang sama - "manual" dan "manis" (dengan gula sintaksis Anda). Dan itu dapat berdampak buruk pada konsistensi kode. Menawarkan 2 cara berbeda untuk mencapai hal yang sama di sini, di sana, dan di tempat lain menghasilkan 8 kemungkinan kombinasi :)
OKE, kamu tidak bisa. Tapi mungkin itu pertanda baik?
Jika
I1
danI2
menyiratkan tanggung jawab yang berbeda (jika tidak, tidak perlu memisahkan mereka di tempat pertama), maka mungkinfoo()
salah untuk mencoba dansomething
melakukan dua tanggung jawab yang berbeda sekaligus?Dengan kata lain, bisa jadi itu
foo()
sendiri melanggar prinsip Tanggung Jawab Tunggal, dan fakta bahwa itu membutuhkan pemeriksaan tipe casting dan runtime untuk menariknya adalah sebuah bendera merah yang mengkhawatirkan kita tentang hal itu.EDIT:
Jika Anda bersikeras memastikan bahwa
foo
dibutuhkan sesuatu yang mengimplementasikanI1
danI2
, Anda dapat meneruskannya sebagai parameter terpisah:Dan mereka bisa menjadi objek yang sama:
Apa
foo
peduli jika mereka adalah contoh yang sama atau tidak?Tetapi jika itu benar-benar peduli (mis. Karena mereka tidak berkewarganegaraan), atau Anda hanya berpikir ini terlihat jelek, seseorang dapat menggunakan obat generik sebagai gantinya:
Sekarang kendala generik menangani efek yang diinginkan sambil tidak mencemari dunia luar dengan apa pun. Ini adalah C # idiomatis, berdasarkan pada pemeriksaan waktu kompilasi, dan secara keseluruhan terasa lebih tepat.
Saya melihat
I3 : I2, I1
agak sebagai upaya untuk meniru jenis serikat dalam bahasa yang tidak mendukungnya di luar kotak (C # atau Java tidak, sedangkan jenis serikat ada dalam bahasa fungsional).Mencoba meniru suatu konstruksi yang tidak terlalu didukung oleh bahasa sering kali kikuk. Demikian pula, di C # Anda dapat menggunakan metode ekstensi dan antarmuka jika Anda ingin meniru mixin . Bisa jadi? Iya. Ide bagus? Saya tidak yakin.
sumber
Jika ada kepentingan khusus dalam membuat kelas mengimplementasikan kedua antarmuka, maka saya tidak melihat ada yang salah dengan membuat antarmuka ketiga yang mewarisi dari keduanya. Namun, saya akan berhati-hati untuk tidak jatuh ke dalam perangkap hal-hal rekayasa ulang. Kelas bisa mewarisi dari satu atau yang lain, atau keduanya. Kecuali jika program saya memiliki banyak kelas dalam tiga kasus ini, itu sedikit banyak untuk membuat antarmuka untuk keduanya, dan tentu saja tidak jika kedua antarmuka tidak memiliki hubungan satu sama lain (tidak ada antarmuka IComparableAndSerializable silakan).
Saya mengingatkan Anda bahwa Anda dapat membuat kelas abstrak juga yang mewarisi dari kedua antarmuka, dan Anda dapat menggunakan kelas abstrak itu di tempat I3. Saya pikir itu salah untuk mengatakan bahwa Anda tidak boleh melakukannya, tetapi Anda setidaknya harus memiliki alasan yang bagus.
sumber
Saya tidak setuju. Baca tentang antarmuka marker .
sumber
instanceof
alih-alih polimorfisme