Cukup mudah. Saya menerapkan antarmuka, tetapi ada satu properti yang tidak perlu untuk kelas ini dan, pada kenyataannya, tidak boleh digunakan. Ide awal saya adalah melakukan sesuatu seperti:
int IFoo.Bar
{
get { raise new NotImplementedException(); }
}
Saya kira tidak ada yang salah dengan ini, tapi itu tidak terasa "benar". Adakah orang lain yang pernah mengalami situasi serupa sebelumnya? Jika demikian, bagaimana Anda mendekatinya?
c#
design
interfaces
implementations
Chris Pratt
sumber
sumber
Jawaban:
Ini adalah contoh klasik tentang bagaimana orang memutuskan untuk melanggar Prinsip Subtitusi Liskov. Saya sangat tidak menyarankannya tetapi akan mendorong kemungkinan solusi yang berbeda:
Jika yang pertama adalah kasus untuk Anda, hanya saja jangan mengimplementasikan antarmuka pada kelas itu. Anggap saja seperti soket listrik di mana lubang arde tidak perlu sehingga tidak benar - benar menempel ke arde. Anda tidak memasukkan apa pun dengan ground dan bukan masalah besar! Tapi begitu Anda menggunakan sesuatu yang membutuhkan tanah - Anda bisa mengalami kegagalan yang spektakuler. Lebih baik tidak meninju lubang tanah palsu. Jadi, jika kelas Anda tidak benar - benar melakukan apa yang diinginkan antarmuka, jangan mengimplementasikan antarmuka.
Berikut adalah beberapa bit cepat dari wikipedia:
Prinsip Substitusi Liskov dapat dengan mudah dirumuskan sebagai, "Jangan memperkuat pra-kondisi, dan jangan melemahkan pasca-kondisi".
Untuk interoperabilitas semantik dan substitusi antara implementasi berbeda dari kontrak yang sama - Anda membutuhkan mereka semua untuk berkomitmen pada perilaku yang sama.
Prinsip Segregasi Antarmuka berbicara pada gagasan bahwa antarmuka harus dipisahkan menjadi set yang kohesif sehingga Anda tidak memerlukan antarmuka yang melakukan banyak hal yang berbeda ketika Anda hanya menginginkan satu fasilitas. Pikirkan lagi antarmuka soket listrik, itu bisa memiliki termostat juga, tetapi itu akan membuat lebih sulit untuk memasang soket listrik dan mungkin membuatnya lebih sulit untuk digunakan untuk keperluan non-pemanasan. Seperti soket listrik dengan termostat, antarmuka besar sulit diimplementasikan dan sulit digunakan.
sumber
Terlihat baik bagi saya, jika ini adalah situasi Anda.
Namun, menurut saya antarmuka Anda (atau menggunakannya) rusak jika kelas turunan tidak benar-benar mengimplementasikan semua itu. Pertimbangkan untuk memisahkan antarmuka itu.
Penafian: Ini membutuhkan beberapa pewarisan untuk melakukannya dengan benar, dan saya tidak tahu apakah C # mendukungnya.sumber
Saya telah menemukan situasi ini. Bahkan seperti yang ditunjukkan di tempat lain BCL memiliki contoh seperti itu ... Saya akan mencoba memberikan contoh yang lebih baik dan memberikan beberapa alasan:
Ketika Anda memiliki antarmuka yang sudah dikirim yang Anda simpan untuk alasan kompatibilitas dan ...
Antarmuka berisi anggota yang usang atau rusak. Misalnya
BlockingCollection<T>.ICollection.SyncRoot
(antara lain) sementaraICollection.SyncRoot
tidak usang, itu akan melemparNotSupportedException
.Antarmuka berisi anggota yang didokumentasikan sebagai opsional, dan implementasi dapat membuang pengecualian. Misalnya pada MSDN tentang
IEnumerator.Reset
hal itu mengatakan:Dengan kesalahan desain antarmuka, seharusnya lebih dari satu antarmuka di tempat pertama. Ini adalah pola umum dalam BCL untuk mengimplementasikan versi Read Only dari wadah
NotSupportedException
. Saya telah melakukannya sendiri, inilah yang diharapkan sekarang ... SayaICollection<T>.IsReadOnly
kembalitrue
sehingga Anda dapat memberi tahu mereka appart. Desain yang benar seharusnya memiliki versi antarmuka yang Dapat Dibaca, dan kemudian antarmuka penuh mewarisi dari itu.Tidak ada antarmuka yang lebih baik untuk digunakan. Sebagai contoh, saya memiliki kelas yang memungkinkan Anda mengakses item dengan indeks, memeriksa apakah itu berisi item dan pada indeks apa, itu memiliki beberapa ukuran, Anda dapat menyalinnya ke sebuah array ... sepertinya pekerjaan
IList<T>
tetapi kelas saya memiliki ukuran tetap, dan tidak mendukung menambahkan atau menghapus, sehingga berfungsi lebih seperti sebuah array daripada daftar. Tapi tidak adaIArray<T>
di BCL .Antarmuka milik API yang porting ke beberapa platform, dan dalam implementasi platform tertentu beberapa bagian tidak didukung. Idealnya akan ada beberapa cara untuk mendeteksinya, sehingga kode portabel yang menggunakan API seperti itu dapat memutuskan apa pun untuk memanggil bagian-bagian itu ... tetapi jika Anda memanggilnya, itu benar-benar tepat untuk didapatkan
NotSupportedException
. Ini terutama benar, jika ini adalah port ke platform baru yang tidak diramalkan dalam desain aslinya.Juga pertimbangkan mengapa itu tidak didukung?
Terkadang
InvalidOperationException
merupakan pilihan yang lebih baik. Sebagai contoh, satu lagi cara untuk menambahkan polimorfisme dalam suatu kelas adalah dengan memiliki berbagai implementasi antarmuka internal dan kode Anda memilih yang mana untuk instantiate tergantung pada parameter yang diberikan dalam konstruktor kelas. [Ini khususnya berguna jika Anda tahu bahwa set opsi sudah diperbaiki dan Anda tidak ingin mengizinkan kelas pihak ketiga diperkenalkan dengan injeksi dependensi.] Saya telah melakukan ini untuk mendukung ThreadLocal karena implementasi pelacakan dan non-pelacakan adalah appart terlalu jauh, dan apa yangThreadLocal.Values
melempar pada implementasi non-pelacakan?InvalidOperationException
bahkan, itu tidak tergantung pada keadaan objek. Dalam hal ini saya memperkenalkan kelas sendiri, dan saya tahu bahwa metode ini harus diimplementasikan dengan hanya melempar pengecualian.Terkadang nilai default masuk akal. Misalnya pada yang
ICollection<T>.IsReadOnly
disebutkan di atas, masuk akal untuk mengembalikan 'benar' atau 'salah' tergantung kasusnya. Jadi ... apa arti semantiknyaIFoo.Bar
? mungkin ada beberapa nilai default yang masuk akal untuk kembali.Tambahan: jika Anda mengendalikan antarmuka (dan Anda tidak perlu tetap menggunakannya untuk kompatibilitas) seharusnya tidak ada kasus di mana Anda harus melempar
NotSupportedException
. Meskipun, Anda mungkin harus membagi antarmuka menjadi dua atau lebih antarmuka yang lebih kecil agar sesuai untuk kasus Anda, yang dapat menyebabkan "polusi" dalam situasi ekstrem.sumber
Ya, desainer perpustakaan .Net melakukannya. Kelas Kamus melakukan apa yang Anda lakukan. Menggunakan implementasi eksplisit untuk menyembunyikan * beberapa metode IDictionary secara efektif . Ini dijelaskan lebih baik di sini , tetapi untuk meringkas, untuk menggunakan Kamus's Add, CopyTo, atau Hapus metode yang mengambil KeyValuePairs, Anda harus terlebih dahulu melemparkan objek ke IDictionary.
* Ini bukan "menyembunyikan" metode dalam arti ketat dari kata "sembunyikan" seperti yang digunakan Microsoft . Tapi saya tidak tahu istilah yang lebih baik dalam hal ini.
sumber
Anda selalu bisa menerapkan dan mengembalikan nilai jinak atau nilai default. Bagaimanapun itu hanya sebuah properti. Itu bisa mengembalikan 0 (properti default int), atau nilai apa pun yang masuk akal dalam implementasi Anda (int.MinValue, int.MaxValue, 1, 42, dll ...)
Melempar pengecualian sepertinya bentuk yang buruk.
sumber