it-swarm.dev

Kod Kokusu: Kalıtım Kötüye Kullanımı

Genel olarak OO topluluğunda “kompozisyonu kalıtım yerine tercih etme” gerektiği kabul edilir. Öte yandan, kalıtım hem polimorfizm hem de her şeyi bir temel sınıfa devretmenin basit ve kısa bir yolunu sağlar. açıkça geçersiz kılınmadıkça ve bu nedenle son derece kullanışlı ve yararlı olmadıkça, Delegasyon genellikle (her zaman olmasa da) ayrıntılı ve kırılgan olabilir.

En açık ve IMHO'nun miras istismarının en kesin işareti Liskov İkame İlkesi ihlalidir. Uygun görünse bile mirasın İş için Yanlış Araç olduğuna dair başka işaretler nelerdir?

52
dsimcha

Sadece işlevsellik elde etmek için miras alırken, büyük olasılıkla kalıtımı kötüye kullanıyorsunuzdur. Bu bir Tanrı Nesnesi yaratılmasına yol açar.

Kalıtımın kendisi, sınıflar arasında gerçek bir ilişki gördüğünüz sürece (Köpek gibi Hayvanı genişletir gibi klasik örnekler gibi) bir sorun değildir ve ebeveyn sınıfına, bazılarına anlam vermeyen yöntemler koymazsınız. çocuklar (örneğin, Animal sınıfına Bark () yöntemi eklemek, Animal'i genişleten bir Fish sınıfında bir anlam ifade etmeyecektir).

Bir sınıfın işlevselliğe ihtiyacı varsa kompozisyon kullanın (belki de işlevsellik sağlayıcısını yapıcıya enjekte etmek?). Bir sınıfın diğeri gibi olması gerekiyorsa, kalıtım kullanın.

48
Fernando

Ben vurulma riskini alarak, Kalıtım kendi başına bir kod kokusu olduğunu söyleyebilirim :)

Kalıtımla ilgili mesele, iki dikey amaç için kullanılabilmesidir:

  • arayüz (polimorfizm için)
  • uygulama (kodun yeniden kullanımı için)

Her ikisini de elde etmek için tek bir mekanizmaya sahip olmak, ilk önce "miras istismarına" yol açan şeydir, çünkü çoğu insan mirasın arayüzle ilgili olmasını bekler, ancak aksi takdirde dikkatli programcılar tarafından bile varsayılan uygulamayı almak için kullanılabilir ( bunu gözden kaçır ...)

Aslında, Haskell veya Go gibi modern diller, her iki endişeyi de ayırmak için mirastan vazgeçti.

İz arkasında:

Bu nedenle, Liskov İlkesinin ihlali en kesin işarettir, çünkü sözleşmenin "arayüz" kısmına uyulmadığı anlamına gelir.

Ancak ara yüzeye saygı duyulsa bile, yöntemlerden biri yararlı olduğu için "yağ" temel sınıflarından miras alan nesneler olabilir.

Bu nedenle Liskov İlkesi kendi başına yeterli değildir, bilmeniz gereken şey polimorfizmin kullanılmış olup olmadığıdır. Değilse, ilk etapta miras almanın pek bir anlamı yoktu, bu bir kötüye kullanım.

Özet:

  • Liskov Principle
  • gerçekten kullanıldığını kontrol et

Etrafında bir yol:

Endişelerin net bir şekilde ayrılması:

  • sadece arayüzlerden devral
  • uygulamayı yetkilendirmek için kompozisyon kullanma

her yöntem için en az birkaç tuşa basmanız gerektiği anlamına gelir ve birdenbire insanlar bu "yağ" sınıfını sadece küçük bir parçası için tekrar kullanmanın harika bir fikir olup olmadığını düşünmeye başlarlar.

39
Matthieu M.

Gerçekten "genel kabul görüyor" mu? Birkaç kez duyduğum bir fikir, ama evrensel olarak tanınan bir ilke. Az önce belirttiğiniz gibi, kalıtım ve polimorfizm OOP'un ayırt edici özellikleridir. Onlar olmadan, aptal sözdizimiyle prosedürel programlamaya sahipsiniz.

Kompozisyon, nesneleri belirli bir şekilde oluşturmak için bir araçtır. Kalıtım, nesneleri farklı bir şekilde oluşturmak için bir araçtır. İkisinde de yanlış bir şey yok; sadece her ikisinin de nasıl çalıştığını ve her stili kullanmaya ne zaman uygun olduğunu bilmeniz gerekir.

14
Mason Wheeler

Kalıtımın gücü tekrar kullanılabilirliktir. Arayüz, veri, davranış, işbirliği. Öznitelikleri yeni sınıflara aktarmak için kullanışlı bir modeldir.

Kalıtım kullanmanın dezavantajı yeniden kullanılabilirliktir. Arayüz, veri ve davranış topluca kapsüllenir ve iletilir, bu da ya hep ya hiç teklifidir. Hiyerarşiler derinleştikçe, özellikle soyutta faydalı olabilecek özellikler daha az hale geldikçe ve yerel düzeydeki özellikleri gizlemeye başladığında, sorunlar özellikle belirginleşir.

Composition nesnesini kullanan her sınıf, onunla etkileşim kurmak için aşağı yukarı aynı kodu yeniden uygulamak zorunda kalacaktır. Bu çoğaltma bazı DRY ihlallerini gerektirse de, tasarımcıların alanlarına en anlamlı olan bir sınıfın özelliklerini seçip seçmeleri için daha fazla özgürlük sağlar. Bu, sınıflar daha uzmanlaştığında özellikle avantajlıdır.

Genel olarak, kompozisyon yoluyla sınıflar oluşturmak ve daha sonra bu sınıfları ortak özelliklerinin büyük bir kısmına sahip olan mirasa dönüştürmek ve farklılıkları kompozisyonlar olarak bırakmak tercih edilmelidir.

IOW, YAGNI (Mirasa ihtiyacınız olmayacak).

5
Huperniketes

A sınıfını B sınıfına alıyorsanız, ancak B 'ın A' dan miras alıp almadığını kimse umursamıyorsa, yanlış yaptığınızın işareti budur .

3
tia

"Kalıtım üzerine iyilik kompozisyonu" iddianın en saçma olduğunu. Her ikisi de OOP'nin temel unsurlarıdır. "Çekiçleri testereleri tercih edin" demek gibi.

Elbette kalıtım kötüye kullanılabilir, herhangi bir dil özelliği kötüye kullanılabilir, koşullu ifadeler kötüye kullanılabilir.

2
Chuck Stephanski

Belki de en belirgin olanı, miras kalan sınıfın hiçbir zaman açıkta kalan arayüzde kullanılmayan bir yöntem/özellik yığını ile sonuçlanmasıdır. Temel sınıfta soyut üyelerin bulunduğu en kötü durumlarda, soyut üyelerin olduğu gibi 'boşlukları doldurmak' için boş (veya anlamsız) uygulamalar bulacaksınız.

Daha ustaca, bazen kodu yeniden kullanma çabasında, benzer uygulamalara sahip iki şeyin bir uygulamada sıkıştığını görüyorsunuz - bu iki şey ilişkili olmamasına rağmen. Bu, kod birleşimini artırma ve benzerliklerin erken tespit edilmediği sürece sadece bir tesadüf olduğu ortaya çıktığında bunları çözme eğilimindedir.

Birkaç örnek ile güncellendi: Her ne kadar doğrudan programlama ile ilgili olmasa da, bu tür şeyler için en iyi örnek yerelleştirme/uluslararasılaştırma ile düşünüyorum. İngilizce'de "evet" için bir kelime vardır. Diğer dillerde, soru ile dilbilgisel olarak anlaşmak için çok, çok daha fazlası olabilir. Birisi diyebilir ki, hadi "evet" için bir dize alalım ve bu birçok dil için iyi. Sonra "evet" dizesinin gerçekten "evet" e karşılık gelmediğini, aslında "bu soruya olumlu cevap" kavramının - her zaman "evet" olmasının tesadüf olduğunu ve sadece zamanın kurtarıldığını fark ederler. bir "evet" değerine sahip olmak, ortaya çıkan karmaşanın çözülmesinde tamamen kaybolur.

Ebeveyn ve çocuk benzer isimlere sahip özelliklere sahip olduğu bir ebeveyn -> çocuk ilişkisinin aslında miras ile modellenmiş olduğu kod tabanımızda bunun özellikle kötü bir örneğini gördüm. Kimse bunu fark etmedi, çünkü her şey çalışıyor gibi görünüyordu, o zaman çocuğun (aslında temel) ve ebeveynin (alt sınıf) farklı yönlere gitmesi gerekiyordu. Şansın olacağı gibi, bu, bir son tarihin ortaya çıktığı zaman tam olarak yanlış zamanda oldu - modeli burada ve burada karıştırmak için (hem mantık hem de kod çoğaltma açısından) derhal çatıdan geçti. Kod, işletmenin bu sınıfların temsil ettiği kavramlar hakkında nasıl düşündüğü veya konuştuğu ile ilgili olmadığından, akıl yürütmek/çalışmak da gerçekten zordu. Sonunda mermiyi ısırmak zorunda kaldık ve orijinal adam aynı değerlere sahip birkaç benzer sondaj özelliğinin aynı kavramı temsil ettiğine karar vermemiş olsaydı, tamamen önlenebilecek bazı büyük yeniden düzenleme yapmak zorunda kaldık.

0
FinnNk

Bence miras ve kompozisyon gibi savaşlar gerçekten sadece daha derin bir çekirdek savaşın unsurları.

Bu mücadele: gelecekteki geliştirmelerde en büyük genişletilebilirlik için en az kodla sonuçlanacak olan nedir?

Bu yüzden soruyu cevaplamak çok zor. Bir dilde kalıtım diğer bir dilde olduğundan daha hızlı bir şekilde kompozisyona yerleştirilebilir ya da tam tersi olabilir.

Ya da kod avantajlarında çözdüğünüz belirli bir sorun, uzun vadeli bir büyüme vizyonundan ziyade yararsızlıktan daha fazla olabilir. Bu bir programlama problemi kadar bir iş problemidir.

Bu nedenle, soruya geri dönmek için, sizi gelecekte bir noktada düz bir ceketi içine sokarsa - veya orada olması gerekmeyen bir kod oluşturuyorsa (başka bir sebepten ötürü başkalarından miras kalan sınıflar) miras yanlış olabilir veya daha da kötüsü, çocuklar için çok sayıda koşullu test yapmaya başlayan temel sınıflar).

Kompozisyonda kalıtımın tercih edilmesi gereken durumlar vardır ve ayrım bir stil meselesinden çok daha net bir şekilde kesilir. Sutter ve Alexandrescu'nun C++ Kodlama Standartları'dan alıntı yapıyorum, çünkü kopyam şu anda işyerinde kitap rafımda. Bu arada, şiddetle tavsiye edilir.

Her şeyden önce, temel ve türetilmiş sınıflar arasında bakım için baş ağrısına neden olabilecek çok samimi, sıkı bir şekilde bir ilişki oluşturduğundan, kalıtım gereksiz olduğunda cesaret kırılır. Kalıtım sözdiziminin okumayı zorlaştırdığı ya da bir şey yapması sadece bir erkeğin görüşü değildir.

Bununla birlikte, türetilmiş sınıfının, temel sınıfın kullanılmakta olduğu bağlamlarda, bu bağlamda kodu değiştirmek zorunda kalmadan yeniden kullanılmasını istediğinizde kalıtım teşvik edilir. Örneğin, if (type1) type1.foo(); else if (type2) type2.foo(); gibi görünmeye başlayan bir kodunuz varsa, muhtemelen mirasa bakmak için çok iyi bir durumunuz vardır.

Özet olarak, yalnızca temel sınıf yöntemlerini yeniden kullanmak istiyorsanız kompozisyon kullanın. Şu anda temel sınıf kullanan kodda türetilmiş bir sınıf kullanmak istiyorsanız, kalıtım kullanın.

0
Karl Bielefeldt