Neden Java Kalıtımından “Genişletilmiş”


40

Jame Gosling dedi.

“Mümkün olduğunda uygulama devralmasından kaçınmalısınız.”

ve bunun yerine, arabirim devralmayı kullanın.

Ama neden? "Extends" anahtar sözcüğünü kullanarak bir nesnenin yapısını devralmayı nasıl önleyebiliriz ve aynı zamanda kodumuzu Nesne Yönelimli yapabilir mi?

Birisi lütfen bu kavramı “bir kitapçıda kitap sipariş etmek” gibi bir senaryoda gösteren Nesneye Yönelik bir örnek verebilir mi?



Sorun şu ki, sadece bir sınıfı uzatabilirsiniz .

Yanıtlar:


38

Gosling, extend anahtar sözcüğünü kullanmaktan kaçınmak istemedi ... sadece kalıtımın tekrar kod kullanımı için bir araç olarak kullanmadığını söyledi.

Kalıtım başlı başına kötü değildir ve OO yapılarının oluşturulmasında kullanılacak çok güçlü (gerekli) bir araçtır. Ancak, doğru kullanılmadığında ( Nesne yapıları oluşturmaktan başka bir şey için kullanıldığında ), çok sıkı bir şekilde bağlanmış ve bakımı çok zor olan bir kod oluşturur.

Kalıtım, polimorfik yapılar oluşturmak için kullanılması gerektiğinden, arayüzler ile kolayca yapılabilir, dolayısıyla Nesne Yapılarınızı tasarlarken sınıflar yerine arayüzleri kullanma ifadesi. Kendinizi kullanarak bulursanız, bir nesneden diğerine kopya yapıştırma kodundan kaçınmanın bir yolu olarak kullanabilirsiniz, belki de yanlış kullanıyorsunuzdur ve bu işlevselliği idare etmek ve bunun yerine kompozisyonu paylaşmak için uzmanlaşmış bir sınıfla daha iyi hizmet görürsünüz.

Liskov Değiştirme Prensibi hakkında bilgi edinin ve anlayın. Bir sınıfı (veya bununla ilgili bir ara yüzü) genişletmek üzereyken, prensibi ihlal edip etmediğinizi kendinize sorun, eğer evet ise, büyük olasılıkla (ab) mirası doğal olmayan şekillerde kullanmaktasınız. Yapması kolaydır ve çoğu zaman doğru şey gibi görünür, ancak kodunuzu sürdürmek, test etmek ve geliştirmek zorlaştırır.

--- Düzenle Bu cevap , soruyu daha genel anlamda cevaplamak için oldukça iyi bir argüman yapar.


2
(Uygulama) miras, OO yapıları oluşturmak için gerekli değildir . Onsuz bir OO diline sahip olabilirsiniz.
Andres F.

Bir örnek verebilir misiniz? Kalıtım olmadan OO'yu hiç görmedim.
Newtopian

1
Sorun, OOP'nin ne olduğu konusunda kabul edilmiş bir tanım bulunmamasıdır. (Bir yana, Alan Kay bile “mesajlar” yerine “nesneler” e vurgu yaparak tanımının ortak yorumuna pişman oldu.) İşte sorunuza bir cevap . OOP'yi kalıtım olmadan görmenin nadir olduğu konusunda hemfikirim; bu yüzden benim tartışmam sadece zorunlu değil ;)
Andres F.

9

Nesneye yönelik programlamanın ilk günlerinde, uygulama mirası yeniden kodlamanın altın çekiçiydi. İhtiyacınız olan bir sınıfta bir işlevsellik parçası olsaydı, yapmanız gereken şey genel olarak o sınıftan devralmaktı (bunlar C ++ 'ın Java'dan daha yaygın olduğu günlerdi) ve bu işlevselliği “ücretsiz” olarak aldınız.

Ancak, gerçekten özgür değildi. Sonuç, ele alınması zor olan elmas şeklindeki miras ağaçları da dahil olmak üzere neredeyse anlaşılması imkansız olan, oldukça karmaşık kalıtım hiyerarşileriydi. Bu, Java tasarımcılarının çoklu sınıf mirasını desteklememeyi tercih etmelerinin bir parçası.

Gosling'in tavsiyesi (ve diğer ifadeleri gibi) oldukça ciddi bir hastalık için güçlü bir ilaçtı ve bazı bebeklerin banyo suyuyla atılması mümkün. Gerçek olan sınıflar arasındaki ilişkiyi modellemek için miras kullanımında yanlış bir şey yoktur is-a. Ancak, yalnızca başka bir sınıftan bir işlevsellik parçası kullanmak istiyorsanız, önce diğer seçenekleri araştırmaktan daha iyi olursunuz.


6

Kalıtım, son zamanlarda oldukça korkunç bir üne kavuşmuştur. Bunu engellemenin bir nedeni, temel olarak insanları bir kod yazmamak için gerçek ilişki olmadığı durumlarda miras kullanmamaya teşvik eden "mirasa ilişkin kompozisyon" tasarım ilkesi ile birliktedir. Bu tür bir miras, bazılarının bulunması zor ve hataların giderilmesi zor olabilir. Arkadaşınızın muhtemelen bir arayüz kullanmayı önermesinin nedeni, arayüzlerin dağıtılmış api'lere daha esnek ve sürdürülebilir bir çözüm sağlamasıdır. Daha sık olarak, kullanıcı kodunu bozmadan bir api'de değişiklik yapılmasına izin verir, burada sınıfları ifşa etmek genellikle kullanıcının kodunu kırar. Net'te buna en iyi örnek, List ile IList arasındaki kullanım farkıdır.


5

Çünkü birçok arayüzü uygularken sadece bir sınıfı uzatabilirsiniz.

Bir zamanlar kalıtımın ne olduğunuzu tanımladığını duydum, ancak arayüzler oynayabileceğiniz bir rol tanımladı.

Arayüzler ayrıca daha iyi polimorfizm kullanımına izin verir.

Bu sebebin bir parçası olabilir.


neden aşağı oy?
Mahmud Hossam

6
Cevap arabayı attan önce koyar. Java, eklemlenmiş olan Gosling (Java tasarımcısı) ilkesi nedeniyle yalnızca bir sınıfı genişletmenize izin verir. Çocukların bisiklet sürerken kask takmaları gerektiğini söylemek gibi bir şey çünkü kanun bu. Bu doğru, ancak hem yasa hem de tavsiye, kafa travmalarından kaçınılmasından kaynaklanıyor.
JohnMcG

@JohnMcG Yaptığım tasarım seçimini açıklamıyorum Gosling, sadece bir kodlayıcıya tavsiye veriyorum, kodlayıcılar genellikle dil tasarımı ile uğraşmıyor.
Mahmoud Hossam

1

Bunu ben de öğrendim ve mümkün olduğunda arayüzleri tercih ediyorum (tabii ki hala mantıklı olan kalıtımı kullanıyorum).

Yaptığımı düşündüğüm bir şey, kodunuzu belirli uygulamalardan ayırmak. Diyelim ki ConsoleWriter adında bir sınıfım var ve bu bir şeyi yazmak için bir yönteme geçiyor ve konsola yazıyor. Şimdi bir GUI penceresine yazdırmak istediğimi söyleyelim. Şimdi ya yöntemi değiştirmem ya da GUIWriter'ı parametre olarak alan yeni bir tane yazmam gerekiyor. Eğer bir IWriter arayüzünü tanımlayarak başlasaydım ve metodu bir IWriter'a dahil ettiysem, ConsoleWriter (IWriter arayüzünü uygulayacak) ile başlayabilir ve daha sonra GUIWriter (IWriter arayüzünü uygulayan) adında yeni bir sınıf yazabilir ve sonra I sadece geçen sınıfı değiştirmek zorunda kalacaktı.

Başka bir şey (C # için doğrudur, Java hakkında emin değilim) sadece 1 sınıfı uzatabilirsin ama birçok arayüz uygulayabilirsin. Diyelim ki, Teacher, MathTeacher ve HistoryTeacher adında bir sınıfım vardı. Şimdi, MathTeacher ve HistoryTeacher, Öğretmen'den genişler ancak hem bir MathTeacher hem de HistoryTeacher olan birini temsil eden bir sınıf istiyorsak. Bir seferde yalnızca bir tane yapabildiğiniz zaman, birden fazla sınıftan miras almayı denediğinizde oldukça karışık olabilir (bir yol vardır ancak bunlar tam olarak optimum değildir). Arayüzlerle, IMathTeacher ve IHistoryTeacher adında 2 arayüze sahip olabilirsiniz ve ardından öğretmenden uzanan ve bu 2 arayüzü uygulayan bir sınıfa sahip olabilirsiniz.

Arayüzleri kullanmanın bir dezavantajı, bazen insanların yinelenen kodu görmemleridir (çünkü her sınıfı için uygulamayı uygulayan arayüzü oluşturmak zorundasınız) ancak bu soruna temiz bir çözüm var, örneğin delegeler gibi şeylerin kullanımı (emin değil) Java buna eşdeğerdir).

Miras alanlarındaki arayüzleri kullanmanın en büyük sebebi uygulama kodunun çözülmesidir ancak kalıtımın hala çok faydalı olduğu için kötülük olduğunu düşünmeyin.


2
"Yalnızca 1 sınıfı uzatabilir, ancak birçok arabirim uygulayabilirsiniz" Bu, C # yanı sıra Java için de geçerlidir.
SinirliFormsDesigner ile

Kod çoğaltma sorununa olası bir çözüm, arabirimi uygulayan ve bunu genişleten soyut bir sınıf oluşturmaktır. Yine de dikkatli kullanın; Mantıklı olduğunu gördüğüm birkaç vaka var.
Michael K,

0

Bir sınıftan miras alırsanız, sadece o sınıfa bağımlılık duymazsınız, aynı zamanda o sınıfın müşterileri tarafından yaratılan her türlü sözleşmeyi de onurlandırmanız gerekir. Bob amca subtlely ihlal çok kolay nasıl büyük bir örnek sağlar Liskov değiştirme İlke temel kullanılarak Meydanı'nı Dikdörtgen uzanır ikilem. Arayüzler, uygulamaları olmadığından gizli sözleşmeleri de yoktur.


2
Bir nitpicking: Circle-elips problemi , sadece saf arayüzler kullanılsa bile, nesneler değiştirilebilirse bile ortaya çıkacaktır.
rwong
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.