"Miras yerine kompozisyonu tercih et" sadece iyi bir buluşsal yöntemdir
Bağlamı düşünmelisiniz, çünkü bu evrensel bir kural değildir. Kompozisyon yapabildiğinizde asla miras kullanma anlamına gelmeyin. Eğer durum buysa, kalıtımı yasaklayarak düzeltirdiniz.
Umarım bu nokta bu yazı boyunca açıklığa kavuşturulur.
Kompozisyonun değerlerini tek başına savunmaya çalışmayacağım. Hangi konu dışı düşünün. Bunun yerine, geliştiricinin kompozisyonu daha iyi kullanacak miras olarak değerlendirebileceği bazı durumlar hakkında konuşacağım. Bu notta, mirasın kendi teması var, ki bu da konu dışı.
Araç örneği
Anlatım amacıyla, aptalca şeylere çalışan geliştiriciler hakkında yazıyorum
Bazı cepten kursları kullandığını bize klasik örneklerinden bir varyantı için gidelim ... Bir var Vehicle
, sınıf o zaman türetmek Car
, Airplane
, Balloon
ve Ship
.
Not : Bu örneği topraklamanız gerekiyorsa, bunların bir video oyunundaki tür nesneler olduğunu varsayalım.
O zaman Car
ve Airplane
bazı ortak kodları olabilir, çünkü her ikisi de karada tekerlek üzerinde yuvarlanabilir. Geliştiriciler bunun için miras zincirinde bir ara sınıf oluşturmayı düşünebilirler. Yine de, Airplane
ve arasında bazı paylaşılan kodlar da vardır Balloon
. Bunun için miras zincirinde başka bir aracı sınıf oluşturmayı düşünebilirler.
Böylece, geliştirici çoklu miras arar. Geliştiricilerin birden fazla miras aradığı noktada, tasarım zaten yanlış gitti.
Bu davranışı arayüzler ve kompozisyon olarak modellemek daha iyidir, bu nedenle çoklu sınıf mirasına girmek zorunda kalmadan tekrar kullanabiliriz. Örneğin, geliştiriciler FlyingVehicule
sınıfı oluşturun. Bunun Airplane
bir FlyingVehicule
(sınıf mirası) olduğunu söyleyebilirler, ancak bunun yerine Airplane
bir Flying
bileşeni (kompozisyonu) ve Airplane
bir IFlyingVehicule
(arayüz mirası) olduğunu söyleyebiliriz .
Gerekirse, arayüzleri kullanarak çoklu kalıtım (arayüzlerin) olabilir. Ayrıca, belirli bir uygulamaya bağlanmıyorsunuz. Kodunuzun yeniden kullanılabilirliği ve test edilebilirliğinin artırılması.
Kalıtımın polimorfizm için bir araç olduğunu unutmayın. Ek olarak, polimorfizm yeniden kullanılabilirlik için bir araçtır. Kompozisyon kullanarak kodunuzun tekrar kullanılabilirliğini artırabilirseniz, bunu yapın. Kompozisyonun daha iyi tekrar kullanılabilirlik sağlayıp sağlamadığından emin değilseniz, "Kompozisyonu kalıtım yerine tercih et" iyi bir buluşsal yöntemdir.
Bütün bunlar bahsetmeden Amphibious
.
Aslında, yerden çıkan şeylere ihtiyacımız olmayabilir. Stephen Hurn'un “Miras Üzerine Kompozisyonu İyileştirme” bölüm 1 ve bölüm 2 makalelerinde daha anlamlı bir örneği var .
Değiştirilebilirlik ve Kapsülleme
Miras mı A
yoksa beste B
mi olmalı ?
Bunun A
bir uzmanlığı Liskov ikame ilkesiniB
yerine getirecekse , kalıtım uygulanabilir, hatta arzu edilir. Geçerli bir ikame olmadığı durumlar varsa, miras kullanmamalıyız.A
B
Türetilmiş sınıfı savunmak için bir tür savunmacı programlama biçimi olarak kompozisyonla ilgilenebiliriz . Özellikle, B
başka farklı amaçlar için kullanmaya başladıktan sonra , bu amaçlar için daha uygun olması için değiştirilmesi veya uzatılması yönünde bir baskı olabilir. B
Geçersiz bir duruma yol açabilecek yöntemleri ortaya çıkarabilecek risk varsa A
, miras yerine kompozisyon kullanmalıyız. Her ikisinin de yazarı olsak B
ve A
endişelenmemiz gereken bir şey daha az olsa bile , kompozisyon yeniden kullanılabilirliği kolaylaştırır B
.
Hatta varsa features iddia edebilir B
o A
ihtiyacı yoktur (ve bu özellikler için geçersiz bir durumda neden olabilir eğer biz bilmiyoruz A
mevcut uygulamadaki ya da gelecekte ya), bu kullanım bileşimine iyi bir fikirdir miras yerine.
Kompozisyon ayrıca anahtarlama uygulamalarına izin verme ve alaycılığı azaltma avantajlarına sahiptir.
Not : İkame geçerli olmasına rağmen kompozisyon kullanmak istediğimiz durumlar vardır. Bu ikame edilebilirliği arayüzler veya soyut sınıflar (ki bu başka bir konu olduğunda kullanılacak) kullanarak arşivler ve daha sonra gerçek uygulamanın bağımlılık enjeksiyonuyla kompozisyon kullanırız.
Son olarak, elbette, üst sınıfı savunmak için kompozisyon kullanmamız gerektiği argümanı vardır, çünkü miras ana sınıfın kapsüllenmesini bozar:
Miras, bir alt sınıfı ebeveyninin uygulamasının ayrıntılarına maruz bırakır, genellikle 'miras kapsüllemeyi keser' denir
- Tasarım Desenleri: Yeniden Kullanılabilir Nesneye Yönelik Yazılım Öğeleri, Dörtlü Çete
Bu kötü tasarlanmış bir ebeveyn sınıfı. Bu yüzden:
Miras için tasarım yapın veya yasaklayın.
- Etkili Java, Josh Bloch
Yo-Yo sorunu
Kompozisyonun yardımcı olduğu bir başka durum Yo-Yo problemidir . Bu Wikipedia'dan bir alıntı:
Yazılım geliştirmede yo-yo problemi, bir programcının kalıtım grafiği o kadar uzun ve karmaşık olan bir programı okuması ve anlaması gerektiğinde ortaya çıkan bir anti-modeldir ve programcı takip etmek için birçok farklı sınıf tanımı arasında dolaşmaya devam etmelidir. programın kontrol akışı.
Çözebilirsiniz, örneğin: Sınıfınız sınıftan C
miras alınmaz B
. Bunun yerine sınıfınızda C
türden A
bir nesne olabilir (veya olmayabilir) veya türünde bir nesne olabilir B
. Bu şekilde, uygulama detaylarına B
karşı değil, arayüzün (of) A
sunduğu sözleşmeye karşı programlama yapacaksınız .
Sayaç örnekleri
Birçok çerçeve kompozisyon üzerinde kalıtımı desteklemektedir (tartıştığımız şeyin tersidir). Geliştirici bunu yapabilir, çünkü temel sınıflarına kompozisyonla birlikte uygulandığında istemci kodunun boyutunu artıracaktır. Bazen bunun nedeni dilin sınırlamalarıdır.
Örneğin, bir PHP ORM çerçevesi, nesnenin gerçek özelliklere sahipmiş gibi kod yazmaya izin vermek için sihirli yöntemler kullanan bir temel sınıf oluşturabilir. Bunun yerine, sihirli yöntemlerle işlenen kod veritabanına gidecek, istenen belirli alanı sorgulayacak (belki de gelecekteki istek için önbelleğe alacak) ve döndürecektir. Kompozisyon ile yapılması, istemcinin her alan için özellikler oluşturmasını veya sihirli yöntemler kodunun bir sürümünü yazmasını gerektirir.
Ek : ORM nesnelerinin genişletilmesine izin vermenin başka yolları da vardır. Dolayısıyla, bu davada kalıtımın gerekli olduğunu düşünmüyorum. O daha ucuz.
Başka bir örnek olarak, bir video oyunu motoru, 3B oluşturma ve olay işleme için hedef platforma bağlı olarak yerel kodu kullanan bir temel sınıf oluşturabilir. Bu kod karmaşık ve platforma özgüdür. Motorun geliştirici kullanıcısının bu kodla başa çıkması pahalı ve hataya yatkındır, aslında motoru kullanma nedeninin bir parçasıdır.
Ayrıca, 3B oluşturma parçası olmadan, bu kaç widget çerçevesi çalışır. Bu, işletim sistemi mesajlarını ele alma konusunda endişelenmenizi önler ... aslında, birçok dilde, yerel bir tür teklif olmadan bu kodu yazamazsınız. Dahası, eğer bunu yaparsanız, taşınabilirliğinizi sıkılaştırırdı. Bunun yerine, geliştiricinin uyumluluğu bozmaması koşuluyla kalıtımla (çok fazla); kodunuzu gelecekte destekleyecekleri yeni platformlara kolayca taşıyabileceksiniz.
Ayrıca, birçok kez yalnızca birkaç yöntemi geçersiz kılmak ve diğer her şeyi varsayılan uygulamalarla bırakmak istediğimizi düşünün. Kompozisyon kullansaydık, sadece sarılmış nesneye delege olsa bile, tüm bu yöntemleri yaratmamız gerekirdi.
Bu argümanla, kompozisyonun kalıtım için kalıtımdan daha kötü olabileceği bir nokta vardır (temel sınıf çok karmaşık olduğunda). Yine de, kalıtımın sürekliliğinin bileşiminkinden daha kötü olabileceğini (kalıtım ağacı çok karmaşık olduğunda), yo-yo probleminde bahsettiğim şey budur.
Sunulan örneklerde, geliştiriciler nadiren kalıtım yoluyla üretilen kodu diğer projelerde yeniden kullanmayı planlamaktadır. Bu, miras kullanımının kompozisyon yerine yeniden kullanılabilirliğinin azaltılmasını azaltır. Buna ek olarak, miras kullanarak çerçeve geliştiricileri kullanımı kolay ve keşfedilmesi kolay bir kod sağlayabilir.
Son düşünceler
Gördüğünüz gibi, kompozisyon her zaman değil, bazı durumlarda kalıtım üzerinde bazı avantajlara sahiptir. Kararı vermek için bağlamı ve ilgili farklı faktörleri (yeniden kullanılabilirlik, sürdürülebilirlik, test edilebilirlik vb.) Dikkate almak önemlidir. İlk noktaya dönelim: "Kompozisyonu kalıtım yerine tercih et" sadece iyi bir buluşsal yöntemdir.
Ayrıca açıkladığım durumların çoğunun bir dereceye kadar Özellikler veya Mixinler ile çözülebileceğini fark edebilirsiniz. Ne yazık ki, bunlar büyük dil listesinde ortak özellikler değildir ve genellikle bazı performans maliyetleriyle birlikte gelirler. Neyse ki, popüler kuzenleri Genişletme Yöntemleri ve Varsayılan Uygulamalar bazı durumları azaltır.
Arayüzlerin bazı avantajları hakkında neden konuştuğum son bir yazım var, neden C #'da UI, İş ve Veri erişimi arasında arayüzlere ihtiyacımız var . Ayrışmaya yardımcı olur ve yeniden kullanılabilirliği ve test edilebilirliği kolaylaştırır, ilgilenebilirsiniz.