Kompozisyonu tercih etmek sadece polimorfizm ile ilgili değildir. Her ne kadar bunun bir parçası olsa da ve haklı olarak (en azından nominal olarak yazılmış dillerde) insanların gerçekten kastettiği şeylerin "bir kompozisyon ve arayüz uygulamasının bir kombinasyonunu tercih etmesini" haklısın. Ancak, kompozisyonu tercih etme nedenleri (çoğu durumda) oldukça derindir.
Polimorfizm , birden fazla şekilde davranan bir şey hakkındadır. Dolayısıyla, jenerikler / şablonlar, tek bir kod parçasının davranışını türlere göre değiştirmesine izin verdikleri ölçüde "polimorfik" bir özelliktir. Aslında, bu tür polimorfizm gerçekten en iyi şekilde davranılır ve varyasyon bir parametre ile tanımlandığı için genellikle parametrik polimorfizm olarak adlandırılır .
Birçok dil, "aşırı yükleme" olarak adlandırılan bir polimorfizm veya aynı addaki çoklu işlemlerin geçici olarak tanımlandığı ve dilin dil tarafından seçildiği (belki de en özel olan) bir polimorfizm biçimi sağlar. Bu, en az iyi davranış gösteren polimorfizmdir, çünkü gelişmiş kongre hariç iki prosedürün davranışını hiçbir şey bağlamadı.
Üçüncü tür polimorfizm, alt tip polimorfizmdir . Burada, belirli bir tipte tanımlanmış bir prosedür, aynı zamanda, bu tipteki bir "alt tip" ailesinin tümü üzerinde de çalışabilir. Bir arayüz uyguladığınızda veya bir sınıfı genişlettiğinizde, genellikle bir alt tür oluşturma niyetinizi beyan etmiş olursunuz. Gerçek alt türler Liskov'un Değiştirme İlkesi tarafından yönetiliyorBir üst tipteki tüm nesneler hakkında bir şeyler ispat ederseniz, onu bir alt tipteki tüm örnekler hakkında ispatlayabileceğinizi söyler. C ++ ve Java gibi dillerde, insanlar genel olarak güçsüz ve çoğu zaman alt sınıfları için geçerli olabilecek sınıflarla ilgili belgelenmemiş varsayımlara sahip olduklarından, hayat tehlikeli hale geliyor. Yani, kod gerçekten olduğundan daha fazla kanıtlanmış gibi yazılır, bu da dikkatsiz bir şekilde alt yazı yazdığınızda çok sayıda sorun ortaya çıkarır.
Kalıtım aslında polimorfizmden bağımsızdır. Kendisine referansı olan bir "T" olayı göz önüne alındığında, kalıtım, "T" den "S" ye bir referansla değiştirilen "S" den yeni bir şey yarattığınızda meydana gelir. Bu tanım kasıtlı olarak belirsizdir, çünkü kalıtım birçok durumda gerçekleşebilir, ancak en yaygın olanı this
sanal işlevler tarafından işaretlenen this
işaretçiyi işaretçiyle alt tip yerine değiştirme etkisi olan bir nesneyi alt sınıftır.
Kalıtım tüm çok güçlü şeyler gibi tehlikelidir ; kalıtımın tahribata yol açma gücü vardır. Örneğin, bir sınıftan miras alırken bir yöntemi geçersiz kıldığınızı varsayalım: her şey iyi ve iyidir; o sınıfın başka bir yöntemi, özgün sınıfın yazarı tarafından tasarlandığı şekliyle belirli bir şekilde davranmak için miras aldığınız yöntemi kabul edene kadar . Başkaları tarafından çağrılan tüm yöntemleri geçersiz kılmak için tasarlanmadıkça , özel veya sanal olmayan (son) olarak bildirerek kısmen koruyabilirsiniz . Bu olsa bile her zaman yeterince iyi değil. Bazen böyle bir şey görebilirsiniz (sözde Java'da, umarım C ++ ve C # kullanıcılarına okunabilir)
interface UsefulThingsInterface {
void doThings();
void doMoreThings();
}
...
class WayOfDoingUsefulThings implements UsefulThingsInterface{
private foo stuff;
public final int getStuff();
void doThings(){
//modifies stuff, such that ...
...
}
...
void doMoreThings(){
//ignores stuff
...
}
}
bunun çok güzel olduğunu düşünüyorsunuz ve “şeyler” yapmak için kendi yolunuza sahipsiniz, ancak “daha fazla şeyler” yapma yeteneğini kazanmak için miras kullanıyorsunuz,
class MyUsefulThings extends WayOfDoingUsefulThings{
void doThings {
//my way
}
}
Ve her şey iyi ve güzel. WayOfDoingUsefulThings
bir yöntemi değiştirmek, başka hiçbir şeyin anlamını değiştirmeyecek şekilde tasarlandı ... beklemek dışında, hayır değildi. Öyle gözüküyor, ama doThings
önemli olan değişken durumu değiştirdi. Dolayısıyla, geçersiz kılınabilir işlevler olmasa da,
void dealWithStuff(WayOfDoingUsefulThings bar){
bar.doThings()
use(bar.getStuff());
}
şimdi, a'yı geçirdiğinizde beklenenden farklı bir şey yapıyor MyUsefulThings
. Daha da kötüsü, WayOfDoingUsefulThings
bu sözleri verdiğini bile bilmiyor olabilirsiniz . Belki dealWithStuff
aynı kitaplığından gelen WayOfDoingUsefulThings
ve getStuff()
(düşünmek bile kütüphaneye tarafından ihraç edilmez arkadaş sınıfı C ++). Daha da kötüsü, sen fark etmeden dilin statik kontrollerini yendin: dealWithStuff
Bir sürdü WayOfDoingUsefulThings
sadece bunun olurdu emin olmak için getStuff()
belirli bir şekilde davrandığını işlevi.
Kompozisyon kullanarak
class MyUsefulThings implements UsefulThingsInterface{
private way = new WayOfDoingUsefulThings()
void doThings() {
//my way
}
void doMoreThings() {
this.way.doMoreThings();
}
}
statik tip güvenliği geri getirir. Genel olarak, alt tipleme yapılırken, kalıtsallıktan daha kolay ve daha güvenli bir bileşim kullanmak kolaydır. Ayrıca, son yöntemleri geçersiz kılmanıza izin verir, bu da ara yüzlerde çoğu zaman sanal olmayan ve sanal olmayan her şeyi ilan etmekte özgür olmanız gerektiği anlamına gelir .
Daha iyi bir dünyada, diller otomatik olarak bir delegation
anahtar kelimeyle kazan plakasını yerleştirir . Çoğu, öyle değil, bir dezavantajı daha büyük sınıflar. Bununla birlikte, IDE'nizi size temsilci seçme örneğini yazmasını sağlayabilirsiniz.
Şimdi, yaşam sadece polimorfizmle ilgili değil. Her zaman alt tür yazmanıza gerek yoktur. Polimorfizmin amacı genellikle yeniden kod kullanımıdır, ancak bu hedefe ulaşmak için tek yol bu değildir. Çoğu zaman, bileşimin, alt tip polimorfizmi olmadan, işlevselliği yönetmenin bir yolu olarak kullanılması mantıklıdır.
Ayrıca, davranışsal kalıtımın da kullanımları vardır. Bilgisayar bilimindeki en güçlü fikirlerden biridir. Sadece bu, çoğu zaman iyi OOP uygulamaları sadece arayüz kalıtım ve kompozisyonlar kullanılarak yazılabilir. İki ilke
- Kalıtım yasağı veya bunun için tasarım
- Kompozisyon tercih et
yukarıdaki nedenlerden dolayı iyi bir rehberdir ve önemli bir maliyete maruz kalmayın.