Arabirim ve Temel sınıf


767

Ne zaman bir arayüz kullanmalıyım ve ne zaman bir temel sınıf kullanmalıyım?

Yöntemlerin temel uygulamasını tanımlamak istemiyorsam, her zaman bir arabirim olmalı mı?

Eğer bir Köpek ve Kedi dersim varsa. Neden PetBase yerine IPet uygulamak isteyeyim? ISheds veya IBarks (IMakesNoise?) İçin arayüzlere sahip olduğunu anlayabiliyorum, çünkü bunlar evcil hayvan bazında bir evcil hayvana yerleştirilebilir, ancak genel bir evcil hayvan için hangisinin kullanılacağını anlamıyorum.


11
Sadece dikkate almanız gereken bir nokta - arayüzler çok geç aşamalara kadar farkında olmayabileceğiniz birkaç sınır oluşturabilir. Örneğin, .NET ile bir arabirim üyesi değişkenini serileştiremezsiniz, bu nedenle bir sınıf Zoo ve IAnimals üyesi değişken dizisine sahipseniz, Zoo'yu serileştiremezsiniz (ve bu WebServices veya serileştirme gerektiren diğer şeyleri yazmak anlamına gelir) Bir ağrı).
synhershko

1
Bu soru, arayüzler kavramının anlaşılmasına yardımcı olabilir. stackoverflow.com/q/8531292/1055241
gprathour

Sadece merak ediyorum. CLR'de aşağıdaki alıntı ile C # tanıştım I tend to prefer using the interface technique over the base type technique because the base type technique doesn’t allow the developer to choose the base type that works best in a particular situation.. Alıntıda ne anlama geldiğini kavrayamıyorum. Birkaç temel tür oluşturabilir ve bunlardan herhangi biri için türetilmiş bir tür oluşturabiliriz, böylece bir geliştirici bir taban türü seçebilir. Birisi açıklayabilir mi, lütfen, neyi kaçırıyorum? Bunun bu sorunun bir parçası olabileceğine inanıyorum. Yoksa belirli bir alıntıyla ilgili bir tane daha yayınlamalı mıyım?
qqqqqqq

Yanıtlar:


501

Bir Köpek ve Kedi sınıfı örneğini ele alalım ve C # kullanarak örnek verelim:

Hem köpek hem de kedi hayvanlar, özellikle dörtlü memelilerdir (hayvanlar çok geneldir). Her ikisi için de soyut bir Memeli sınıfınız olduğunu varsayalım:

public abstract class Mammal

Bu temel sınıf muhtemelen aşağıdaki gibi varsayılan yöntemlere sahip olacaktır:

  • besleme
  • Dostum

Bunların hepsi, her iki tür arasında aşağı yukarı aynı uygulamaya sahip olan davranışlardır. Bunu tanımlamak için:

public class Dog : Mammal
public class Cat : Mammal

Şimdi bir hayvanat bahçesinde genellikle göreceğimiz başka memeliler olduğunu varsayalım:

public class Giraffe : Mammal
public class Rhinoceros : Mammal
public class Hippopotamus : Mammal

Bu hala geçerli olacak çünkü işlevselliğin merkezinde Feed()ve Mate()hala aynı olacak.

Bununla birlikte, zürafalar, gergedanlar ve suaygırları evcil hayvan çıkarabileceğiniz hayvanlar değildir. Burada bir arayüz yararlı olacaktır:

public interface IPettable
{
    IList<Trick> Tricks{get; set;}
    void Bathe();
    void Train(Trick t);
}

Yukarıdaki sözleşmenin uygulanması bir kedi ve köpek arasında aynı olmayacaktır; uygulamalarını miras almak için soyut bir sınıfa yerleştirmek kötü bir fikir olacaktır.

Köpek ve Kedi tanımlarınız şimdi şöyle görünmelidir:

public class Dog : Mammal, IPettable
public class Cat : Mammal, IPettable

Teorik olarak bunları daha yüksek bir temel sınıftan geçersiz kılabilirsiniz, ancak temelde bir arabirim, yalnızca kalıtım gereksinimi olmadan bir sınıfa ihtiyacınız olan şeyleri eklemenize izin verir.

Sonuç olarak, genellikle yalnızca bir soyut sınıftan miras alabilirsiniz (en statik olarak yazılan OO dillerinde ... istisnalar C ++ içerir) ancak birden çok arabirim uygulayabildiğiniz için, nesneleri kesinlikle gerektiği gibi oluşturmanıza izin verir .


149
Bence bu kadar basit değil. Soruyu (gereksinimleri) biraz değiştirdiniz, böylece arayüz daha mantıklı. Her zaman kendinize bir sözleşme (arayüz) veya paylaşılan bir uygulama (temel sınıf) tanımlayıp tanımlamadığınızı sormalısınız.
David Pokluda

18
Arayüz bir sözleşmedir. Sözleşmenin yalnızca bir hizmetin gerektirdiği kısmını açığa çıkarıyorsunuz. Bir 'PettingZoo' varsa, kesinlikle 'Mate'-ing'i kullanıcıya göstermek istemezsin!
Anthony Mastrean

10
@David Touche, bir arabirimin ne için olduğunu ve soyut bir sınıfın anlayışına karşı ne olduğunu daha iyi göstermek için yaptım. Bir köpek ve bir kedi katı bir gereklilik gibi görünmüyor!
Jon Limjap

2
Yorumlanan JIT-ing ortamlarında (özellikle JVM), sanal yöntem çağrılarının içeriğe bağlı olarak arayüz yöntemi çağrılarından önemli ölçüde daha hızlı olduğuna dikkat edilmelidir . "Bağlam" ı vurgularım çünkü JVM yavaş yöntem aramalarını sıklıkla optimize edebilir. (örneğin, arabirim mirasçılarının listesi aynı sınıfın örnekleri olma eğilimindeyse. Bu ne yazık ki karşılaştırmayı biraz zorlaştırır.) Performansa duyarlı bir şeyi optimize etmeye çalışıyorsanız ve ancak o zaman bunu düşünmelisiniz.
Philip Guin

14
Ayrıca, bir arayüz, bir evcil kayanın yıkanmasına ve püf noktalarının öğretilmesine izin verir, ancak besleme ve çiftleşme desteklenmez, çünkü bu bir kaya için saçma olacaktır.
eclux

146

Josh Bloch kendini Etkili Java 2d'de söyledi :

Soyut sınıflara göre arayüzleri tercih etme

Bazı temel noktalar:

  • Mevcut sınıflar, yeni bir arayüz uygulamak için kolayca uyarlanabilir . Yapmanız gereken tek şey, henüz mevcut değilse gerekli yöntemleri eklemek ve sınıf bildirimine bir uygulama cümlesi eklemek.

  • Arayüzler, karışımları tanımlamak için idealdir . Kısacası, bir mixin, bir sınıfın “birincil türüne” ek olarak, isteğe bağlı bir davranış sağladığını beyan etmek için uygulayabileceği bir türdür. Örneğin, Karşılaştırılabilir, bir sınıfın örneklerinin karşılıklı olarak karşılaştırılabilir diğer nesnelere göre sıralandığını bildirmesine izin veren bir mixin arabirimidir.

  • Arayüzler, hiyerarşik olmayan tip çerçevelerin oluşturulmasına izin verir . Tür hiyerarşileri bazı şeyleri düzenlemek için harikadır, ancak diğer şeyler düzgün bir hiyerarşiye girmez.

  • Arabirimler sınıf başına deyim ile güvenli ve güçlü işlevsellik geliştirmeleri sağlar . Türleri tanımlamak için soyut sınıflar kullanırsanız, kalıtım kullanmaktan başka alternatif olmadan işlevsellik eklemek isteyen programcıdan ayrılırsınız.

Dahası, dışa aktardığınız her önemsiz arabirimle birlikte gitmek için soyut bir iskelet uygulama sınıfı sağlayarak arabirimlerin ve soyut sınıfların erdemlerini birleştirebilirsiniz.

Öte yandan, arayüzlerin gelişmesi çok zordur. Bir arabirime bir yöntem eklerseniz, tüm uygulamaları bozulur.

Not: Kitabı satın alın. Çok daha detaylı.


71
Bir arayüzde her değişiklik yapılması gerektiğinde, bunu yapmanın kırılmaz yolu eskisinden miras kalan yeni bir arayüz oluşturmaktır. Bu, mevcut uygulamaları korur ve yeni uygulamada istediğinizi yapmanızı sağlar.
Scott Lawrence

5
"Arayüz Ayırma Prensibi" ne sahibiz. Bu prensip bize arayüzlerimizi nasıl yazdığımıza dikkat etmemizi öğretiyor, arayüzlerimizi yazarken sadece orada olması gereken yöntemleri eklemeye özen göstermeliyiz. Orada olmaması gereken yöntemler eklersek, arabirimi uygulayan sınıfların da bu yöntemleri uygulaması gerekir. Örneğin, İşçi adlı bir arabirim oluşturur ve öğle yemeği molası yöntemi eklersek, tüm çalışanlar bunu uygulamak zorunda kalacaktır. Sonuç olarak, kendisine özgü olmayan yöntemler içeren arabirimlere kirli veya yağ arabirimleri denir.
Eklavyaa

Java 8'den beri, Varsayılan yöntemler arabirimlere yeni işlevler eklemenizi ve bu arabirimi uygulayan mevcut sınıflar için geriye dönük uyumluluk sağlamanızı sağlar. Varsayılan yöntemler, uygulama sınıfında geçersiz kılınmadıysa, varsayılan olarak çağrılır. Tüm uygulayıcı sınıf, varsayılan yöntemleri geçersiz kılabilir veya bunları instance.defaultMethod () kullanarak doğrudan çağırabilir
Arpit Rastogi

118

Arayüzler ve temel sınıflar iki farklı ilişki biçimini temsil eder.

Kalıtım (temel sınıflar) "is-a" ilişkisini temsil eder. Örneğin, bir köpek ya da kedi "bir" evcil hayvan. Bu ilişki daima sınıfın (tek) amacını temsil eder ( "tek sorumluluk ilkesi" ile bağlantılı olarak ).

Arayüzler ise bir sınıfın ek özelliklerini temsil eder . Ben buna " Footek kullanımlık" olduğu gibi bir "is" ilişkisi , yani IDisposableC # arayüz olarak adlandırırdım.


10
Tüm cevaplardan, bu netlik kaybı olmadan kısalık en iyi karışımını verir
Matt Wilko

Birisi bana "has-a" ilişkisi olduğunda bir arayüz kullanmamı söyledi. Bunun her zaman doğru olup olmadığından emin değilim; Dizüstü bilgisayarımda bir ekran var, bu yüzden dizüstü bilgisayar IScreen uygulamalı mı yoksa bir Screen özelliğine sahip mi? İkincisi benim için daha doğal görünüyor.
Berend

1
@berend, ekranlar arayüzler - VGA, HDMI vb. aracılığıyla uygulandığı için yazdığınız komik
Konstantin

OOP olduğu için, gerçek yaşam senaryolarını takip etmek için hayal gücümüzü genişletmeliyiz. Her zaman geçerli değildir.
Crismogram

110

Modern tarz IPet ve PetBase'i tanımlamaktır .

Arayüzün avantajı, diğer kodun diğer yürütülebilir kodlarla herhangi bir bağ olmadan kullanabilmesidir. Tamamen "temiz." Ayrıca arayüzler karıştırılabilir.

Ancak temel sınıflar basit uygulamalar ve ortak yardımcı programlar için kullanışlıdır. Bu nedenle zamandan ve koddan tasarruf etmek için soyut bir temel sınıf da sağlayın.


pastanı al ve onu da ye!
Darren Kopp

3
Bir arabirim, diğer sınıfların kodunuzu nasıl kullanabileceğini tanımlar. Temel sınıf, uygulayıcıların arabiriminizi uygulamalarına yardımcı olur. İki farklı amaç için iki farklı şey.
Bill Michell

27
Burada "modern" bir şey yok. Aynı API ile temel sınıf ve arayüz gereksizdir. Bu yaklaşımı bazı durumlarda kullanabilirsiniz, ancak genellememelisiniz!
smentek

3
İkinci desteklenen cevap için en çok desteklenen yorum aslında cevabın kendisi ile aynı fikirde değil, ilginç. Arayüz ve taban sınıfın birlikte var olduğu birçok örneği gördüğümü söylemeliyim. Bu anlamda "modern" yoldur. Örneğin, MVVM deseninde, aslında INotifyPropertyChanged uygulayan ViewModelBase sınıfı vardır. Bunun yerine her görünüm modelinde arayüzü uygulanması, temel sınıf var neden meslektaşım bana sorulduğunda Ama onu ikna etmeye bilmiyorum
tete

1
Doğru. Bu 1 ya da diğeri meselesi değil. 2 farklı problemi ele almak için varlar. Arayüzler, bir uygulayıcı sınıfın ele alması gereken sözleşmelerdir. IoC ve TDD yönleri için son zamanlarda (bazen fanatik olarak) iyilik buldular. Özet / Temel sınıflar, hiyerarşik olarak yaygın mantık ve özellikleri gruplandırmaya yarar. Yinelenen kodu azaltır, bu da çözümlerin sürdürülebilirliğini artırır ve hataya daha az eğilimli hale getirir.
ComeIn

63

Arayüzler

  • 2 modül arasındaki sözleşmeyi tanımlar. Hiçbir uygulama yapılamaz.
  • Çoğu dil birden fazla arabirim uygulamanıza izin verir
  • Bir arayüzde değişiklik yapmak son derece önemli bir değişikliktir. Tüm uygulamaların yeniden derlenmesi / değiştirilmesi gerekir.
  • Tüm üyeler herkese açıktır. Uygulamalar tüm üyeleri uygulamak zorundadır.
  • Arabirimler Ayırmada yardımcı olur. Bir arayüzün arkasındaki herhangi bir şeyi alay etmek için sahte çerçeveleri kullanabilirsiniz
  • Arayüzler normalde bir tür davranışı gösterir
  • Arayüz uygulamaları birbirinden ayrılır / yalıtılır

Temel sınıflar

  • Türev yoluyla ücretsiz olarak alacağınız bazı varsayılan uygulamaları eklemenizi sağlar
  • C ++ dışında yalnızca bir sınıftan türetebilirsiniz. Birden fazla sınıftan bile olsa, bu genellikle kötü bir fikirdir.
  • Temel sınıfın değiştirilmesi nispeten kolaydır. Türevlerin özel bir şey yapmasına gerek yoktur
  • Temel sınıflar, türevlerle erişilebilen korumalı ve genel işlevleri açıklayabilir
  • Abstract Base sınıfları arayüzler gibi kolayca alay edilemez
  • Temel sınıflar normalde tür hiyerarşisini gösterir (IS A)
  • Sınıf türevleri bazı temel davranışlara bağlı olabilir (ana uygulama hakkında karmaşık bilgiye sahip olabilir). Bir adam için temel uygulamada bir değişiklik yapar ve diğerlerini kırırsanız işler dağınık olabilir.

Not: çerçeve tasarım yönergeleri, daha iyi sürümleri için temel sınıfların (arabirimlerin aksine) kullanılmasını önerir.
VNext

59

Genel olarak, soyut sınıflara göre arayüzleri tercih etmelisiniz. Soyut bir sınıf kullanmanın bir nedeni, somut sınıflar arasında ortak uygulamanızın olmasıdır. Tabii ki, hala bir arabirim (IPet) bildirmeli ve bu arabirimi uygulamak için soyut bir sınıf (PetBase) kullanmalısınız.Küçük, farklı arabirimler kullanarak, esnekliği daha da artırmak için katları kullanabilirsiniz. Arayüzler, sınırların ötesinde tiplerin maksimum esneklik ve taşınabilirliğine izin verir. Referansları sınırlar üzerinden geçirirken, somut tipten değil her zaman arayüzden geçin. Bu, alıcı ucun somut uygulamayı belirlemesine izin verir ve maksimum esneklik sağlar. Bu, TDD / BDD tarzında programlama yaparken kesinlikle doğrudur.

Dörtlü Çet, "Miras, ebeveyninin uygulamasının ayrıntılarına bir alt sınıf açığa çıkardığı için, genellikle" miras kapsüllemeyi keser "denir. Bunun doğru olduğuna inanıyorum.


Yeesh. Şahsen, bu eşek geri olduğunu düşünüyorum. Arabirimler bir tür için asgari işlevselliğe sahip olmalı ve temel sınıflar, özelleştirmenin oluşturulabileceği zengin bir çerçeve sağlamalıdır. Bunu bir arayüze koyun ve uygulamayı çok zorlaştırdınız.

Kimse arayüzlerinizin devasa olması gerektiğini söylemedi. Daha küçük, çoklu arayüzler ve daha zengin taban sınıfları harika bir API yapar.
Kilhoffer

3
Sadece ben miyim, yoksa “ortak işçi” sınıflarının çoğu ortak bir uygulamayı mı paylaşıyor? Bu bağlamda, arayüzleri tercih etme genel kuralınıza aykırıdır. Genellemenizi 2 genelleme şeklinde yeniden ifade ederim: Mantık içermeyen ya da çok az mantık içeren bu ortak sınıflar bir arabirim uygulamalıdır. "İyi" bir miktar mantık içeren bu yaygın sınıflar temel sınıftan türetilmelidir (büyük olasılıkla işlevselliği paylaşacaklardır).
goku_da_master

@Kilhoffer "Arayüzler, sınırların ötesinde türlerin maksimum esneklik ve taşınabilirliğine izin verir" lütfen bu beyanı hazırlayın.
JAVA

49

Bu oldukça .NET'e özgüdür, ancak Çerçeve Tasarım Yönergeleri kitabı genel sınıflarda gelişen bir çerçevede daha fazla esneklik sağladığını savunmaktadır. Bir arabirim gönderildikten sonra, o arabirimi kullanan kodu bozmadan değiştiremezsiniz. Ancak bir sınıfla, onu değiştirebilir ve ona bağlanan kodu bozamazsınız. Yeni işlevsellik eklemeyi de içeren doğru değişiklikleri yaptığınız sürece, kodunuzu genişletebilir ve geliştirebilirsiniz.

Krzysztof Cwalina diyor ki sayfa 81:

.NET Framework'ün üç sürümü boyunca, bu kılavuzdan ekibimizdeki birkaç geliştiriciyle konuştum. Başlangıçta kurallara katılmayanlar da dahil olmak üzere birçoğu, bir API olarak bir arayüz olarak gönderdiğinden pişman olduklarını söyledi. Birinin bir sınıf gönderdiğinden pişman olduğu bir vakayı bile duymadım.

Kesinlikle arayüzler için bir yer olduğu söyleniyor. Genel bir kılavuz olarak, arabirimi uygulamanın bir yolu olarak başka hiçbir şey için değilse, her zaman bir arabirimin soyut temel sınıf uygulamasını sağlar. En iyi durumda, bu temel sınıf çok fazla iş tasarrufu sağlayacaktır.


19

Juan

Arayüzleri bir sınıfı karakterize etmenin bir yolu olarak düşünmeyi seviyorum. YorkshireTerrier gibi belirli bir köpek ırkı sınıfı, ana köpek sınıfının bir inişi olabilir, ancak aynı zamanda IFurry, IStubby ve IYippieDog'u da uygular. Böylece sınıf sınıfın ne olduğunu tanımlar ancak arayüz bize bu konuda bir şeyler anlatır.

Bunun avantajı, örneğin, tüm IYippieDog'ları toplamam ve onları Okyanus koleksiyonuma atmamı sağlıyor. Şimdi belirli bir nesne kümesine ulaşabilir ve sınıfı çok yakından incelemeden baktığım kriterlere uyan nesneleri bulabilirim.

Arayüzlerin gerçekten bir sınıfın genel davranışının bir alt kümesini tanımlaması gerektiğini düşünüyorum. Uygulayan tüm sınıflar için tüm genel davranışları tanımlarsa, genellikle var olması gerekmez. Bana yararlı bir şey söylemiyorlar.

Bu düşünce her sınıfın bir arayüze sahip olması ve arayüze kod yazmanız gerektiği fikrine ters düşmektedir. Bu iyi, ama derslere çok sayıda birebir arayüz oluşturuyorsunuz ve bu durum kafa karıştırıcı oluyor. Fikir yapmamın bir maliyeti olmadığını anlıyorum ve şimdi işleri kolayca değiştirip değiştirebilirsiniz. Ancak bunu nadiren yaptığımı fark ettim. Çoğu zaman sadece mevcut sınıfı değiştiriyorum ve bu sınıfın genel arabiriminin değişmesi gerekiyorsa, her zaman yaptığım aynı sorunları yaşıyorum, ancak şimdi iki yerde değiştirmek zorundayım.

Eğer benim gibi düşünüyorsan Cat ve Dog'un IPettable olduğunu kesinlikle söyleyebilirsin. Her ikisiyle de eşleşen bir karakterizasyon.

Bunun bir diğer parçası da aynı temel sınıfa mı sahip olmalıdır? Soru şu ki, aynı şey olarak geniş çapta muamele görmeleri gerekiyor mu? Elbette ikisi de Hayvan, ama onları birlikte nasıl kullanacağımıza uyuyor.

Diyelim ki tüm Hayvan sınıflarını toplamak ve bunları Ark konteynerinize koymak istiyorum.

Yoksa Memeli olmaları mı gerekiyor? Belki bir çeşit çapraz hayvan sağım fabrikasına ihtiyacımız var?

Birbirleriyle bağlantılı olmaları bile gerekiyor mu? İkisinin de IPettable olduğunu bilmek yeterli mi?

Gerçekten sadece bir sınıfa ihtiyacım olduğunda bütün bir sınıf hiyerarşisi elde etme arzusunu sık sık hissediyorum. Bunu bir gün beklentimde yapabilirim, buna ihtiyacım olabilir ve genellikle asla yapmam. Yaptığımda bile, genellikle düzeltmek için çok şey yapmam gerektiğini buluyorum. Çünkü yarattığım ilk sınıf Köpek değil, o kadar şanslı değilim, bunun yerine Platypus. Şimdi benim tüm sınıf hiyerarşisi tuhaf durumda dayanmaktadır ve boşa kod çok şey var.

Bir noktada, tüm Kedilerin IPettable olmadığını (tüysüz gibi) bulabilirsiniz. Artık bu Arabirimi, uyan tüm türev sınıflarına taşıyabilirsiniz. Aniden bir Kedinin artık PettableBase'den türetilmediğinden çok daha az kırılma olduğunu göreceksiniz.


18

İşte arayüz ve temel sınıfın temel ve basit tanımı:

  • Temel sınıf = nesne mirası.
  • Arayüz = fonksiyonel kalıtım.

şerefe


12

Mümkün olduğunca kalıtım yerine kompozisyon kullanmanızı öneririm. Arabirimleri kullanın ancak temel uygulama için üye nesnelerini kullanın. Bu şekilde, nesnelerinizi belirli bir şekilde davranacak şekilde inşa eden bir fabrika tanımlayabilirsiniz. Davranışı değiştirmek isterseniz, farklı alt nesne türleri oluşturan yeni bir fabrika yöntemi (veya soyut fabrika) yaparsınız.

Bazı durumlarda, değiştirilebilir nesnelerin tümü yardımcı nesnelerde tanımlanmışsa, birincil nesnelerinizin hiç bir arabirime ihtiyacı olmadığını görebilirsiniz.

IPet veya PetBase yerine, IFurBehavior parametresine sahip bir Pet ile sonuçlanabilir. IFurBehavior parametresi PetFactory'nin CreateDog () yöntemi ile ayarlanır. Shed () yöntemi için çağrılan bu parametredir.

Bunu yaparsanız, kodunuzun çok daha esnek olduğunu görürsünüz ve basit nesnelerinizin çoğu, sistem genelinde çok temel davranışlarla ilgilenir.

Bu kalıbı çoklu kalıtım dillerinde bile öneririm.


12

Bu Java World makalesinde iyi açıklanmıştır .

Şahsen, arayüzleri tanımlamak için arayüzler kullanma eğilimindeyim - yani sistem tasarımının bir şeye nasıl erişilmesi gerektiğini belirten kısımları.

Bir veya daha fazla arabirim uygulayan bir sınıfa sahip olmam nadir değildir.

Soyut dersler başka bir şey için temel olarak kullanıyorum.

Aşağıda, yukarıda bahsedilen JavaWorld.com makalesinden bir alıntı, yazar Tony Sintes, 04/20/01


Arayüz ile soyut sınıf karşılaştırması

Arayüzler ve soyut sınıflar seçmek bir / veya önerme değildir. Tasarımınızı değiştirmeniz gerekiyorsa, bir arayüz yapın. Ancak, bazı varsayılan davranışlar sağlayan soyut sınıflarınız olabilir. Soyut sınıflar, uygulama çerçeveleri içinde mükemmel adaylardır.

Soyut sınıflar bazı davranışları tanımlamanızı sağlar; alt sınıflarınızı başkalarını sağlamaya zorlarlar. Örneğin, bir uygulama çerçeveniz varsa, soyut bir sınıf olay ve ileti işleme gibi varsayılan hizmetleri sağlayabilir. Bu hizmetler uygulamanızın uygulama çerçevenize bağlanmasına izin verir. Ancak, yalnızca uygulamanızın gerçekleştirebileceği uygulamaya özgü bazı işlevler vardır. Bu tür işlevler, genellikle uygulamaya bağlı olan başlatma ve kapatma görevlerini içerebilir. Dolayısıyla, bu davranışın kendisini tanımlamak yerine, soyut temel sınıf soyut kapatma ve başlatma yöntemlerini bildirebilir. Temel sınıf, bu yöntemlere ihtiyaç duyduğunu bilir, ancak soyut bir sınıf, sınıfınızın bu eylemleri nasıl gerçekleştireceğini bilmediğini itiraf etmesini sağlar; sadece eylemleri başlatması gerektiğini bilir. Başlama zamanı geldiğinde, soyut sınıf başlatma yöntemini çağırabilir. Temel sınıf bu yöntemi çağırdığında, Java alt sınıf tarafından tanımlanan yöntemi çağırır.

Birçok geliştirici, soyut bir yöntemi tanımlayan bir sınıfın da bu yöntemi çağırabileceğini unutur. Soyut sınıflar, planlanmış kalıtım hiyerarşileri oluşturmanın mükemmel bir yoludur. Ayrıca sınıf hiyerarşilerinde yapraksız sınıflar için de iyi bir seçimdir.

Sınıf ve arayüz

Bazıları tüm sınıfları arabirimler açısından tanımlamanız gerektiğini söylüyor, ancak bence tavsiye biraz aşırı görünüyor. Tasarımımda bir şeyin sık sık değişeceğini gördüğümde arayüzler kullanıyorum.

Örneğin, Strateji deseni, yeni algoritmaları ve işlemleri, bunları kullanan nesneleri değiştirmeden programınıza değiştirmenize olanak tanır. Bir medya oynatıcı CD, MP3 ve wav dosyalarının nasıl çalınacağını bilebilir. Tabii ki, bu oynatma algoritmalarını oynatıcıya sabit olarak kodlamak istemezsiniz; bu AVI gibi yeni bir biçim eklemeyi zorlaştıracaktır. Ayrıca, kodunuz yararsız vaka ifadeleri ile çevrilecektir. Yaralanmaya hakaret eklemek için, her yeni algoritma eklediğinizde bu vaka bildirimlerini güncellemeniz gerekir. Sonuçta, bu programlamak için çok nesne yönelimli bir yol değildir.

Strateji kalıbıyla, algoritmayı bir nesnenin arkasına kapsülleyebilirsiniz. Bunu yaparsanız, istediğiniz zaman yeni medya eklentileri sağlayabilirsiniz. Eklenti sınıfına MediaStrategy diyelim. Bu nesnenin bir yöntemi vardır: playStream (Stream s). Yeni bir algoritma eklemek için algoritma sınıfımızı genişletiyoruz. Şimdi program yeni medya türüyle karşılaştığında, akışın oynatılmasını medya stratejimize devretmektedir. Tabii ki, ihtiyacınız olacak algoritma stratejilerini doğru bir şekilde başlatmak için bazı tesisatlara ihtiyacınız olacak.

Bu bir arayüz kullanmak için mükemmel bir yerdir. Tasarımda değişecek bir yeri açıkça belirten Strateji modelini kullandık. Dolayısıyla, stratejiyi bir arayüz olarak tanımlamalısınız. Bir nesnenin belirli bir türe sahip olmasını istediğinizde genellikle kalıtım yerine arabirimleri tercih etmelisiniz; bu durumda, MediaStrategy. Tip kimliği için mirasa güvenmek tehlikelidir; sizi belirli bir miras hiyerarşisine kilitler. Java birden fazla kalıtıma izin vermez, bu nedenle size yararlı bir uygulama veya daha fazla tür kimliği veren bir şeyi genişletemezsiniz.


2
+1. "Tip kimliği için mirasa güvenmek tehlikelidir; sizi belirli bir miras hiyerarşisine kilitler." Bu cümle arayüzleri tercih etme nedenlerimi mükemmel bir şekilde anlatıyor.
Mühendis

Ve bunun ötesinde, genişletmek yerine, arayüzünüzün her yönteminin arkasındaki uygulamayı oluşturun.
Mühendis

10

Ayrıca , ihtiyacınız olan tek davranışın bir hayvan için genel bir isim ve tür olduğu bir uygulama tasarlıyorsanız, OO'da ( blog'a bakın ) süpürülmemeyi ve her zaman gerekli davranışa göre nesneleri modellememeyi unutmayın. Dünyadaki her olası hayvan için milyonlarca sınıf yerine, adı olan bir sınıf Hayvan.


10

Kabaca bir başparmak kuralım var

İşlevsellik: tüm parçalarda farklı olması muhtemel: Arayüz.

Veri ve işlevsellik, parçalar çoğunlukla aynı, parçalar farklı olacaktır: soyut sınıf.

Veri ve işlevsellik, aslında sadece küçük değişikliklerle genişletilirse çalışır: sıradan (somut) sınıf

Veri ve işlevsellik, planlanan değişiklik yok: son değiştiricili sıradan (somut) sınıf.

Veriler ve belki işlevsellik: salt okunur: enum üyeleri.

Bu çok kaba ve hazırdır ve kesin olarak tanımlanmamıştır, ancak her şeyin salt okunur bir dosya gibi sabitlendiği numaralandırmalarla değiştirilmesinin amaçlandığı arabirimlerden bir spektrum vardır.


7

Arayüzler küçük olmalıdır. Gerçekten küçük. Nesnelerinizi gerçekten parçalara ayırıyorsanız, arayüzleriniz muhtemelen sadece birkaç çok spesifik yöntem ve özellik içerecektir.

Soyut sınıflar kısayollardır. PetBase'in tüm türevlerinin bir kez kodlayabileceğiniz ve yapabileceğiniz şeyler var mı? Cevabınız evet ise, soyut bir ders zamanı.

Soyut sınıflar da sınırlayıcıdır. Size alt nesneler üretmek için mükemmel bir kısayol sağlarken, verilen herhangi bir nesne yalnızca bir soyut sınıf uygulayabilir. Çoğu zaman, bunu Abstract sınıflarının bir sınırlaması olarak görüyorum ve bu yüzden çok fazla arayüz kullanıyorum.

Soyut sınıflar çeşitli arayüzler içerebilir. PetBase özet sınıfınız IPet (evcil hayvanların sahiplerine sahiptir) ve IDigestion (evcil hayvan yiyor ya da en azından öyle olmalıdır) uygulayabilir. Bununla birlikte, PetBase muhtemelen IMammal uygulamamaktadır, çünkü tüm evcil hayvanlar memeli değildir ve tüm memeliler evcil hayvan değildir. PetBase'yi genişleten bir MammalPetBase ekleyebilir ve IMammal ekleyebilirsiniz. FishBase, PetBase'e sahip olabilir ve IFish ekleyebilir. IFish, arayüzler olarak ISwim ve IUnderwaterBreather olurdu.

Evet, örneğim basit örnek için aşırı derecede karmaşık, ancak bu, arayüzlerin ve soyut sınıfların birlikte nasıl çalıştığıyla ilgili harika şeyin bir parçası.


7

Kaynak : http://jasonroell.com/2014/12/09/interfaces-vs-abstract-classes-what-should-you-use/

C #, son 14 yılda olgunlaşan ve gelişen harika bir dildir. Bu, geliştiriciler için harika bir şey çünkü olgun bir dil, elimizde olan çok sayıda dil özelliği sağlıyor.

Bununla birlikte, çok fazla güçle çok sorumluluk olur. Bu özelliklerden bazıları yanlış kullanılabilir veya bazen bir özelliği neden diğerine tercih edeceğinizi anlamak zordur. Yıllar boyunca, birçok geliştiricinin mücadele ettiği bir özellik, ne zaman bir arayüz kullanmayı veya soyut bir sınıf kullanmayı seçmektir. Her ikisinin de avantajları ve dezavantajları ve her birini kullanmak için doğru zaman ve yer var. Ama nasıl karar veririz ???

Her ikisi de türler arasında ortak işlevselliğin yeniden kullanılmasını sağlar. En belirgin fark arayüzlerin işlevsellikleri için herhangi bir uygulama sağlamaması, soyut sınıflar ise bazı “temel” veya “varsayılan” davranışları uygulamanıza ve daha sonra bu varsayılan davranışı gerekirse türetilmiş sınıflarla “geçersiz kılma” özelliğine sahip olmasıdır. .

Bu iyi ve iyidir ve kodun büyük ölçüde yeniden kullanılmasını sağlar ve DRY (Kendinizi Tekrar Etmeyin) yazılım geliştirme prensibine bağlıdır. Soyut sınıflar, bir “is” ilişkiniz olduğunda kullanmak için harikadır.

Örneğin: Bir altın av köpeği “tür” bir köpek türüdür. Bir fino köpeği de öyle. Her ikisi de, tüm köpekler gibi havlayabilir. Bununla birlikte, kaniş parkının “varsayılan” köpek kabuğundan önemli ölçüde farklı olduğunu belirtmek isteyebilirsiniz. Bu nedenle, aşağıdaki gibi bir şey uygulamanız mantıklı olabilir:

public abstract class Dog
{
      public virtual void Bark()
      {
        Console.WriteLine("Base Class implementation of Bark");
      }
}

public class GoldenRetriever : Dog
{
   // the Bark method is inherited from the Dog class
}

public class Poodle : Dog
{
  // here we are overriding the base functionality of Bark with our new implementation
  // specific to the Poodle class
  public override void Bark()
  {
     Console.WriteLine("Poodle's implementation of Bark");
  }
}

// Add a list of dogs to a collection and call the bark method.

void Main()
{
    var poodle = new Poodle();
    var goldenRetriever = new GoldenRetriever();

    var dogs = new List<Dog>();
    dogs.Add(poodle);
    dogs.Add(goldenRetriever);

    foreach (var dog in dogs)
    {
       dog.Bark();
    }
}

// Output will be:
// Poodle's implementation of Bark
// Base Class implementation of Bark

// 

Gördüğünüz gibi, bu, kodunuzu KURU tutmanın ve türlerden herhangi birinin özel bir vaka uygulaması yerine varsayılan Bark'a güvenebilmesi durumunda temel sınıf uygulamasının çağrılmasına izin vermenin harika bir yolu olacaktır. GoldenRetriever, Boxer, Lab gibi sınıfların tümü, yalnızca Köpek soyut sınıfını uyguladıkları için “varsayılan” (bas sınıfı) Bark'ı ücretsiz olarak devralabilir.

Ama eminim zaten biliyordun.

Buradasınız, çünkü soyut bir sınıf üzerinden neden bir arayüz seçmek isteyebileceğinizi veya tam tersini anlamak isteyebilirsiniz. Soyut bir sınıf üzerinde bir arabirim seçmek isteyebileceğiniz nedenlerden biri, varsayılan bir uygulamaya sahip olmamanız veya bunu önlemek istemenizdir. Bunun nedeni genellikle arabirimi uygulayan türlerin “ilişkisiz” bir ilişkidir. Aslında, her türün bir şey yapabilmesi ya da bir şey yapabilmesi için “yapabilmesi” ya da “bereketine” sahip olması dışında hiçbir şekilde ilişkili olmaları gerekmez.

Şimdi bu ne anlama geliyor? Mesela: Bir insan ördek değil… ve ördek insan değil. Çok belirgin. Ancak, hem bir ördek hem de bir insan yüzme yeteneğine sahiptir (insan yüzme derslerini 1. sınıfta geçtiği göz önüne alındığında :)). Ayrıca, bir ördek bir insan olmadığından ya da tam tersi olduğundan, bu bir “bir” ilişkidir, bunun yerine “mümkün” bir ilişkidir ve bunu göstermek için bir arayüz kullanabiliriz:

// Create ISwimable interface
public interface ISwimable
{
      public void Swim();
}

// Have Human implement ISwimable Interface
public class Human : ISwimable

     public void Swim()
     {
        //Human's implementation of Swim
        Console.WriteLine("I'm a human swimming!");
     }

// Have Duck implement ISwimable interface
public class Duck: ISwimable
{
     public void Swim()
     {
          // Duck's implementation of Swim
          Console.WriteLine("Quack! Quack! I'm a Duck swimming!")
     }
}

//Now they can both be used in places where you just need an object that has the ability "to swim"

public void ShowHowYouSwim(ISwimable somethingThatCanSwim)
{
     somethingThatCanSwim.Swim();
}

public void Main()
{
      var human = new Human();
      var duck = new Duck();

      var listOfThingsThatCanSwim = new List<ISwimable>();

      listOfThingsThatCanSwim.Add(duck);
      listOfThingsThatCanSwim.Add(human);

      foreach (var something in listOfThingsThatCanSwim)
      {
           ShowHowYouSwim(something);
      }
}

 // So at runtime the correct implementation of something.Swim() will be called
 // Output:
 // Quack! Quack! I'm a Duck swimming!
 // I'm a human swimming!

Yukarıdaki kod gibi arayüzler kullanmak, bir nesneyi bir şeyleri “yapabilen” bir yönteme geçirmenize izin verecektir. Kod nasıl yaptığını umursamıyor… Tek bildiği şey, o nesne üzerindeki Swim yöntemini çağırabilmesidir ve bu nesne, türüne göre çalışma zamanında hangi davranışın alınacağını bilecektir.

Bir kez daha, bu kodunuzun KURU kalmasına yardımcı olur, böylece nesneyi aynı çekirdek işlevini (ShowHowHumanSwims (insan), ShowHowDuckSwims (ördek) vb.) Oluşturmak için çağıran birden çok yöntem yazmak zorunda kalmazsınız.

Burada bir arabirim kullanmak, çağrı yöntemlerinin hangi türün ne olduğu veya davranışın nasıl uygulandığı konusunda endişelenmesine gerek yoktur. Sadece arayüz verildiğinde, her nesnenin Swim yöntemini uygulamış olması gerektiğini bilir, böylece kendi kodunda çağırmak ve Swim yönteminin davranışının kendi sınıfında işlenmesine izin vermek güvenlidir.

Özet:

Bu yüzden temel kuralım, bir sınıf hiyerarşisi veya / için “varsayılan” bir işlevsellik uygulamak istediğinizde soyut bir sınıf kullanmaktır ve / ve birlikte çalıştığınız sınıf veya türler bir “ilişkidir” (ör. Kaniş “ "Köpek türü).

Öte yandan, “bir” ilişkiniz olmadığında ancak bir şey yapma ya da bir şeye sahip olma “yeteneğini” paylaşan türlere sahip olduğunuzda bir arayüz kullanın (ör. Ördek “insan değildir”, ancak ördek ve insan payı "Yüzme yeteneği".

Soyut sınıflar ve arayüzler arasında dikkat edilmesi gereken bir diğer fark, bir sınıfın birden fazla arayüze uygulayabilmesidir, ancak bir sınıf yalnızca BİR soyut sınıftan (veya bu konudaki herhangi bir sınıftan) miras alabilir. Evet, sınıfları iç içe geçirebilir ve kalıtım hiyerarşisine sahip olabilirsiniz (birçok programın sahip olduğu ve sahip olması gereken), ancak türetilmiş bir sınıf tanımında iki sınıfı devralamazsınız (bu kural C # için geçerlidir. Diğer bazı dillerde genellikle bunu yapabilirsiniz sadece bu dillerdeki arayüzlerin eksikliği nedeniyle).

Ayrıca, Arabirim Ayrışma İlkesine (ISS) uymak için arabirimleri kullanırken de unutmayın. ISP, hiçbir müşterinin kullanmadığı yöntemlere bağımlı olmaya zorlanmaması gerektiğini belirtir. Bu nedenle arayüzler belirli görevlere odaklanmalıdır ve genellikle çok küçüktür (örn. IDisposable, IComparable).

Başka bir ipucu, küçük, özlü işlevsellik parçaları geliştiriyorsanız, arabirimler kullanın. Büyük fonksiyonel birimler tasarlıyorsanız, soyut bir sınıf kullanın.

Umarım bu bazı insanlar için işleri temizler!

Ayrıca daha iyi örnekler düşünebilir veya bir şeyi belirtmek isterseniz, lütfen aşağıdaki yorumlarda belirtin!


6

Arabirimler Üzerindeki Taban Sınıflar için durum, Submain .NET Kodlama Yönergeleri'nde iyi açıklanmıştır:

Temel Sınıflar ve Arayüzler Arayüz tipi, potansiyel olarak birçok nesne türü tarafından desteklenen bir değerin kısmi bir tanımıdır. Mümkünse arabirimler yerine temel sınıflar kullanın. Sürüm oluşturma açısından sınıflar arabirimlerden daha esnektir. Bir sınıfla, Sürüm 1.0'ı gönderebilir ve Sürüm 2.0'da sınıfa yeni bir yöntem ekleyebilirsiniz. Yöntem soyut olmadığı sürece mevcut türetilmiş sınıflar değişmeden çalışmaya devam eder.

Arabirimler uygulama devralma özelliğini desteklemediğinden, sınıflar için geçerli olan desen arabirimler için geçerli değildir. Bir arabirime yöntem eklemek, temel sınıfa soyut bir yöntem eklemekle eşdeğerdir; sınıf yeni yöntemi uygulamadığı için arabirimi uygulayan herhangi bir sınıf kırılacaktır. Arayüzler aşağıdaki durumlarda uygundur:

  1. İlişkisiz birkaç sınıf protokolü desteklemek istiyor.
  2. Bu sınıflar zaten temel sınıflara sahiptir (örneğin, bazıları kullanıcı arabirimi (UI) denetimleri ve bazıları XML Web hizmetleridir).
  3. Toplama uygun veya uygulanabilir değildir. Diğer tüm durumlarda, sınıf mirası daha iyi bir modeldir.

Bu cevabın daha fazla dikkat çekmesi gerektiğini hissediyorum. Burada birçok cevabın tahılına aykırıdır. Tamamen katıldığımı söyleyemem, ama burada harika noktalar var.
kayleeFrye_onDeck

5

Önemli bir fark, yalnızca bir temel sınıfı devralabilmenizdir , ancak birçok arabirimi uygulayabilirsiniz . Yalnızca eğer bir temel sınıf kullanmak istiyorum Yani kesinlikle emin Farklı temel sınıf devralır da gerek olmayacak. Ayrıca, arayüzünüzün genişlediğini fark ederseniz, bağımsız işlevselliği tanımlayan birkaç mantıksal parçaya bölmeye başlamalısınız, çünkü sınıfınızın hepsini uygulayamayacağı (veya farklı bir sadece onları gruplandırmak için miras alan arayüz).


4

Nesneye yönelik programlamayı ilk öğrenmeye başladığımda, ortak davranışı paylaşmak için miras kullanmada kolay ve muhtemelen yaygın bir hata yaptım - bu davranış nesnenin doğası için gerekli olmasa bile.

Bu özel soruda çok kullanılan bir örneği daha da geliştirmek için , petable olan çok şey var - kız arkadaşları, arabalar, bulanık battaniyeler ... - bu yüzden bu ortak davranışı sağlayan bir Petable sınıfına ve çeşitli sınıfların miras almasına sahip olabilirdim ondan.

Bununla birlikte, petable olmak bu nesnelerin hiçbirinin doğasının bir parçası değildir. Daha önemli kavramlar büyük ölçüde vardır vardır onların doğaya gerekli - kız arkadaşı bir kişidir, araba bir kara aracıdır, kedi bir memelidir ...

Davranışlar önce arabirimlere (sınıfın varsayılan arabirimi dahil) atanmalı ve yalnızca (a) daha büyük bir sınıfın alt kümeleri olan büyük bir sınıf grubunda ortak olan - aynı anlamda "kedi" ve "kişi", "memeli" nin alt kümeleridir.

Yakalama, nesne odaklı tasarımı ilk başta benden daha iyi anladıktan sonra, normalde bunu düşünmeden bile otomatik olarak yapacaksınız. Dolayısıyla, "soyut bir sınıfa değil, bir arayüze kodlama" ifadesinin çıplak gerçeği o kadar belirginleşir ki, herkesin bunu söylemeye zahmet edeceğine inanıyor ve içine başka anlamlar okumaya çalışıyorsunuz.

Ekleyeceğim başka bir şey, eğer bir sınıf tamamen soyutsa - soyut olmayan, kalıtsal olmayan üyelere veya çocuğa, ebeveyne veya müşteriye maruz kalan yöntemlere sahip değilse - o zaman neden bir sınıftır? Bazı durumlarda bir arabirim ve diğer durumlarda Null ile değiştirilebilir.


Tamamen soyut bir sınıf, yöntemler için varsayılan davranışlar sağlayabilir. Bu, somut sınıflarınız tekrar tekrar uygulamak için gereksiz genel yöntemleri paylaştığında yararlıdır.
Adam Hughes

4

Soyut sınıflara göre arayüzleri tercih etme

Gerekçe, dikkate alınması gereken ana noktalar [burada daha önce bahsedilen iki]:

  • Bir sınıf birden çok arabirim uygulayabildiğinden arabirimler daha esnektir. Java'da birden fazla devralma olmadığından, soyut sınıfların kullanılması kullanıcılarınızın başka bir sınıf hiyerarşisi kullanmasını önler. Genel olarak, varsayılan uygulamalar veya durum olmadığında arabirimleri tercih edin. Java koleksiyonları bunun güzel örneklerini sunar (Harita, Küme, vb.).
  • Soyut sınıflar daha iyi ileri uyumluluk sağlama avantajına sahiptir. İstemciler bir arabirimi kullandıktan sonra değiştiremezsiniz; soyut bir sınıf kullanıyorlarsa, varolan kodu bozmadan da davranış ekleyebilirsiniz. Uyumluluk endişe kaynağıysa soyut sınıfları kullanmayı düşünün.
  • Varsayılan uygulamalarınız veya dahili durumunuz olsa bile, bir arabirim ve bunun soyut bir uygulamasını sunmayı düşünün . Bu, müşterilere yardımcı olacaktır, ancak istenirse daha fazla özgürlük de sağlayacaktır [1].
    Tabii ki, konu başka bir yerde tartışılmıştır [2,3].

[1] Elbette daha fazla kod ekliyor, ancak eğer kısalık birincil kaygınızsa, muhtemelen ilk etapta Java'dan kaçınmalısınız!

[2] Joshua Bloch, Etkili Java, 16-18. Maddeler.

[3] http://www.codeproject.com/KB/ar ...


3

Ortak uygulama için soyut sınıfların kullanılmasıyla ilgili önceki yorumlar kesinlikle işaretlidir. Daha önce bahsetmediğim bir fayda, arayüzlerin kullanımının, birim test amacıyla sahte nesnelerin uygulanmasını çok daha kolay hale getirmesidir. IPet ve PetBase'i Jason Cohen'in tanımladığı gibi tanımlamak, fiziksel bir veritabanının yükü olmadan (gerçek şeyi test etme zamanı olduğuna karar verene kadar) farklı veri koşullarını kolayca taklit etmenizi sağlar.


3

Bunun ne anlama geldiğini bilmiyorsanız ve bu durumda geçerli olduğunu bilmiyorsanız temel sınıf kullanmayın. Geçerliyse, kullanın, aksi takdirde arayüzler kullanın. Ancak küçük arayüzler hakkındaki cevaba dikkat edin.

Kamu Kalıtım OOD'de aşırı kullanıldı ve çoğu geliştiricinin farkettiğinden veya yaşamak istediğinden çok daha fazlasını ifade ediyor. Liskov Yerine Koyma İlkesine bakın

Kısacası, A "bir" B ise, A, B'den daha fazlasını gerektirmez ve maruz kaldığı her yöntem için B'den daha az vermez.


3

Akılda tutulması gereken bir diğer seçenek de "has-a" ilişkisini kullanmaktır, aka "" veya "kompozisyon" anlamında uygulanır. Bazen bu, şeyleri yapılandırmak için "is-a" kalıtımını kullanmaktan daha temiz ve daha esnek bir yoldur.

Köpek ve Kedinin her ikisinin de bir Evcil Hayvana sahip olduğunu söylemek mantıklı olmayabilir, ancak ortak çoklu miras tuzaklarından kaçınır:

public class Pet
{
    void Bathe();
    void Train(Trick t);
}

public class Dog
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

public class Cat
{
    private Pet pet;

    public void Bathe() { pet.Bathe(); }
    public void Train(Trick t) { pet.Train(t); }
}

Evet, bu örnek, kodların çoğaltılması ve işlerin bu şekilde yapılmasında zerafet eksikliği olduğunu göstermektedir. Ancak, bunun Köpek ve Kedinin Pet sınıfından ayrılmasına yardımcı olduğuna da dikkat edilmelidir (Köpek ve Kedinin özel Pet üyelerine erişimi yoktur) ve Köpek ve Kedinin başka bir şeyden miras kalması için yer bırakır. - Muhtemelen Memeli sınıfı.

Özel erişim gerekmediğinde kompozisyon tercih edilir ve genel Evcil hayvan referansları / işaretçileri kullanarak Köpek ve Kedi'ye başvurmanız gerekmez. Arayüzler size genel referans kabiliyetini verir ve kodunuzun ayrıntı düzeyini azaltmaya yardımcı olabilir, ancak kötü organize olduklarında şeyleri de şaşırtabilirler. Kalıtım, özel üye erişimine ihtiyacınız olduğunda yararlıdır ve bunu kullanırken, Köpek ve Kedi sınıflarınızı Pet sınıfınıza yüksek oranda bağlamayı taahhüt edersiniz, bu da dik bir maliyettir.

Kalıtım, kompozisyon ve arayüzler arasında her zaman doğru olan tek bir yol yoktur ve her üç seçeneğin de uyum içinde nasıl kullanılabileceğini düşünmeye yardımcı olur. Üçünden kalıtım tipik olarak en az sıklıkla kullanılması gereken seçenektir.


3

Kavramsal olarak, bir arabirim , bir nesnenin sağlayacağı bir dizi yöntemi resmi ve yarı biçimsel olarak tanımlamak için kullanılır. Resmi olarak bir dizi yöntem adı ve imzası anlamına gelir ve yarı resmi olarak bu yöntemlerle ilişkili insan tarafından okunabilir belgeler anlamına gelir.

Arayüzler yalnızca bir API'nin tanımlarıdır (sonuçta, API uygulama programlama arayüzü anlamına gelir ), herhangi bir uygulama içeremezler ve bir arayüz kullanmak veya çalıştırmak mümkün değildir. Sadece bir nesneyle nasıl etkileşimde bulunmanız gerektiğine dair sözleşmeyi açıkça ortaya koyarlar.

Sınıflar bir uygulama sağlar ve sıfır, bir veya daha fazla Arayüz uyguladıklarını beyan edebilirler. Bir sınıfın miras alınması amaçlanıyorsa, kural sınıf isminin önüne "Base" getirecektir.

Temel sınıf ve soyut temel sınıflar (ABC) arasında bir ayrım vardır . ABC'ler arayüzü ve uygulamayı bir araya getirir. Bilgisayar programlama dışında özet "özet", yani "özet == arayüz" anlamına gelir. Bir soyut temel sınıftır sonra bir ara-yüz olarak miras amaçlanan bir boş kısmi veya tam uygulama, hem tarif edebilir.

Ne zaman kullanılacağı hakkında Görüşler arayüzleri karşı soyut temel sınıflar sadece karşı sınıfları Eğer gelişmekte ne hem dayanarak çılgınca değişir gidiyor ve siz de gelişmekte olan dil. Arabirimler genellikle Java veya C # gibi statik olarak yazılan dillerle sadece ilişkilidir, ancak dinamik olarak yazılan diller de arayüzlere ve soyut temel sınıflara sahip olabilir . Örneğin Python'da, bir arabirimi uyguladığını bildiren bir Sınıf ile bir sınıfın örneği olan ve bu arabirimi sağladığı söylenen bir nesne arasında ayrım yapılır.. Dinamik bir dilde, her ikisi de aynı sınıfın örneği olan iki nesnenin tamamen farklı arabirimler sağladıklarını bildirmeleri mümkündür . Python'da bu yalnızca nesne nitelikleri için mümkündür, ancak yöntemler bir sınıfın tüm nesneleri arasında paylaşılan durumdur . Ancak, Ruby'de nesneler örnek başına yöntemlere sahip olabilir, bu nedenle aynı sınıftaki iki nesne arasındaki arabirimin programcının istediği kadar değişebilir (ancak, Ruby'nin Arabirimleri bildirmenin açık bir yolu yoktur).

Dinamik dillerde, bir nesneye arayüz genellikle bir nesneyi iç içe geçirerek ve ona hangi yöntemleri sağladığını ( sıçramadan önce bak ) sorarak veya tercihen bir nesne üzerinde istenen arabirimi kullanmaya çalışarak ve nesne varsa istisnaları yakalayarak dolaylı olarak varsayılır. bu arayüzü sağlamaz ( affetmeyi istemekten izin almaktan daha kolaydır ). Bu, iki arabirimin aynı yöntem adına sahip olduğu ancak anlamsal olarak farklı olduğu "yanlış pozitiflere" yol açabilir . Ancak, değiş tokuş kodunuzun daha esnek olmasıdır, çünkü kodunuzun olası tüm kullanımlarını önceden tahmin etmek için fazla ön belirtmeniz gerekmez.


2

Gereksinimlerinize bağlıdır. IPet yeterince basitse, bunu uygulamayı tercih ederim. Aksi takdirde, PetBase çoğaltmak istemediğiniz bir ton işlevsellik uygularsa, o zaman buna sahip olun.

Bir taban sınıfın uygulanmasının dezavantajı, mevcut yöntemlere override(veya new) gereksinimdir . Bu onları sanal yöntemler yapar, bu da nesne örneğini nasıl kullandığınız konusunda dikkatli olmanız gerektiği anlamına gelir.

Son olarak, .NET'in tek mirası beni öldürüyor. Naif bir örnek: Bir kullanıcı denetimi yaptığınızı ve bu nedenle miras aldığınızı varsayalım UserControl. Ama şimdi miras almaktan alıkonuldun PetBase. Bu sizi PetBasesınıf üyesi yapmak gibi yeniden düzenlemeye zorlar .


2

Genelde ikisine de ihtiyacım olana kadar uygulama yapmıyorum. Arayüzleri soyut sınıflara tercih ediyorum çünkü bu biraz daha esneklik sağlıyor. Bazı kalıtsal sınıflarda ortak davranışlar varsa bunu yukarı taşıyıp soyut bir temel sınıf oluştururum. Her ikisi için de ihtiyacı görmüyorum, çünkü aslında aynı amaca hizmet ediyorlar ve her ikisine de sahip olmak çözümün aşırı mühendisliği yapılmış bir kötü kod kokusu (imho).


2

C # ile ilgili olarak, bazı duyularda arayüzler ve soyut sınıflar birbiriyle değiştirilebilir. Ancak, farklar şunlardır: i) arayüzler kod uygulayamaz; ii) bu nedenle, arabirimler yığının alt sınıfa daha fazla çağrılamaz; ve iii) bir sınıfta yalnızca soyut sınıf miras alınabilirken, bir sınıfa birden fazla arayüz uygulanabilir.


2

Def ile, arayüz diğer kodlarla iletişim kurmak için bir katman sağlar. Bir sınıfın tüm ortak özellikleri ve yöntemleri varsayılan olarak örtük arabirim uygulamaktır. Ayrıca bir arayüzü bir rol olarak tanımlayabiliriz, herhangi bir sınıfın bu rolü oynaması gerektiğinde, onu uygulayan sınıfa bağlı olarak farklı uygulama biçimleri vererek onu uygulamak zorundadır. Dolayısıyla arayüz hakkında konuşurken, polimorfizmden bahsediyorsunuz ve temel sınıftan bahsederken mirastan bahsediyorsunuz. Hata!


2

Arayüz> Özet> Beton kalıbının aşağıdaki kullanım durumunda çalıştığını gördüm:

1.  You have a general interface (eg IPet)
2.  You have a implementation that is less general (eg Mammal)
3.  You have many concrete members (eg Cat, Dog, Ape)

Abstract sınıfı, somut sınıfların varsayılan paylaşılan özelliklerini tanımlar, ancak arabirimi zorlar. Örneğin:

public interface IPet{

    public boolean hasHair();

    public boolean walksUprights();

    public boolean hasNipples();
}

Şimdi, tüm memelilerin saçları ve meme uçları (AFAIK, ben bir zoolog değilim) olduğundan, bunu soyut temel sınıfa dönüştürebiliriz

public abstract class Mammal() implements IPet{

     @override
     public walksUpright(){
         throw new NotSupportedException("Walks Upright not implemented");
     }

     @override
     public hasNipples(){return true}

     @override
     public hasHair(){return true}

Ve sonra somut sınıflar sadece dik yürüdüklerini tanımlarlar.

public class Ape extends Mammal(){

    @override
    public walksUpright(return true)
}

public class Catextends Mammal(){

    @override
    public walksUpright(return false)
}

Bu tasarım çok sayıda beton sınıfı olduğunda güzeldir ve sadece bir arayüze programlamak için kazan plakasını korumak istemezsiniz. Arabirime yeni yöntemler eklenirse, sonuçta elde edilen sınıfların tümünü kırar, böylece yine de arabirim yaklaşımının avantajlarını elde edersiniz.

Bu durumda, soyut da somut olabilir; bununla birlikte, soyut adlandırma, bu modelin kullanıldığını vurgulamaya yardımcı olur.


1

Bir temel sınıfın mirasçısı "is" ilişkisine sahip olmalıdır. Arayüz bir "uygular" ilişkisini temsil eder. Bu nedenle, yalnızca mirasçılarınız ilişkiyi koruyacaksa bir temel sınıf kullanın.


1

İlişkisiz sınıfların ACROSS aileleriyle sözleşme yapmak için Arabirimler kullanın. Örneğin, koleksiyonları temsil eden, ancak kökten farklı veriler içeren sınıflar için ortak erişim yöntemleriniz olabilir, yani bir sınıf sorgudan bir sonuç kümesini temsil ederken, diğeri galerideki görüntüleri temsil edebilir. Ayrıca, birden çok arabirim uygulayarak sınıfın yeteneklerini harmanlamanıza (ve göstermenize) olanak tanır.

Sınıflar ortak bir ilişkiye sahip olduklarında ve bu nedenle similair yapısal ve davranışsal bir imzası olduğunda Kalıtım kullanın, yani Araba, Motosiklet, Kamyon ve SUV, bir dizi tekerlek, en yüksek hız içerebilen her türlü yol aracıdır

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.