Bir arabirim diğer arabirimlerden miras alırsa 'boş' sayılır mı?


12

Boş arayüzler genellikle anlayabildiğim kadarıyla kötü uygulamaları düşünür - özellikle nitelikler gibi şeylerin dil tarafından desteklendiği yerlerde.

Ancak, bir arabirim diğer arabirimlerden miras alırsa 'boş' sayılır mı?

interface I1 { ... }
interface I2 { ... } //unrelated to I1

interface I3
    : I1, I2
{
    // empty body
}

Uygulayan her şeyin uygulanması I3gerekir I1ve I2devralınan farklı sınıflardan nesneler I3daha sonra birbirinin yerine kullanılabilir (aşağıya bakın), I3 boş çağırmak doğru mu? Eğer öyleyse bunu mimarileştirmenin daha iyi bir yolu ne olurdu?

// 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
    }
}

1
Bir parametre / alan vb. Türü olarak birden fazla arabirimi belirleyememek, C # / .NET'te bir tasarım hatasıdır.
CodesInChaos

Bence bu bölümün mimarisini geliştirmenin daha iyi bir yolu ayrı bir soruya dönüşmelidir. Şu anda kabul edilen cevabın soru başlığıyla ilgisi yok.
Kapol

@CodesInChaos Hayır, bunu zaten yapmanın iki yolu olduğu için değil. Biri bu soruda, diğer yöntem yöntem argümanı için genel bir tür parametresi belirtmek ve her iki arabirimi belirtmek için where kısıtlamasını kullanmak olacaktır.
Andy

Bir sınıf uygular I1 ve I2 değil I3, gerektiği mantıklı mı değil tarafından kullanılacak muktedir foo? (Cevabınız "evet" ise, devam edin!)
user253751

@Andy CodesInChaos'un böyle bir kusur olduğu iddiasına tamamen katılmama rağmen , yöntemdeki jenerik tip parametrelerinin bir çözüm olduğunu düşünmüyorum - yöntemin uygulanmasında kullanılan bir türü bilmenin sorumluluğunu ne olursa olsun itiyor yanlış hissettiren yöntemi çağırıyor. Belki de var : I1, I2 something = new A();yerel değişkenler gibi bazı sözdizimleri iyi olurdu (Konrad'ın cevabında ortaya çıkan iyi noktaları görmezden
Kai

Yanıtlar:


27

I3'ü uygulayan her şeyin I1 ve I2'yi uygulaması gerekir ve I3'ü devralan farklı sınıflardan nesneler daha sonra birbirinin yerine kullanılabilir (aşağıya bakın), bu yüzden I3'ü boş çağırmak doğru mu? Eğer öyleyse bunu mimarileştirmenin daha iyi bir yolu ne olurdu?

"Paketleme" I1ve I2içine I3hoş bir (?) Kısayol veya her ikisinin de uygulanmasını istediğiniz yere bir tür sözdizimi şekeri sunar.

Bu yaklaşımdaki sorun açıkça uygulayan diğer geliştiriciler duramazsın ki I1 ve I2 yan yana, ancak her iki yönde çalışmaz - ederken : I3eşdeğerdir : I1, I2, : I1, I2bir eşdeğer değildir : I3. İşlevsel olarak aynı olsalar bile, her ikisini de destekleyen I1ve destekleyici I2olarak algılanmayacak bir sınıf I3.

Bu, aynı şeyi gerçekleştirmenin iki yolunu sunduğunuz anlamına gelir - "manuel" ve "tatlı" (sözdizimi şekerinizle). Kod tutarlılığı üzerinde kötü bir etkisi olabilir. Burada, orada ve başka bir yerde aynı şeyi başarmanın 2 farklı yolunu sunmak zaten 8 olası kombinasyonla sonuçlanıyor :)

void foo() {
    I1 something = new A();
    something = new B();
    something.SomeI1Function();
    something.SomeI2Function(); // we can't do this without casting first
}

Tamam, yapamazsın. Ama belki de bu aslında iyi bir işarettir?

Eğer I1ve I2farklı sorumluluklar ima (Aksi ilk etapta bunları ayırmak gerek olmazdı), sonra belki foo()denemek ve yapmak yanlıştır somethingtek seferde iki farklı sorumluluklarını yerine?

Başka bir deyişle, foo()Tek Sorumluluk ilkesini ihlal ediyor olabilir ve onu çıkarmak için döküm ve çalışma zamanı kontrolleri gerektirmesi, bizi bu konuda endişe verici kırmızı bir bayraktır.

DÜZENLE:

fooBunun uygulayan bir şeyi almasını sağlamakta ısrar ettiyseniz I1ve I2bunları ayrı parametreler olarak iletebilirsiniz:

void foo(I1 i1, I2 i2) 

Ve aynı nesne olabilirler:

foo(something, something);

fooAynı örnek olup olmamaları ne yapar ?

Ama umursamıyorsa (örneğin, vatansız olmadıkları için) veya bunun çirkin göründüğünü düşünüyorsanız, bunun yerine jenerikler kullanılabilir:

void Foo<T>(T something) where T: I1, I2

Artık genel kısıtlamalar, dış dünyayı hiçbir şeyle kirletmezken istenen etkiyi halleder. Bu, derleme zamanı kontrollerine dayanan deyimsel C # ve genel olarak daha doğru hissediyor.

Bir I3 : I2, I1şekilde sendika türlerini kutunun dışında desteklemeyen bir dilde taklit etme çabası olarak görüyorum (C # veya Java desteklemiyor, ancak sendika türleri fonksiyonel dillerde mevcut).

Dil tarafından gerçekten desteklenmeyen bir yapıyı taklit etmeye çalışmak genellikle karmaşıktır. Benzer şekilde, C # 'da mixins öykünmek istiyorsanız uzantı yöntemleri ve arabirimler kullanabilirsiniz . Mümkün? Evet. İyi bir fikir? Emin değilim.


4

Bir sınıfın her iki arabirimi de uygulamasında özel bir önem varsa, o zaman her ikisinden de miras kalan üçüncü bir arabirim oluştururken yanlış bir şey görmüyorum. Ancak, aşırıya kaçan şeylerin tuzağına düşmemeye dikkat ederdim. Bir sınıf birinden veya diğerinden veya her ikisinden miras alabilir. Programımın bu üç durumda bolca sınıfları olmadıkça, her ikisi için de bir arayüz oluşturmak biraz fazladır ve kesinlikle bu iki arayüzün birbiriyle ilişkisi yoksa (IComparableAndSerializable arayüzleri lütfen).

Size her iki arabirimden miras kalan soyut bir sınıf oluşturabileceğinizi ve bu soyut sınıfı I3 yerine kullanabileceğinizi hatırlatırım. Bence bunu yapmaman gerektiğini söylemek yanlış olur, ama en azından iyi bir sebebin olmalı.


Ha? Kesinlikle tek bir sınıfta iki arabirim uygulayabilirsiniz. Arabirimleri miras almazsınız, uygularsınız.
Andy

@Andy Tek bir sınıfın iki arabirimi uygulayamayacağını nereden söyledim? "Uygulama", anlambilim yerine "miras" kelimesinin kullanımı ile ilgili. C ++ 'da, arayüzlere en yakın şey soyut sınıflar olduğu için miras mükemmel çalışır .
Neil

Devralma ve arayüz uygulaması aynı kavramlar değildir. Birden fazla alt sınıfı devralan sınıflar, bazı garip sorunlara yol açabilir, bu da birden fazla arabirimin uygulanmasıyla ilgili değildir. Ve C ++ eksik arayüzleri farkın ortadan kalktığı anlamına gelmez, C ++ 'ın yapabileceği şeyle sınırlı olduğu anlamına gelir. Kalıtım "is-a" ilişkisidir, arabirimler ise "can-do" yı belirtir.
Andy

@Andy Bahsedilen sınıflarda uygulanan yöntemler arasındaki çarpışmalardan kaynaklanan garip konular, evet. Ancak, bu artık isimde veya pratikte olmayan bir arayüz değildir. Metodları bildiren fakat uygulamayan soyut sınıflar, isim, bir arayüz haricinde her anlamda anlamlıdır. Terminoloji diller arasında değişir, ancak bu bir referans ve bir göstergenin aynı kavram olmadığı anlamına gelmez. Bilgiçlik taslayan bir nokta, ama eğer bu konuda ısrar ediyorsanız, katılmama konusunda anlaşmamız gerekir.
Neil

"Terminoloji diller arasında değişir" Hayır, değişmez. OO terminolojisi diller arasında tutarlıdır; Kullandığım her OO dilinde başka bir şey ifade eden soyut bir sınıf duymadım. Evet, C ++ 'da bir arabirimin anlambilimine sahip olabilirsiniz, ancak mesele şu ki, birinin o dilde soyut bir sınıfa gitmesini ve bu semantiği kırmasını engelleyen hiçbir şey yoktur.
Andy

2

Boş arayüzler genellikle kötü uygulama olarak kabul edilir

Katılmıyorum. İşaretleyici arabirimleri hakkında bilgi edinin .

Tipik bir arabirim, bir uygulayıcı sınıfın desteklemesi gereken işlevselliği (yöntem bildirimleri biçiminde) belirtirken, bir işaretçi arabiriminin bunu yapması gerekmez. Sadece böyle bir arayüzün varlığı, uygulayıcı sınıfın belirli davranışını gösterir.


OP'nin bunların farkında olduğunu düşünüyorum, ama aslında biraz tartışmalılar. Bazı yazarlar onları tavsiye ederken (örn. "Etkili Java" da Josh Bloch), bazıları Java'nın henüz ek açıklamaları olmadığı zamanlardan gelen bir antipattern ve miras olduğunu iddia ediyor. (Java'daki ek açıklamalar kabaca .NET'teki özelliklerin eşdeğeridir). Benim izlenimim Java dünyasında .NET'ten çok daha popüler oldukları.
Konrad Morawski

1
Onları arada bir kullanıyorum, ama biraz kibirli hissettiriyor - başımın üstünde, olumsuz taraflar miras aldıkları gerçeğine yardım edemezsiniz, bu yüzden bir sınıfı biriyle işaretlediğinizde, tüm çocukları isteyip istemediğinizi de işaretleyin. Ve instanceofpolimorfizm yerine kötü uygulamayı kullanmayı teşvik ediyor gibi görünüyorlar
Konrad Morawski
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.