it-swarm.asia

Bu "miras üzerine iyilik kompozisyonu" kavramı nereden geliyor?

Son birkaç ay içinde, mantra "kalıtım üzerine iyilik kompozisyonu" hiçbir yerden çıkmış ve programlama topluluğu içinde neredeyse bir çeşit meme haline gelmiş gibi görünüyor. Ve her gördüğümde biraz gizemliyim. Sanki birisi "çekiçlerin üzerinde matkaplar tercih et" dedi. Deneyimlerime göre, kompozisyon ve miras, farklı kullanım durumlarına sahip iki farklı araçtır ve bunları birbirinin yerine kullanılabilir ve biri diğerinden üstün olarak ele alır.

Ayrıca, neden kalıtım kötü ve kompozisyon iyi olduğu için asla gerçek bir açıklama görmüyorum, bu da beni daha şüpheli hale getiriyor. Sadece imanla kabul edilmesi mi gerekiyor? Liskov ikamesi ve polimorfizmin iyi bilinen, net faydaları vardır ve IMO, nesne yönelimli programlama kullanımının tamamını oluşturur ve hiç kimse neden kompozisyon lehine atılmaları gerektiğini açıklamaz.

Bu kavramın nereden geldiğini ve arkasındaki mantığın ne olduğunu bilen var mı?

145
Mason Wheeler

GoF'tan çok önce kompozisyon-miras tartışmaları duyduğumu düşünüyorum, ancak parmağımı belirli bir kaynağa koyamıyorum. Zaten Booch olabilirdi.

<Rant>

Ah ama birçok mantra gibi, bu tipik çizgiler boyunca dejenere oldu:

  1. catch-ifadesini belirleyen saygın bir kaynak tarafından ayrıntılı bir açıklama ve argüman ile tanıtılmaktadır orijinal karmaşık tartışmanın bir hatırlatıcısı olarak
  2. genellikle n00b hataları hakkında yorum yaparken, bir süreliğine bilerek birkaç kulüp tarafından bilen bir kulübün göz kırpmasıyla paylaşılır
  3. yakında açıklamayı hiç okumamış binlerce kişi düşüncesizce tekrar ediyor, ama düşünmemek için bir bahane olarak kullanmayı seviyorum ve başkalarına üstün hissetmenin ucuz ve kolay bir yolu olarak
  4. nihayetinde, hiçbir makul mantıksız “meme” gelgitini doğuramaz ve paradigma din ve dogmaya dönüşür.

Başlangıçta n00bs'yi Aydınlanmaya götürmeyi amaçlayan meme, şimdi onları bilinçsiz olarak bludgeon için bir kulüp olarak kullanılıyor.

Kompozisyon ve kalıtım çok farklı şeylerdir ve birbirleriyle karıştırılmamalıdır. Kompozisyonun kalıtımı simüle etmek için kullanılabileceği doğru olsa da bir sürü ekstra çalışma ile, bu kalıtım ikinci sınıf bir vatandaş değil, kompozisyonu en sevdiği oğul yapmaz. Birçok n00b'nin kalıtımı bir kısayol olarak kullanmaya çalışması, mekanizmayı geçersiz kılmaz ve neredeyse tüm n00b'ler hatadan öğrenir ve böylece gelişir.

Lütfen TASARIMLARINIZ HAKKINDA düşünün ve sloganları atmayı bırakın.

</ Rant>

139
Steven A. Lowe

Deneyim.

Dediğiniz gibi, bunlar farklı işler için araçlardır, ancak ifade, insanlar bu şekilde kullanmadıkları için ortaya çıktı.

Kalıtım öncelikle polimorfik bir araçtır, ancak bazı insanlar, daha sonraki tehlikelerine göre, kodu yeniden kullanma/paylaşma yolu olarak kullanmaya çalışırlar. Gerekçe "miras alırsam tüm yöntemleri ücretsiz alırsam" olmakla birlikte, bu iki sınıfın potansiyel olarak polimorfik bir ilişkisi olmadığı gerçeğini göz ardı etmek.

Öyleyse neden kompozisyonu kalıtım yerine tercih ediyor - çünkü sınıflar arasındaki ilişki daha çok polimorfik olmadığı için. Basitçe insanlara kalıtsal olarak diz sarsıntısının yanıt vermemelerini hatırlatmak için var.

75
Stephen Bailey

Bu yeni bir fikir değil, aslında 1994'te yayınlanan GoF tasarım desenleri kitabında tanıtıldığına inanıyorum.

Kalıtımla ilgili temel sorun, beyaz kutu olmasıdır. Tanım olarak, miras aldığınız sınıfın uygulama ayrıntılarını bilmeniz gerekir. Kompozisyonla ise, sadece oluşturduğunuz sınıfın genel arayüzünü önemsersiniz.

GoF kitabından:

Miras, bir alt sınıfı ebeveyninin uygulamasının ayrıntılarına maruz bırakır, genellikle 'miras kapsüllemeyi keser' denir

GoF kitabındaki wikipedia makalesinin iyi bir tanıtımı var.

43
Dean Harding

Sorunuzun bir bölümünü yanıtlamak için, bu kavramın ilk olarak 1994 yılında yayınlanan GOF Tasarım Desenleri: Yeniden Kullanılabilir Nesne Odaklı Yazılım Öğeleri kitabında ortaya çıktığını düşünüyorum. 20, girişte:

Miras yerine nesne kompozisyonunu tercih etme

Bu ifadeyi, kalıtımın kompozisyonla kısa bir karşılaştırmasıyla önsözler. "Asla kalıtım kullanma" demiyorlar.

27
vjones

"Kalıtım üzerine kompozisyon", "Bir sınıfın verilerinin (veya davranışının) başka bir sınıfa dahil edilmesi gerektiğini hissederken, körü körüne miras uygulamadan önce daima kompozisyon kullanmayı düşünün" demenin kısa (ve görünüşte yanıltıcı) bir yoludur.

Bu neden doğru? Çünkü kalıtım 2 sınıf arasında sıkı, derleme zamanı birleştirme yaratır. Buna karşılık kompozisyon, diğerlerinin yanı sıra kaygıların net bir şekilde ayrılmasını, çalışma zamanında bağımlılıkları değiştirme ve daha kolay, daha izole bağımlılık test edilebilirliğini sağlayan gevşek bağlantıdır.

Bu sadece kalıtımın dikkatle ele alınması gerektiği anlamına gelir, çünkü yararlı olmadığı için bir bedeli vardır. Aslında, "Kalıtım üzerinde kompozisyon" genellikle "Kompozisyon + kalıtım üzerinde miras" olur, çünkü genellikle bağımlılığınızın somut alt sınıfın kendisinden ziyade soyut bir üst sınıf olmasını istersiniz. Çalışma zamanında bağımlılığınızın farklı somut uygulamaları arasında geçiş yapmanızı sağlar.

Bu nedenle (diğerleri arasında), muhtemelen mirasın Vanilla mirasından daha çok arayüz uygulaması veya soyut sınıflar şeklinde kullanıldığını göreceksiniz.

(Mecazi) bir örnek şunlar olabilir:

"Bir Snake sınıfım var ve bu sınıfın bir parçası olarak Snake ısırdığında ne olacağını dahil etmek istiyorum. Snake'in Bite () yöntemine sahip bir BiterAnimal sınıfını miras alması ve zehirli ısırığı yansıtması için bu yöntemi geçersiz kılması cazip olurdu Ama Kalıtım Üzerine Kompozisyon beni bunun yerine kompozisyon kullanmaya çalışmam gerektiği konusunda uyarıyor ... Benim durumumda, bu, bir Isırık üyesi olan Yılan'a dönüşebilir.Bite sınıfı birkaç alt sınıfla soyut (veya bir arayüz) olabilir. bana VenomousBite ve DryBite alt sınıflarına sahip olmak ve yılan yaşlandıkça aynı Snake örneğindeki ısırığı değiştirmek gibi güzel şeyler, ayrıca bir Isırığın tüm etkilerini kendi ayrı sınıfında ele almak, o Frost sınıfında tekrar kullanmamı sağlayabilir. , çünkü don ısırıyor ama BiterAnimal değil, vb ... "

24
guillaume31

Kompozisyon için bazı olası argümanlar:

Kompozisyon biraz daha fazla dil/çerçeve agnostiktir
Kalıtım ve uyguladığı/gerektirdiği/etkinleştirdiği şey, alt/üst sınıfın neye erişebileceği ve sanal yöntemler vb. İçin hangi performans sonuçları olabileceği açısından diller arasında farklılık gösterecektir. Kompozisyon oldukça basittir ve çok az dil gerektirir ve böylece farklı platformlar/çerçevelerdeki uygulamalar kompozisyon desenlerini daha kolay paylaşabilir.

Kompozisyon, nesneleri inşa etmenin çok basit ve dokunsal bir yoludur
Kalıtımın anlaşılması nispeten kolaydır, ancak yine de gerçek hayatta bu kadar kolay gösterilemez. Gerçek hayatta birçok nesne parçalara ayrılabilir ve bestelenebilir. Diyelim ki bir bisiklet iki tekerlek, bir çerçeve, bir koltuk, bir zincir vb. Kullanılarak inşa edilebilir. Kompozisyon ile kolayca açıklanabilir. Bir miras metaforunda, bir bisikletin bir tek tekerlekli sirk bisikletine uzandığını söyleyebilirsin, gerçek kompozisyondan kompozisyondan biraz daha uzak ama yine de çok daha uzundur (açıkçası bu çok iyi bir miras örneği değildir, ancak nokta aynı kalır). Word mirası bile (en azından beklediğim çoğu ABD İngilizcesi) otomatik olarak, "Ölen bir akrabadan geçen bir şey", yazılımdaki anlamı ile bir korelasyonu olan, ama yine de sadece gevşekçe uyan bir çizgi çağrıştırıyor.

Kompozisyon neredeyse her zaman daha esnektir
Kompozisyon kullanarak her zaman kendi davranışınızı tanımlamayı seçebilir veya bestelediğiniz bölümlerin o kısmını açığa çıkarabilirsiniz. Bu şekilde, bir miras hiyerarşisi (sanal ve sanal olmayan vb.) Tarafından uygulanabilecek kısıtlamaların hiçbiriyle karşılaşmazsınız.

Bu nedenle, Kompozisyon doğal olarak, mirastan daha az teorik kısıtlamaları olan daha basit bir metafor olduğu için olabilir. Ayrıca, bu özel nedenler tasarım sırasında daha belirgin olabilir veya kalıtımın bazı acı noktalarıyla uğraşırken muhtemelen ortaya çıkabilir.

Yasal Uyarı:
Açıkçası bu net bir kesim değil/tek yönlü bir sokak. Her tasarım çeşitli kalıpların/araçların değerlendirilmesini hak eder. Kalıtım yaygın olarak kullanılır, birçok faydası vardır ve çoğu zaman kompozisyondan daha zariftir. Bunlar, kompozisyonu tercih ederken kullanılabilecek bazı olası nedenlerdir.

22
TJB

Belki de insanların son birkaç ay içinde bunu söylediğini fark ettiniz, ancak iyi programcılar tarafından bundan daha uzun zamandır biliniyor. Yaklaşık on yıldır uygun olan yerlerde kesinlikle söylüyorum.

Kavramın amacı kalıtım için büyük bir kavramsal yükün olmasıdır. Kalıtım kullandığınızda, her bir yöntem çağrısında örtük bir gönderim vardır. Derin miras ağaçlarınız veya birden fazla gönderiminiz varsa veya her ikisi de (daha da kötüsü) varsa, belirli bir çağrıda belirli yöntemin nereye gönderileceğini bulmak kraliyet PITA olabilir. Kod hakkında doğru muhakemeyi daha karmaşık hale getirir ve hata ayıklamayı zorlaştırır.

Açıklamak için basit bir örnek vereyim. Bir miras ağacının derinliklerinde birinin foo yöntemini olduğunu varsayalım. Sonra başka biri gelir ve ağacın üstüne foo ekler, ancak farklı bir şey yapar. (Bu durum çoklu mirasta daha yaygındır.) Şimdi kök sınıfında çalışan kişi, belirsiz çocuk sınıfını kırmıştır ve muhtemelen bunu fark etmemektedir. Ünite testlerinde% 100 kapsama sahip olabilirsiniz ve bu kırılmayı fark edemezsiniz, çünkü üstteki kişi çocuk sınıfını test etmeyi düşünmez ve çocuk sınıfı testleri, üstte oluşturulan yeni yöntemleri test etmeyi düşünmez. . (Kuşkusuz bunu yakalayacak birim testleri yazmanın yolları vardır, ancak testleri bu şekilde kolayca yazamayacağınız durumlar da vardır.)

Bunun aksine, kompozisyon kullandığınızda, her çağrıda genellikle çağrıyı ne gönderdiğinize daha açıktır. (Tamam, örneğin bağımlılık enjeksiyonu ile kontrolün ters çevrilmesini kullanıyorsanız, çağrının nereye gittiğini bulmak da sorunlu olabilir. Ancak genellikle anlaşılması daha kolaydır.) Bonus olarak, bileşim, yöntemlerin birbirinden ayrılmasıyla sonuçlanır. Yukarıdaki örnek orada olmamalıdır, çünkü alt sınıf bazı belirsiz bileşenlere taşınır ve foo çağrısının belirsiz bileşen veya ana nesne için tasarlanıp amaçlanmadığı konusunda hiçbir zaman bir soru yoktur.

Şimdi, kalıtımın ve kompozisyonun iki farklı türde hizmet veren çok farklı iki araç olduğu konusunda kesinlikle haklısınız. Tabii miras kavramsal yükü taşır, ancak iş için doğru araç olduğunda, onu kullanmamaya ve sizin için ne yaptığını elle yapmaya çalışmaktan daha az kavramsal yük taşır. Ne yaptığını bilen kimse, asla miras almamanız gerektiğini söylemez. Ancak yapılacak doğru şey olduğundan emin olun.

Ne yazık ki birçok geliştirici nesne yönelimli yazılımı öğreniyor, kalıtım hakkında bilgi alıyor ve sonra yeni baltalarını mümkün olduğunca sık kullanıyor. Bu, kompozisyonun doğru araç olduğu kalıtım kullanmaya çalıştıkları anlamına gelir. Umarım zamanla daha iyi öğrenirler, ancak çoğu zaman bu, kaldırılan birkaç uzuv vb. Sonrasına kadar gerçekleşmez. Onlara kötü bir fikir olduğunu söylemek, öğrenme sürecini hızlandırma ve yaralanmaları azaltma eğilimindedir.

11
btilly

OO yeni başlayanlar gerek duymadıklarında miras kullanma eğilimindedirler) Kalıtım kesinlikle kötü bir şey değildir, ancak aşırı kullanılabilir. başka durumlarda kompozisyon muhtemelen işe yarayacak, diğer durumlarda miras işe yarayacak ve kompozisyon işe yaramayacak.

Bir sınıftan miras almak birçok şey ifade eder. Bir Derived'in bir tür Baz olduğunu ima eder (kanlı ayrıntılar için Liskov İkame ilkesi bölümüne bakın), çünkü bir Baz kullandığınızda bir Derived kullanmanın mantıklı olacağı anlamına gelir. Base'in korunan üyelerine ve üye işlevlerine Türetilmiş erişim sağlar. Bu yakın bir ilişkidir, yani yüksek kuplajı vardır ve birindeki değişikliklerin diğerinde değişiklik gerektirmesi daha olasıdır.

Kuplaj kötü bir şeydir. Programların anlaşılmasını ve değiştirilmesini zorlaştırır. Diğer şeyler eşit olduğunda, her zaman daha az bağlantı ile seçeneği seçmelisiniz.

Bu nedenle, kompozisyon veya miras işi etkili bir şekilde yapacaksa, kompozisyonu seçin, çünkü daha düşük kuplajdır. Kompozisyon işi etkili bir şekilde yapmayacak ve veraset miras yapacaksa, miras seçmelisiniz, çünkü yapmanız gerekir.

8
David Thornley

İşte benim iki sent (şimdiye kadar ortaya çıkan tüm mükemmel noktaların ötesinde):

IMHO, Çoğu programcının gerçekten miras almadığı ve kısmen bu kavramın öğretilmesinin bir sonucu olarak aşırıya kaçması gerçeğine geliyor. Bu kavram, onları nasıl doğru yapacaklarını öğretmeye odaklanmak yerine, aşırıya kaçmaktan caydırmaya çalışmanın bir yolu olarak var.

Öğretmek veya mentorluk yapmak için zaman harcayan herkes, özellikle diğer paradigmalarda deneyime sahip yeni geliştiricilerde bunun sık sık olduğunu bilir:

Bu geliştiriciler başlangıçta kalıtımın bu korkutucu kavram olduğunu düşünüyorlar. Böylece arayüz çakışmaları (örneğin, ortak alt tipleme olmadan aynı belirtilen davranış) ve ortak işlevsellik parçalarını uygulamak için globaller ile türler oluştururlar.

Sonra (genellikle aşırı hevesli öğretimin bir sonucu olarak), kalıtımın faydalarını öğrenirler, ancak genellikle yeniden kullanmak için hepsini yakalama çözümü olarak öğretilir. Sonuçta, paylaşılan davranışların miras yoluyla paylaşılması gerektiği algısı ortaya çıkar. Bunun nedeni, odaklanmanın alt tiplemeden ziyade uygulama mirası üzerinedir.

Vakaların% 80'inde bu yeterince iyi. Fakat diğer% 20'si sorunun başladığı yer. Yeniden yazmayı önlemek ve uygulamayı paylaşma avantajından yararlandıklarından emin olmak için, hiyerarşilerini temeldeki soyutlamalardan ziyade amaçlanan uygulama etrafında tasarlamaya başlarlar. "Yığın çift bağlantılı listeden miras alır" bunun iyi bir örneğidir.

Çoğu öğretmen bu noktada arabirim kavramını tanıtmakta ısrar etmez, çünkü ya başka bir kavramdır ya da (C++ 'da) bu aşamada öğretilmeyen soyut sınıflar ve çoklu kalıtım kullanarak onları taklit etmeniz gerekir. Java'da, birçok öğretmen "çoklu miras yok" veya "çoklu miras kötüdür" arayüzlerin öneminden ayırt etmez.

Bütün bunlar, uygulama kalıtımı ile gereksiz kod yazmak zorunda olmamanın güzelliğini çok fazla öğrendiğimiz, tonlarca basit delegasyon kodunun doğal görünmediği gerçeğiyle birleşiyor. Kompozisyon dağınık görünüyor, bu yüzden yeni programcıları onları yine de kullanmak için zorlamak için bu başparmak kurallarına ihtiyacımız var (ki bunlar da aşırıya kaçıyor).

8
Uri

Yorumlardan birinde Mason bir gün Kalıtım zararlı olarak kabul edildi hakkında konuşacağımızı söyledi.

Umarım.

Kalıtım sorunu bir zamanlar basittir ve ölümcül bir kavramın bir işlevselliğe sahip olması gerektiği fikrine saygı göstermez.

Çoğu OO dilinde, bir temel sınıftan miras alırken:

  • arayüzünden devral
  • uygulanmasından miras (hem veriler hem de yöntemler)

Ve işte sorun haline gel.

Bu değil tek yaklaşımdır, ancak 00 dili çoğunlukla ona takılı kalmıştır. Neyse ki arayüzler/soyut sınıflar bunlarda mevcuttur.

Ayrıca, başka türlü yapmanın kolaylığının olmaması, büyük ölçüde kullanılmasına katkıda bulunur: Açıkçası, bunu bile bilmek, bir arabirimden miras alır ve temel sınıfı kompozisyonla gömür, yöntem çağrılarının çoğunu devreder misiniz? Yine de, arayüzde aniden yeni bir yöntem ortaya çıkarsa ve bilinçli olarak, nasıl uygulanacağını seçmek zorunda kalırsanız bile uyarılırsınız.

Bir karşı nokta olarak, Haskell Liskov Prensibinin sadece saf arayüzlerden (kavramlar olarak adlandırılır) "türetilirken" kullanılmasına izin verir (1). Varolan bir sınıftan türeyemezsiniz, yalnızca kompozisyon verilerini katıştırmanıza izin verir.

(1) kavramlar bir uygulama için mantıklı bir temerrüt sağlayabilir, ancak veri bulunmadığından, bu temerrüt sadece kavram tarafından önerilen diğer yöntemler veya sabitler açısından tanımlanabilir.

8
Matthieu M.

Bu tür bir tavsiyenin "uçmaya gitmeyi tercih et" demek olduğunu düşünüyorum. Yani, uçakların arabalara göre her türlü avantajı vardır, ancak bu belirli bir karmaşıklıkla birlikte gelir. Birçok insan şehir merkezinden banliyölere uçmaya çalışırsa, gerçekten, gerçekten duymaları gereken tavsiye uçmak zorunda olmamalarıdır ve aslında, uçmak sadece kısa vadede serin/verimli/kolay görünse bile uzun vadede daha karmaşıktır. Oysa do uçmanız gerektiğinde, genellikle açık olması gerekir.

Benzer şekilde, miras, kompozisyonun yapamayacağı şeyleri yapabilir, ancak bunu ihtiyacınız olduğunda değil, ihtiyacınız olduğunda kullanmalısınız. Bu yüzden asla yapmadığınız zaman kalıtsallığa ihtiyacınız olduğunu varsaymak istemiyorsanız, "kompozisyonu tercih et" tavsiyesine ihtiyacınız yoktur. Ama birçok insan do ve do bu tavsiyeye ihtiyaç duyar.

Gerçekten mirasa İHTİYACINIZ olduğunda, bu açıktır ve o zaman onu kullanmalısınız.

Ayrıca Steven Lowe'un yanıtı. Gerçekten, gerçekten.

7
Jack V.

Basit cevap şudur: kalıtım kompozisyondan daha fazla bağlantıya sahiptir. Aksi takdirde eşdeğer niteliklerde iki seçenek göz önüne alındığında, daha az eşleşmiş olanı seçin.

7
Jeffrey Faust

Kalıtım doğal olarak kötü değildir ve kompozisyon doğal olarak iyi değildir. Bunlar sadece bir OO programcının yazılım tasarlamak için kullanabileceği araçlardır.

Bir sınıfa baktığınızda, kesinlikle olması gerekenden daha fazlasını mı yapıyor ( SRP )? İşlevselliği gereksiz yere kopyalıyor mu ( KURU ), yoksa diğer sınıfların özellikleri veya yöntemleri ile aşırı ilgileniyor mu ( Feature Envy ) ?. Eğer sınıf tüm bu kavramları (ve muhtemelen daha fazlasını) ihlal ediyorsa, Tanrı Sınıfı olmaya çalışmaktadır. Bunlar, yazılım tasarlanırken ortaya çıkabilen, hiçbiri mutlaka kalıtım sorunu olmayan, ancak polimorfizmin de uygulandığı yerlerde büyük baş ağrıları ve kırılgan bağımlılıklar oluşturabilen bir takım problemlerdir.

Sorunun kökü, kalıtım hakkında daha az anlayış eksikliği olması ve tasarım açısından ya kötü seçimden daha fazla olması ya da belki de Tek Sorumluluk İlkesi. Polimorfizm ve Liskov İkame kompozisyon lehine atılmak zorunda değildir. Çok biçimliliğin kendisi mirasa dayanmadan uygulanabilir, bunların hepsi tamamlayıcı kavramlardır. Düşünceli uygulanırsa. İşin püf noktası, kodunuzu basit ve temiz tutmayı ve sağlam bir sistem tasarımı oluşturmak için oluşturmanız gereken sınıf sayısı konusunda aşırı endişe duymamaktır.

Kompozisyonun miras üzerine tercih edilmesine ilişkin sorun, çözülen sorun açısından en mantıklı olan tasarım öğelerinin düşünceli bir şekilde uygulanmasının gerçekten başka bir örneğidir. Davranışı devralmak için ihtiyaç yoksa, muhtemelen daha sonra uyumsuzluklardan ve büyük yeniden düzenleme çabalarından kaçınmaya yardımcı olacağı için yapmamalısınız. Öte yandan, çoğaltmanın tamamı benzer sınıflardan oluşan bir grupta ortalanacak şekilde çok fazla kod yinelediğinizi görürseniz, ortak bir ata oluşturmanın aynı çağrı ve sınıfların sayısını azaltmaya yardımcı olabileceği olabilir. her sınıf arasında tekrarlamanız gerekebilir. Bu nedenle, kompozisyonu tercih ediyorsunuz, ancak mirasın asla geçerli olmadığını varsaymıyorsunuz.

2
S.Robins