“Miras yerine kompozisyonu tercih et” - İmza değişikliklerine karşı savunmanın tek nedeni mi?


13

Bu sayfa , miras üzerinde kompozisyonu aşağıdaki argümanla savunuyor (kelimelerimde yeniden ifade etti):

Üst sınıf yönteminin imzasındaki bir değişiklik (alt sınıfta geçersiz kılınmamış olan), Devralma kullandığımızda birçok yerde ek değişikliklere neden olur. Ancak, Kompozisyon'u kullandığımızda, gerekli ek değişiklik yalnızca tek bir yerde bulunur: Alt sınıf.

Miras yerine kompozisyonu tercih etmenin tek nedeni bu mu? Çünkü bu durumda, alt sınıf uygulamayı değiştirmese bile (yani, alt sınıfa kukla geçersiz kılmalar koyarak) üst sınıfın tüm yöntemlerini geçersiz kılan bir kodlama stili uygulanarak bu sorun kolayca hafifletilebilir. Burada bir şey mi eksik?



1
Ayrıca bakınız: Bu $ {blog} tartışın
gnat

2
Alıntı "tek" nedeni olduğunu söylemiyor.
Tulains Córdova

3
Kompozisyon neredeyse her zaman daha iyidir, çünkü kalıtım genellikle karmaşıklık, eşleşme, kod okuma ekler ve esnekliği azaltır. Ancak dikkate değer istisnalar vardır, bazen bir veya iki temel sınıf acıtmaz. Bu yüzden ' her zaman ' yerine ' tercih '.
Mark Rogers

Yanıtlar:


27

Ben benzetmeleri seviyorum, işte bir tane: Hiç dahili VCR oynatıcısı olan TV'lerden birini gördünüz mü? VCR ve DVD oynatıcı olana ne dersiniz? Veya Blu-ray, DVD ve VCR'ye sahip olanlardan. Oh ve şimdi herkes akış yapıyor, bu yüzden yeni bir TV seti tasarlamamız gerekiyor ...

Büyük olasılıkla, bir TV setiniz varsa, yukarıdaki gibi bir setiniz yoktur. Muhtemelen bunların çoğu hiç var olmamıştır. Büyük olasılıkla çok sayıda girişe sahip bir monitörünüz veya setiniz vardır, böylece her yeni tip giriş cihazı olduğunda yeni bir sete ihtiyacınız yoktur.

Kalıtım, VCR yerleşik TV'ye benzer. Birlikte kullanmak istediğiniz 3 farklı türde davranışınız varsa ve her birinin uygulama için 2 seçeneği varsa, bunları temsil etmek için 8 farklı sınıfa ihtiyacınız vardır. Daha fazla seçenek ekledikçe sayılar patlar. Bunun yerine kompozisyon kullanırsanız, bu kombinatoryal problemden kaçınırsınız ve tasarım daha genişletilebilir olma eğilimindedir.


6
90'ların sonunda ve 2000'lerin başında inşa edilmiş VCR ve DVD oynatıcılara sahip çok sayıda TV vardı.
JAB

@JAB ve bugün satılan birçok (en çok?) TV, bu konuda akışı destekliyor.
Casey

4
@JAB Ve hiçbiri bir DVD oynatıcısı Bluray oynatıcı satın almak için satılan olamaz
Alexander - Reinstate Monica

1
@JAB Doğru. "Daha önce dahili bir VCR oynatıcısı olan TV'lerden birini gördünüz mü?" onların var olduğunu ima eder.
JimmyJames

4
@Casey Çünkü açıkçası, bunun yerini alacak başka bir teknoloji olmayacak, değil mi? Birisinin yeni bir TV satın almadan başka bir şey kullanmak istemesi durumunda, bu TV'lerden herhangi birinin harici cihazlardan gelen girdileri desteklediğinden bahse girerim. Ya da daha az sayıda eğitimli alıcı, bu tür girdilere sahip olmayan bir TV satın almak istiyor. Benim önerim, bu yaklaşımların mevcut olmadığı ve kalıtımın olmadığını iddia etmeyeceğim. Mesele şu ki, bu yaklaşımlar kompozisyondan doğal olarak daha az esnektir.
JimmyJames

4

Hayır öyle değil. Miras üzerinde benim deneyim kompozisyonunda bir grup olarak yazılım düşüncesinin bir sonucudur nesneler ile davranışları o konuşma birbirlerine / nesneleri sağlayan hizmetler birbirleri yerine göre sınıflar ve veri .

Bu şekilde, hizmet sağlayan bazı nesneleriniz olur. Sen (örneğin bir diğer davranışı etkinleştirmek için (gerçekten hemen hemen Lego gibi) yapı taşları olarak kullanabilirsiniz UserRegistrationService bir tarafından sağlanan hizmetleri kullanan EmailerService ve UserRepository ).

Bu yaklaşımla daha büyük sistemler kurarken çok az durumda doğal olarak kalıtım kullandım. Günün sonunda miras, dikkatli bir şekilde ve sadece uygun olduğunda kullanılması gereken çok güçlü bir araçtır.


Burada Java zihniyetini görüyorum. OOP, üzerinde etkili olan işlevlerle birlikte paketlenmekte olan verilerle ilgilidir - açıkladığınız şey daha iyi "modüller" olarak adlandırılır. Nesneleri modül olarak yeniden yerleştirirken mirastan kaçınmanın iyi bir fikir olduğunu doğru buluyorsunuz, ancak bunu nesneleri tercih etmenin tercih edilen yolu olarak önerdiğinizi rahatsız edici buluyorum.
Brilliand

1
@Brilliand Açıkladığınız tarzda ne kadar java kodu yazıldığını ve bunun ne kadar dehşet olduğunu biliyorsan ... Smalltalk OOP terimini tanıttı ve mesaj alan nesnelerin etrafında tasarlandı. : "Hesaplama, mesaj göndererek tekdüze olarak çağrılabilen nesnelerin içsel bir yeteneği olarak görülmelidir." Üzerinde çalışan veri ve işlevlerden bahsedilmez. Üzgünüm, ama bence çok yanılıyorsunuz. Eğer veri ve fonksiyonlarla ilgiliyse, mutlu bir şekilde yapıları yazmaya devam edebilir.
marstato

@Tsar ne yazık ki, Java statik yöntemleri desteklese de, Java kültürü desteklemiyor
k_g

@Brilliand OOP'un "hakkında" kimin umurunda? Önemli olan nasıl olabilir onu ve bu şekillerde kullanmanın sonuçlarını kullanırlar.
user253751

1
@Tsar ile tartışmadan sonra: Java sınıflarının başlatılması gerekmez ve gerçekten de UserRegistrationService ve EmailService gibi sınıflar genellikle başlatılmamalıdır - ilişkili verileri olmayan sınıflar statik sınıflar olarak en iyi şekilde çalışır (ve bu nedenle orijinaliyle ilgisizdir) soru). Önceki yorumlardan bazılarını sileceğim, ama marsato'nun cevabına ilişkin orijinal eleştirim duruyor.
Brilliand

4

"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, Balloonve Ship.

Not : Bu örneği topraklamanız gerekiyorsa, bunların bir video oyunundaki tür nesneler olduğunu varsayalım.

O zaman Carve Airplanebazı 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, Airplaneve 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 FlyingVehiculesınıfı oluşturun. Bunun Airplanebir FlyingVehicule(sınıf mirası) olduğunu söyleyebilirler, ancak bunun yerine Airplanebir Flyingbileşeni (kompozisyonu) ve Airplanebir 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ı Ayoksa beste Bmi olmalı ?

Bunun Abir uzmanlığı Liskov ikame ilkesiniB yerine getirecekse , kalıtım uygulanabilir, hatta arzu edilir. Geçerli bir ikame olmadığı durumlar varsa, miras kullanmamalıyız.AB

Türetilmiş sınıfı savunmak için bir tür savunmacı programlama biçimi olarak kompozisyonla ilgilenebiliriz . Özellikle, Bbaş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. BGeçersiz bir duruma yol açabilecek yöntemleri ortaya çıkarabilecek risk varsa A, miras yerine kompozisyon kullanmalıyız. Her ikisinin de yazarı olsak Bve Aendişelenmemiz gereken bir şey daha az olsa bile , kompozisyon yeniden kullanılabilirliği kolaylaştırır B.

Hatta varsa features iddia edebilir Bo Aihtiyacı yoktur (ve bu özellikler için geçersiz bir durumda neden olabilir eğer biz bilmiyoruz Amevcut 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 Cmiras alınmaz B. Bunun yerine sınıfınızda Ctürden Abir nesne olabilir (veya olmayabilir) veya türünde bir nesne olabilir B. Bu şekilde, uygulama detaylarına Bkarşı değil, arayüzün (of) Asunduğ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.


Uh ... .Net çerçevesi, akışla ilgili işi yapmak için çeşitli miras alınan akışları kullanır. İnanılmaz derecede iyi çalışıyor ve kullanımı ve genişletilmesi süper kolay.
T. Sar

1
@TSar "kullanımı ve genişletilmesi çok kolay" Hiç standart çıkış akışını Debug'a aktarmayı denediniz mi? Bu onların akışlarını genişletmeye çalışan tek deneyimimdi. Kolay değildi. Sınıftaki her yöntemi açıkça geçersiz kılan bir kabustu. Son derece tekrarlayan. Ve .NET kaynak koduna bakarsanız, her şeyi açıkça geçersiz kılmak neredeyse bunu nasıl yaparlar. Bu yüzden uzatmanın o kadar kolay olduğuna ikna olmadım.
jpmc26

Bir akıştan bir yapıyı okumak ve yazmak, serbest işlevler veya multimethods ile daha iyi yapılır.
user253751

@ jpmc26 Üzgünüz, deneyiminiz çok iyi değildi. Bununla birlikte, farklı şeyler için akışla ilgili şeyler kullanırken veya uygularken hiçbir sorunum olmadı.
T. Sar

3

Normalde Kompozisyon-Devralmanın itici fikri, daha fazla tasarım esnekliği sağlamak ve bir değişikliği yaymak için değiştirmeniz gereken kod miktarını azaltmamaktır (şüpheli bulduğum).

Üzerinde örnek wikipedia onun yaklaşımının gücünün daha iyi bir örnek vermek:

Her bir "kompozisyon parçası" için bir temel arayüz tanımlayarak, yalnızca kalıtımla ulaşılması zor olabilecek bir çeşitlilikle çeşitli "birleşik nesne" oluşturabilirsiniz.


Yani bu bölüm bir Kompozisyon örneği veriyor, değil mi? Soruyorum çünkü makalede açık değil.
Utku

1
Evet, "Miras üzerinden kompozisyon", arayüzünüz olmadığı anlamına gelmez, Player nesnesine bakarsanız, bunun bir hiyerarşiye iyi uyduğunu görebilirsiniz, ancak (vahşet mazereti) kodunda mirastan gelmiyor, ama bu işi yapan bir sınıfla kompozisyondan
lbenini

2

"Kompozisyonu kalıtım yerine tercih et" çizgisi doğru yönde bir dürtmeden başka bir şey değildir. Sizi kendi aptallığınızdan kurtaramaz ve aslında işleri daha da kötüleştirme şansı verir. Çünkü burada çok fazla güç var.

Bunun bile söylenmesinin ana nedeni programcıların tembel olmalarıdır. O kadar tembeldir ki daha az klavye yazımı anlamına gelen herhangi bir çözümü alacaklardır. Mirasın ana satış noktası budur. Bugün daha az yazıyorum ve yarın başka biri mahvoluyor. Ne yani, eve gitmek istiyorum.

Ertesi gün patron kompozisyon kullanmamda ısrar ediyor. Nedenini açıklamıyor ama üzerinde ısrar ediyor. Bu yüzden miras aldığım şeyin bir örneğini alıyorum, miras aldığımla aynı arayüzü açıyorum ve tüm çalışmaları miras aldığım şeye devrederek bu arayüzü uyguluyorum. Tamamen beyin ölü yazarak bir yeniden düzenleme aracıyla yapılabilirdi ve bana anlamsız geliyor ama patron bunu istedi, ben de yaptım.

Ertesi gün istediğim bu mu diye soruyorum. Patron ne diyor?

Bazılarının (çalışma zamanında) devralınan şeyi (durum modeline bakın) dinamik olarak değiştirebilmesi gerekmedikçe, bu çok büyük bir zaman kaybıdır. Patron bunu nasıl söyleyebilir ve miras üzerine kompozisyon lehine olabilir mi?

Buna ek olarak, yöntem imza değişikliklerinden dolayı kırılmayı önlemek için bir şey yapmamak, bileşimin en büyük avantajını tamamen kaçırır. Kalıtımdan farklı olarak farklı bir arayüz oluşturabilirsiniz . Bu seviyede nasıl kullanılacağına bir tane daha uygun. Bilirsin, soyutlama!

Kalıtımın bileşimi ve yetki devriyle (ve bazı dezavantajlarıyla) otomatik olarak değiştirilmesinin bazı küçük avantajları vardır, ancak bu işlem sırasında beyninizi kapalı tutmanın büyük bir fırsatı yoktur.

Dolaylı aktarım çok güçlüdür. Akıllıca kullanın.


0

İşte başka bir neden: birçok OO dilinde (burada C ++, C # veya Java gibi olanları düşünüyorum), bir nesne oluşturduktan sonra sınıfını değiştirmenin bir yolu yoktur.

Bir çalışan veritabanı oluşturduğumuzu varsayalım. Soyut bir temel Çalışan sınıfımız var ve belirli roller için yeni sınıflar türetmeye başlıyoruz - Mühendis, Güvenlik Görevlisi, Kamyon Sürücüsü vb. Her sınıfın bu rol için özel davranışı vardır. Herşey iyi.

Sonra bir gün bir Mühendis Mühendislik Müdürlüğüne terfi eder. Mevcut bir Mühendis'i yeniden sınıflandıramazsınız. Bunun yerine, bir Mühendislik Yöneticisi oluşturan bir işlev yazmanız, Mühendis'deki tüm verileri kopyalamanız, Mühendis'i veritabanından silmeniz ve ardından yeni Mühendislik Yöneticisi'ni eklemeniz gerekir. Ve olası her rol değişikliği için böyle bir işlev yazmalısınız.

Daha da kötüsü, bir Kamyon Sürücüsünün uzun süreli hastalık iznine gittiğini ve bir Güvenlik Görevlisinin Kamyon Sürüşü'nü doldurmak için yarı zamanlı yapmayı önerdiğini varsayalım. Şimdi onlar için yeni bir Güvenlik Görevlisi ve Kamyon Sürücüsü sınıfı icat etmelisiniz.

Somut bir Çalışan sınıfı oluşturmak ve iş rolü gibi özellikler ekleyebileceğiniz bir kapsayıcı olarak muamele etmek hayatı kolaylaştırır.


Ya da Roller Listesine işaret eden bir Employee sınıfı oluşturursunuz ve her gerçek iş Rolü genişletir. Çok fazla iş yapmadan mirasın çok iyi ve mantıklı bir şekilde kullanılması için örnek verilebilir.
T. Sar

0

Kalıtım iki şey sağlar:

1) Kodun yeniden kullanımı: Kompozisyon ile kolayca yapılabilir. Ve aslında, kapsüllemeyi daha iyi koruduğu için kompozisyon yoluyla daha iyi elde edilir.

2) Polimorfizm: "Arabirimler" (tüm yöntemlerin saf sanal olduğu sınıflar) ile gerçekleştirilebilir.

Arabirim dışı kalıtım kullanmak için güçlü bir argüman, gerekli yöntem sayısının fazla olması ve alt sınıfınızın yalnızca küçük bir alt kümesini değiştirmek istediği zamandır. Bununla birlikte, "tek sorumluluk" ilkesine (eğer yapmanız gerekir) abone olursanız, genel olarak arayüzünüzün çok küçük ayak izleri olmalıdır, bu nedenle bu durumlar nadir olmalıdır.

Tüm üst sınıf yöntemlerinin geçersiz kılınmasını gerektiren kodlama stiline sahip olmak, zaten çok sayıda doğrudan geçiş yöntemini yazmaktan kaçınmıyor, bu yüzden neden kodu kompozisyon aracılığıyla yeniden kullanmıyorsunuz ve kapsülleme avantajından faydalanmıyorsunuz? Ayrıca, süper sınıf başka bir yöntem eklenerek genişletilecekse, kodlama stili bu yöntemi tüm alt sınıflara eklemeyi gerektirir (ve o zaman "arayüz ayırma" yı ihlal etme şansımız vardır ). Kalıtım birden çok düzeyde sahip olduğunuzda, bu sorun hızla büyür.

Son olarak, birçok dilde birim kompozisyonu "kompozisyon" temelli nesneler, üye nesneyi bir sahte / test / kukla nesne ile değiştirerek "miras" temelli objeleri birim test etmekten çok daha kolaydır.


0

Miras yerine kompozisyonu tercih etmenin tek nedeni bu mu?

Hayır!

Kompozisyonun miras yerine tercih edilmesinin birçok nedeni vardır. Tek bir doğru cevap yok. Ama karışıma kalıtım üzerinde kompozisyonu tercih etmek için başka bir neden atacağım:

Kompozisyonun miras üzerine tercih edilmesi, kodunuzun okunabilirliğini artırır , bu da birden fazla kişiyle büyük bir proje üzerinde çalışırken çok önemlidir.

İşte şöyle düşünüyorum: Başka birinin kodunu okurken, bir sınıfı genişlettiklerini görürsem , süper sınıfta hangi davranışın değiştiğini soracağım ?

Ve sonra kodlarını gözden geçireceğim, geçersiz kılınan bir işlev arıyorum. Eğer hiç görmezsem, bunu mirasın kötüye kullanılması olarak sınıflandırırım. Sadece beni (ve takımdaki diğer her kişiyi) kodlarını okuduktan 5 dakika sonra kurtarmak için kompozisyon kullanmalılardı!

Somut bir örnek: Java'da JFramesınıf bir pencereyi temsil eder. Bir pencere oluşturmak için, bir örnek oluşturur JFrameve daha sonra bu örneğe bir şeyler eklemek ve onu göstermek için işlevleri çağırırsınız.

Birçok öğretici (resmi olanlar bile) JFramesınıfınızın örneklerini örnek olarak ele alabilmeniz için genişletmenizi önerir JFrame. Ama imho (ve başkalarının görüşü), bunun içine girmek için oldukça kötü bir alışkanlıktır! Bunun yerine, yalnızca bir örnek oluşturmanız JFrameve bu örnekte işlev çağırmanız gerekir. JFrameÜst sınıfın varsayılan davranışlarından hiçbirini değiştirmediğiniz için bu örnekte genişletmeye gerek yoktur .

Öte yandan, özel boyama yapmanın bir yolu JPanelsınıfı genişletmek ve paintComponent()işlevi geçersiz kılmaktır . Bu kalıtımın güzel bir kullanımıdır. Kodunuza bakıyorum JPanelve paintComponent()işlevi genişletip geçersiz kıldığınızı görüyorsanız, tam olarak hangi davranışı değiştirdiğinizi bileceğim.

Sadece kendiniz kod yazıyorsanız, kompozisyon kullanmak kadar kalıtım kullanmak da kolay olabilir. Ancak grup ortamında olduğunuzu ve diğer kişilerin kodunuzu okuyacağını düşünün. Kompozisyonun miras yerine tercih edilmesi kodunuzun okunmasını kolaylaştırır.


Kalıtım yerine kompozisyon kullanabilmeniz için bir nesne örneğini döndüren tek bir yöntemle tüm fabrika sınıfını eklemek, kodunuzu gerçekten daha okunabilir yapmaz ... (Evet, her seferinde yeni bir örneğe ihtiyacınız varsa buna ihtiyacınız vardır) belirli bir yöntem denir.) Bu, kalıtımın iyi olduğu anlamına gelmez, ancak kompozisyon her zaman okunabilirliği geliştirmez.
jpmc26

1
@ jpmc26 ... neden yeni bir sınıf örneği oluşturmak için neden bir fabrika sınıfına ihtiyacınız var? Ve kalıtım, tanımladığınız şeyin okunabilirliğini nasıl geliştirir?
Kevin Workman

Yukarıdaki yorumda size böyle bir fabrika için açık bir neden vermedim mi? Okunması daha az kodla sonuçlandığı için daha okunabilir . Aynı davranışı temel sınıftaki tek bir soyut yöntemle ve birkaç alt sınıfla gerçekleştirebilirsiniz. (Her durumda, her biri için kendi oluşturduğunuz sınıfın farklı bir uygulamasına sahip olacaktınız.) Nesneyi oluşturmanın ayrıntıları temel sınıfa güçlü bir şekilde bağlıysa, kodunuzda başka hiçbir yerde çok fazla kullanım bulamaz ve daha da azalır. ayrı bir sınıf yapmanın faydaları. Daha sonra kodun ilgili kısımlarını birbirine yakın tutar.
jpmc26

Söylediğim gibi, bu kalıtımın iyi olduğu anlamına gelmez, ancak kompozisyonun bazı durumlarda okunabilirliği nasıl geliştirmediğini gösterir.
jpmc26

1
Belirli bir örnek görmeden gerçekten yorum yapmak zordur. Ama tarif ettiğiniz şey bana aşırı mühendislik gibi geliyor. Bir sınıf örneğine mi ihtiyacınız var? newAnahtar kelime sana bir tane verene. Her neyse tartışma için teşekkürler.
Kevin Workman
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.