Özelliklerden birine ihtiyaç duymadığınızda bir arayüz uygulamak


31

Oldukça yalındır. Bir arayüz uyguluyorum, ancak bu sınıf için gereksiz olan ve aslında kullanılmaması gereken bir özellik var. İlk fikrim şunun gibi bir şey yapmaktı:

int IFoo.Bar
{
    get { raise new NotImplementedException(); }
}

Sanırım bu konuda yanlış bir şey yok, ama "doğru" hissetmiyor. Daha önce benzer bir durumla karşılaşan başka biri var mı? Eğer öyleyse, nasıl yaklaştınız?


1
Belli bir şekilde, C # 'da bir arabirim uygulayan ancak belirli bir yöntemin uygulanmadığını belgeleyen bazı yarı yaygın olarak kullanılan bir sınıf olduğunu hatırlıyorum. Onu bulabilir miyim görmeye çalışacağım.
Mage Xy

Bunu bulabilirsen kesinlikle görmek isterim.
Chris Pratt,

23
Bunun, .NET'in kütüphanesinde birden fazla örneğine dikkat çekebilirim - VE KÖTÜ KORKUNÇ HATALARI OLARAK TÜM TANINMIŞTIR . Bu, Liskov Yerine Takma Prensibi'nin ikonik ve yaygın bir ihlalidir - LSP'yi ihlal etmeme nedenleri burada
Jimmy Hoffa

3
Bu belirli arayüzü uygulamanız gerekiyor mu, yoksa bir süper yüz tanıtabilir ve kullanabilir misiniz?
Christopher Schultz

6
"bu sınıf için gereksiz olan bir özellik" - Bir arabirimin bir kısmının gerekli olup olmadığı, uygulayıcıların değil, arabirimin istemcilerine bağlıdır. Bir sınıf, bir arayüzün bir üyesini makul bir şekilde uygulayamıyorsa, sınıf arayüz için uygun değildir. Bu, arayüzün kötü bir şekilde tasarlandığı anlamına gelebilir - muhtemelen çok fazla şey yapmaya çalışıyor - ancak bu sınıfa yardımcı olmuyor.
Sebastian Redl

Yanıtlar:


51

Bu, insanların Liskov Altyazı İlkesini ihlal etmeye nasıl karar verdiğinin klasik bir örneğidir. Kesinlikle ciddiye almıyorum ama muhtemelen farklı bir çözümü teşvik ediyorum:

  1. Belki de yazdığınız sınıf, arayüzün tüm üyelerini kullanmıyorsa, arayüzün öngördüğü işlevleri sağlamıyordur.
  2. Alternatif olarak, bu arayüz birçok şey yapıyor olabilir ve Arabirim Ayrıştırma İlkesi uyarınca ayrılabilir.

İlki sizin için geçerliyse, arayüzü o sınıfa uygulamayın. Bunu, toprak deliğinin gereksiz olduğu bir elektrik prizi gibi düşünün, böylece gerçekten toprağa yapışmaz. Toprağa takılan hiçbir şey yapmıyorsunuz ve hiçbir şey yapmıyorsunuz! Fakat bir yere ihtiyacı olan bir şeyi kullanır kullanmaz - muhteşem bir başarısızlıkta bulunabilirsiniz. İçinde sahte bir delik açmamak daha iyidir. Bu nedenle, sınıfınız aslında arabirimin amaçladığı şeyi yapmazsa, arabirimi uygulamayın.


İşte wikipedia'dan birkaç hızlı bit:

Liskov Değiştirme Prensibi , "Ön koşulları güçlendirmeyin ve post-koşulları zayıflatmayın" olarak formüle edilebilir.

Daha resmi olarak, Liskov ikame prensibi (LSP), başlangıçta Veri soyutlama ve hiyerarşi başlıklı 1987 konferansı açılış konuşmasında Barbara Liskov tarafından tanıtılan (güçlü) davranış alt tipi olarak adlandırılan alt tiplendirme ilişkisinin özel bir tanımıdır. Bu sadece sözdizimsel bir ilişki değil anlamsal bir ilişkidir, çünkü hiyerarşideki türlerin anlamsal birlikte çalışabilirliğini garanti etmeyi amaçlamaktadır , [...]

Anlamsal birlikte çalışabilirlik ve aynı sözleşmelerin farklı uygulamaları arasındaki ikame için - aynı davranışları yerine getirmeleri için hepsine ihtiyacınız var.


Arabirim Ayrıştırma İlkesi , arabirimlerin tek bir tesis istediğinizde pek çok farklı şey yapan bir arabirime ihtiyaç duymayacak şekilde uyumlu kümelere ayrılması gerektiği fikrinden bahseder . Bir elektrik prizinin arayüzünün Tekrar düşün, o olabilir aynı zamanda bir termostat var, ama zor bir elektrik soketi yüklemek ve dışı ısıtma amaçlı kullanılması daha zor bunu yapabilir hale getirecektir. Termostatlı bir elektrik soketi gibi, büyük arayüzlerin uygulanması zor ve kullanımı zordur.

Arayüz ayrıştırma ilkesi (ISS), hiçbir müşterinin kullanmadığı yöntemlere bağlı kalmaya zorlanmaması gerektiğini belirtmektedir. [1] ISS, daha küçük ve daha özel olanlara kadar büyük ara yüzleri böler; böylece müşterilerin yalnızca ilgilendikleri yöntemleri bilmeleri gerekir.


Kesinlikle. Muhtemelen bu yüzden, her şeyden önce bana doğru gelmiyordu. Bazen aptalca bir şey yaptığına dair nazik bir hatırlatmaya ihtiyacın var. Teşekkürler.
Chris Pratt,

@ChrisPratt yapmak gerçekten yaygın bir hatadır, formalizmin değerli olmasının nedeni budur - kod kokularının sınıflandırılması daha hızlı bir şekilde tanımlanmasına ve daha önce kullanılan çözümlerin hatırlanmasına yardımcı olur.
Jimmy Hoffa,

4

Senin durumun buysa, bana iyi görünüyor.

Bununla birlikte, eğer bir türetme sınıfı gerçekten hepsini uygulamıyorsa, arayüzünüzün (veya kullanımının) kesildiği anlaşılıyor. Bu arayüzü ayırmayı düşünün.

Feragatname: Bu, doğru şekilde yapmak için çoklu kalıtım gerektirir ve C # 'nın bunu destekleyip desteklemediği hakkında hiçbir fikrim yok.


6
C #, birden fazla sınıf mirasını desteklemez, ancak arabirimler için onu destekler.
mgw854

2
Bence sen haklısın. Her şeyden önce arayüz bir sözleşmedir ve bu özel sınıfın, bu özellik devre dışı bırakılırsa arayüzü kullanan herhangi bir şeyi kıracak şekilde kullanılmayacağını bilsem bile, bu açık değildir.
Chris Pratt

@ ChrisPratt: Evet.
Monica

4

Bu duruma rastladım. Aslında başka yerlerde de belirtildiği gibi BCL'nin bu gibi örnekleri var ... Daha iyi örnekler sunmaya ve bazı gerekçeler sunmaya çalışacağım:

Zaten gönderilen bir arabirim varsa, uyumluluk nedenleriyle sakladığınız ve ...

  • Arabirim, eski veya önerilmeyen üyeler içerir. Mesela BlockingCollection<T>.ICollection.SyncRoot(diğerleri arasında) ICollection.SyncRootkendi başına modası geçmiş olmasa da, fırlatacaktır NotSupportedException.

  • Arayüz, isteğe bağlı olduğu belgelenen üyeler içerir ve uygulamanın istisna atabileceğini söyleyebilir. Mesela MSDN ile ilgili IEnumerator.Resetolarak şöyle diyor:

COM birlikte çalışabilirliği için sıfırlama yöntemi sağlanır. Uygulanması zorunlu değildir; bunun yerine, uygulayıcı yalnızca bir NotSupportedException atabilir.

  • Arayüzün tasarımında bir hata olması durumunda, ilk başta birden fazla arayüz olması gerekirdi. BCL'de bulunan ve Read Only konteyner versiyonlarını uygulamak yaygın bir kalıptır NotSupportedException. Kendim yaptım, şimdi beklenen budur ... Onlara ICollection<T>.IsReadOnlygeri döndüm, trueböylece onlara söylerim Doğru tasarım, arayüzün Okunabilir bir versiyonuna sahip olacaktı ve ardından tam arayüz ondan kalıtsaldı.

  • Kullanılacak daha iyi bir arayüz yok. Örneğin, öğelere dizine göre erişmenizi, bir öğe içerip içermediğini ve hangi dizinde bir boyutu olduğunu kontrol etmenizi sağlayan bir sınıfa sahibim, diziye kopyalayabilirsiniz ... bir iş gibi görünüyor IList<T>ama sınıfım var sabit bir boyut ve eklemeyi veya kaldırmayı desteklemediğinden, bir listeden çok bir dizi gibi çalışır. Ama var hayır IArray<T>BCL .

  • Arayüz, birden fazla platforma taşınan bir API'ye aittir ve belirli bir platformun uygulanmasında bazı kısımlar desteklenmez. İdeal olarak, onu saptamanın bir yolu olabilir, böylece bu tür API'yi kullanan taşınabilir kod, bu parçaları aramaya ya da aramamaya karar verebilir ... ama onları çağırırsanız, elde etmek tamamen uygundur NotSupportedException. Bu, özellikle orijinal tasarımda öngörülemeyen yeni bir platforma açılan bir liman ise geçerlidir.


Ayrıca neden desteklenmediğini de düşünün?

Bazen InvalidOperationExceptiondaha iyi bir seçenek. Örneğin, bir sınıfa polimorfizm eklemenin bir başka yolu, bir iç ara yüzün çeşitli uygulamasına sahip olmaktır ve kodunuz sınıfın kurucusunda verilen parametrelere bağlı olarak hangisinin başlatılacağını seçmektir. [Seçenek kümesinin sabit olduğunu ve üçüncü taraf sınıflarının bağımlılık enjeksiyonuyla kullanılmasına izin vermek istemiyorsanız özellikle yararlıdır.] Bunu, ThreadLocal'ı desteklemek için yaptım çünkü izleme ve izleme dışı uygulama çok uzak appart ve ThreadLocal.Valuesizleme dışı uygulamaya ne atıyor ?InvalidOperationExceptiontho bile, nesnenin durumuna bağlı değildir. Bu durumda sınıfa kendim tanıttım ve bu yöntemin sadece bir istisna atarak uygulanması gerektiğini biliyordum.

Bazen varsayılan bir değer anlamlıdır. Mesela ICollection<T>.IsReadOnlyyukarıda belirtilenler için, duruma göre sadece "doğru" veya "yanlış" geri dönmenin bir anlamı vardır. Peki ... anlam IFoo.Barnedir? geri dönmek için bazı makul varsayılan değer olabilir.


Ek: Arabirimin kontrolü sizde ise (ve uyumluluk için onunla kalmanıza gerek kalmazsa) atmanız gereken bir durum olmamalıdır NotSupportedException. Bununla birlikte, sizin durumunuza uygun olması için arayüzü iki veya daha küçük arayüze ayırmanız gerekebilir; bu, aşırı durumlarda "kirlenmeye" yol açabilir.


0

Daha önce benzer bir durumla karşılaşan başka biri var mı?

Evet. Net kütüphane tasarımcıları yaptı. Sözlük sınıfı yaptığınız şeyi yapar. Bazı IDictionary yöntemlerini gizlemek için açık bir uygulama kullanıyor . Burada daha iyi açıklanmaktadır , ancak özetlemek gerekirse, KeyValuePairs'i alan Sözlüğün Ekle, Kopyala veya Kaldır yöntemlerini kullanmak için, önce nesneyi bir Kimlik Doğrulayıcısına atmanız gerekir.

* Microsoft'un kullandığı yöntemlerle "hide" kelimesini tam anlamıyla "gizlemek" değildir . Ancak bu durumda daha iyi bir terim bilmiyorum.


... bu korkunç.
Monica ile

Neden aşağı oy?
user2023861

2
Muhtemelen soruyu cevaplamadığın için. Ish. Sırala.
Monica

@LightnessRacesinOrbit, daha net hale getirmek için cevabımı güncelledim. Umarım OP'ye yardımcı olur.
user2023861

2
Bana soruya cevap veriyor gibi görünüyor. Bunun iyi veya kötü bir fikir olabileceği gerçeği, sorunun kapsamı dahilinde görünmüyor, ancak cevapların belirtilmesi için hala yararlı olabilir.
Ellesedil

-6

Her zaman sadece iyi huylu bir değer veya varsayılan değer uygulayabilir ve geri döndürebilirsiniz. Sonuçta bu sadece bir özellik. 0 değerini döndürür (int'nin varsayılan özelliği) veya uygulamanızdaki değer ne olursa olsun (int.MinValue, int.MaxValue, 1, 42, vb ...)

//We don't officially implement this property
int IFoo.Bar
{
     { get; }
}

Bir istisna atmak kötü bir form gibi görünüyor.


2
Niye ya? Yanlış veriyi daha iyi nasıl döndürmek?
Monica

1
Bu, LSP'yi kırar: Neden bir açıklama için Jimmy Hoffa'nın cevabına bakın.

5
Mümkün olan doğru dönüş değerleri yoksa, yanlış bir değer döndürmekten çok bir istisna atmak daha iyidir. Ne yaparsanız yapın, yanlışlıkla bu özelliği çağırırsa, programınız bozulur. Ancak bir istisna atarsanız, bunun neden hatalı çalıştığı belli olacaktır .
Tanner Swett

Arayüzü uygulayana göre şimdi değerin nasıl yanlış olacağını anlamıyorum! Bu senin uygulaman, istediğin her şey olabilir.
Jon Raynor

Derleme zamanında doğru değer bilinmiyorsa ne olur?
Andy,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.