Neden kalıtım ve polimorfizm bu kadar yaygın kullanılıyor?


18

İşlevsel programlama gibi farklı programlama paradigmaları hakkında ne kadar çok şey öğrenirsem, kalıtım ve polimorfizm gibi OOP kavramlarının bilgeliğini sorgulamaya başlarım. İlk olarak okulda kalıtım ve polimorfizm hakkında bilgi sahibi oldum ve o zaman polimorfizm, kolay genişletilebilirliğe izin veren jenerik kod yazmanın harika bir yolu gibi görünüyordu.

Ancak ördek yazması (hem dinamik hem de statik) ve daha üst düzey işlevler gibi işlevsel özellikler karşısında, miras ve polimorfizme, nesneler arasındaki kırılgan bir ilişki kümesine dayanan gereksiz bir kısıtlama getirmeye bakmaya başladım. Çok biçimliliğin arkasındaki genel fikir, bir kez bir işlev yazmanız ve daha sonra orijinal işlevi değiştirmeden programınıza yeni işlevsellik ekleyebilmenizdir - tek yapmanız gereken gerekli yöntemleri uygulayan başka bir türetilmiş sınıf oluşturmaktır.

Ancak, ister Python gibi dinamik bir dilde, ister C ++ gibi statik bir dilde olsun, ördek yazarak başarmak çok daha kolaydır.

Örnek olarak, aşağıdaki Python işlevini ve ardından statik C ++ eşdeğerini göz önünde bulundurun:

def foo(obj):
   obj.doSomething()

template <class Obj>
void foo(Obj& obj)
{
   obj.doSomething();
}

OOP eşdeğeri aşağıdaki Java kodu gibi bir şey olacaktır:

public void foo(DoSomethingable obj)
{
  obj.doSomething();
}

En büyük fark, elbette, Java sürümünün çalışması için bir arayüz veya miras hiyerarşisi oluşturulmasını gerektirmesidir. Java sürümü böylece daha fazla çalışma gerektirir ve daha az esnektir. Ayrıca, gerçek dünyadaki miras hiyerarşilerinin bir miktar dengesiz olduğunu görüyorum. Hepimiz Şekiller ve Hayvanlar'ın örneklerini gördük, ancak gerçek dünyada iş gereksinimleri değiştikçe ve yeni özellikler eklendiğinden, "is-a" ilişkisini gerçekten germek zorunda kalmadan önce herhangi bir iş yapmak zor yeni gereksinimleri karşılamak için başka temel sınıflar veya arayüzler içerecek şekilde hiyerarşinizi yeniden yapılandırın / yeniden düzenleyin. Ördek yazarken, hiçbir şey modelleme konusunda endişelenmenize gerek yok - sadece ihtiyacınız olan işlevsellik hakkında endişeleniyorsunuz .

Yine de, kalıtım ve polimorfizm o kadar popüler ki, onlara genişletilebilirlik ve kodun yeniden kullanımı için baskın strateji demenin çok abartılı olacağından şüpheliyim. Peki neden kalıtım ve polimorfizm bu kadar çılgınca başarılı? Miras / polimorfizmin ördek tipine göre bazı ciddi avantajlarını görmüyor muyum?


Ben bir Python uzmanı değilim bu yüzden sormak zorunda: objbir doSomethingyöntemi yoksa ne olurdu ? Bir istisna oluştu mu? Hiçbir şey olmuyor mu?
Sinirli

6
@Frustrated: Çok açık, özel bir istisna ortaya çıkıyor.
dsimcha

11
Ördek yazarak sadece polimorfizm 11'e kadar götürülmez mi? Benim tahminim, OOP ile değil, statik olarak yazılmış enkarnasyonları ile sinirli olmanızdır. Bunu gerçekten anlayabiliyorum ama bu bir bütün olarak OOP'yi kötü bir fikir yapmıyor.

3
Önce "yaygın" "çılgınca" olarak okuyun ...

Yanıtlar:


22

Çoğunlukla sana katılıyorum, ama eğlence için Devil's Advocate'i oynayacağım. Açık arayüzler , açıkça, resmi olarak belirlenmiş bir sözleşmeyi aramak için tek bir yer verir ve size bir türün ne yapması gerektiğini söyler. Bu, bir projedeki tek geliştirici olmadığınızda önemli olabilir.

Ayrıca, bu açık arayüzler ördek tiplemesinden daha verimli bir şekilde uygulanabilir. Sanal işlev çağrısı normal bir işlev çağrısından çok daha fazla ek yüke sahiptir, ancak satır içi olamaz. Ördek yazmanın önemli bir yükü vardır. C ++ tarzı yapısal yazım (şablonlar kullanarak) çok miktarda nesne dosyası bloat üretebilir (çünkü her örnekleme nesne dosyası düzeyinde bağımsızdır) ve zamanında derleme değil, zamanında polimorfizm gerektiğinde çalışmaz.

Alt satır: Java tarzı kalıtım ve polimorfizmin bir PITA olabileceğini ve alternatiflerin daha sık kullanılması gerektiğini kabul ediyorum, ancak yine de avantajları var.


29

Kalıtım ve polimorfizm, belirli türdeki programlama problemleri için çalıştıkları için yaygın olarak kullanılmaktadır .

Okullarda yaygın olarak öğretildikleri için değil, geriye doğru: okullarda yaygın olarak öğretiliyorlar çünkü insanlar (pazar olarak da bilinir) eski araçlardan daha iyi çalıştıklarını keşfettiler ve böylece okullar onlara öğretmeye başladı. [Anekdot: OOP'yi ilk öğrendiğimde, herhangi bir OOP dili öğreten bir üniversite bulmak son derece zordu. On yıl sonra, OOP dili öğretmeyen bir üniversite bulmak zordu.]

Dedin:

Çok biçimliliğin arkasındaki genel fikir, bir kez bir işlev yazmanız ve daha sonra orijinal işlevi değiştirmeden programınıza yeni işlevsellik ekleyebilmenizdir - tek yapmanız gereken gerekli yöntemleri uygulayan başka bir türetilmiş sınıf oluşturmaktır

Diyorum:

Hayır, değil

Açıkladığınız şey polimorfizm değil, kalıtımdır. OOP problemleri yaşamanıza şaşmamalı! ;-)

Bir adımı yedekleyin: polimorfizm mesaj geçişinin bir yararıdır; basitçe her nesnenin bir mesaja kendi yolunda yanıt vermekte özgür olduğu anlamına gelir.

Öyleyse ... Ördek Yazma , polimorfizmdir

Sorunuzun özü, OOP'yi anlamadığınız veya beğenmediğiniz değil, arayüzleri tanımlamaktan hoşlanmamanız gibi görünüyor . Bu iyi ve dikkatli olduğunuz sürece her şey yolunda gidecek. Dezavantajı, bir hata yaptıysanız - örneğin bir yöntemi atladıysanız - çalışma zamanına kadar öğrenemeyeceğinizdir.

Bu, Stap'a karşı dinamik bir şey, Lisp kadar eski ve OOP ile sınırlı değil.


2
"Ördek yazarak için 1 olan polimorfizmi". Polimorfizm ve kalıtım bilinçaltında çok uzun süre birlikte zincirlenmiştir ve katı statik tipleme, olmaları gereken tek gerçek nedendir.
cHao

+1. Statik ve dinamik ayrımın bir yan nottan daha fazlası olması gerektiğini düşünüyorum - ördek tipi ve java tarzı polimorfizm arasındaki ayrımın özünde.
Sava B.

8

Miras ve polimorfizme, nesneler arasındaki kırılgan ilişkilere dayanan gereksiz bir kısıtlama getirmeye bakmaya başladım.

Neden?

Kalıtım (ördekle yazarak veya yazmadan), ortak bir özelliğin yeniden kullanılmasını sağlar. Yaygınsa, alt sınıflarda sürekli olarak yeniden kullanılmasını sağlayabilirsiniz.

Bu kadar. "Gereksiz kısıtlama" yoktur. Bu bir basitleştirme.

Polimorfizm de benzer şekilde, "ördek tiplendirme" nin anlamıdır. Aynı yöntemler. Aynı arayüze sahip birçok sınıf, ancak farklı uygulamalar.

Bu bir kısıtlama değil. Bu bir basitleştirme.


7

Kalıtım istismar edilir, ancak ördek yazmak da öyle. Her ikisi de sorun yaratabilir ve yapabilir.

Güçlü yazma ile derleme zamanında birçok "birim testi" yapılır. Ördek yazarken genellikle bunları yazmak zorundasınız.


3
Test hakkındaki yorumunuz yalnızca dinamik ördek yazımı için geçerlidir. C ++ tarzı statik ördek yazarak (şablonlarla) derleme zamanında size hata verir. (Her ne kadar, verilen C ++ şablon hatalarının deşifre edilmesi oldukça zordur, ancak bu tamamen farklı bir konudur.)
Channel72

2

Okulda şeyler öğrenme konusunda iyi bir şey olduğunu yapmak onları öğrenir. O kadar da iyi olmayan şey, ne zaman yararlı olduklarını ve ne zaman yararlı olduklarını anlamadan onları biraz dogmatik olarak kabul edebilmenizdir.

Eğer dogmatik olarak öğrendiyseniz, daha sonra tıpkı diğer yönde dogmatik olarak “isyan” edebilirsiniz. Bu da iyi değil.

Bu tür fikirlerde olduğu gibi, pragmatik bir yaklaşım benimsemek en iyisidir. Nereye uydukları ve nereye uymadıkları hakkında bir anlayış geliştirin. Ve satıldıkları tüm yolları görmezden gelin.


2

Evet, statik yazım ve arabirimler kısıtlamadır. Fakat yapılandırılmış programlama icat edildiğinden beri her şey (yani "zararlı olarak kabul edilir") bizi kısıtlamakla ilgili. Bob Amca , video blogunda bunu mükemmel bir şekilde ele alıyor .

Şimdi, daralmanın kötü olduğu iddia edilebilir, ancak öte yandan çok karmaşık bir konuya düzen, kontrol ve aşinalık getirir.

Tarafından kısıtlamaları Rahatlatıcı (yeniden) dinamik yazarak ve hatta doğrudan bellek erişimi tanıtan olduğu çok güçlü bir kavram, ama aynı zamanda zor birçok programcı başa kolaylaştırabilirsiniz. Özellikle programcılar derleyicilerine güveniyorlardı ve çalışmalarının çoğu için güvenlik türünü kullanıyorlardı.


1

Kalıtım iki sınıf arasında çok güçlü bir ilişkidir. Java'nın daha güçlü olduğunu sanmıyorum. Bu nedenle, sadece kastettiğinizde kullanmalısınız. Toplumsal kalıtım, bir "genellikle-a" değil, "bir-is" ilişkisidir. Devralmayı aşırı kullanmak ve bir karmaşa ile sarmak gerçekten, gerçekten çok kolay. Birçok durumda, kalıtım "has-a" ya da "a-işlevinden-alır" işlevini temsil etmek için kullanılır ve bu genellikle kompozisyon tarafından daha iyi yapılır.

Çok biçimlilik "is-a" ilişkisinin doğrudan bir sonucudur. Türetilmiş Tabandan miras alırsa, Türetilmiş her "--" Taban'dır ve bu nedenle bir Baz kullandığınız her yerde Türetilmiş kullanabilirsiniz. Bu bir miras hiyerarşisinde anlam ifade etmiyorsa, hiyerarşi yanlıştır ve muhtemelen çok fazla miras devam etmektedir.

Ördek yazmak düzgün bir özelliktir, ancak derleyici kötüye kullanacaksanız sizi uyarmaz. İstisnaları çalışma zamanında ele almak istemiyorsanız, her seferinde doğru sonuçları aldığınızdan emin olmanız gerekir. Sadece statik bir miras hiyerarşisi tanımlamak daha kolay olabilir.

Statik yazmanın gerçek bir hayranı değilim (genellikle erken optimizasyonun bir formu olduğunu düşünüyorum), ancak bazı hata sınıflarını ortadan kaldırıyor ve birçok insan bu sınıfların iyi bir şekilde ortadan kaldırmaya değer olduğunu düşünüyor.

Dinamik yazmayı ve ördek yazmayı statik yazmadan ve tanımlanmış kalıtım hiyerarşilerinden daha iyi seviyorsanız, sorun değil. Bununla birlikte, Java yolunun avantajları vardır.


0

C # 's kapanışlarını ne kadar çok kullanırsam, geleneksel OOP yaptığımı o kadar az fark ettim. Kalıtım, uygulamayı kolayca paylaşmanın tek yoluydu, bu yüzden sık sık aşırı kullanıldığını ve gebe kalma sınırlarının çok ileri itildiğini düşünüyorum.

Eğer iken edebilir genellikle miras ile ne yaparım çoğunu yapmak kapanışları kullanmak, aynı zamanda çirkin olabiliyor.

Temel olarak bu iş için doğru bir durumdur: geleneksel OOP, ona uygun bir modeliniz olduğunda gerçekten iyi çalışabilir ve kapanmadığınız zaman gerçekten iyi çalışabilir.


-1

Gerçek ortada bir yerde yatıyor. C # 4.0 statik olarak yazılan dil "dinamik" anahtar kelime tarafından "ördek yazarak" destekler nasıl seviyorum.


-1

Kalıtım, bir FP perspektifinden gerekçe olsa bile, büyük bir kavramdır; sadece zamandan tasarruf etmekle kalmaz, programınızdaki belirli nesneler arasındaki ilişkiye de anlam katar.

public class Animal {
    public virtual string Sound () {
        return "Some Sound";
    }
}

public class Dog : Animal {
    public override string Sound () {
        return "Woof";
    }
}

public class Cat : Animal {
    public override string Sound () {
        return "Mew";
    }
}

public class GoldenRetriever : Dog {

}

İşte sınıfı GoldenRetrieveraynı sahiptir Soundolarak Dogmiras serbest teşekkür yıllardan için.

Farkı görmeniz için Haskell seviyemle aynı örneği yazacağım

data Animal = Animal | Dog | Cat | GoldenRetriever

sound :: Animal -> String
sound Animal = "Some Sound"
sound Dog = "Woof"
sound Cat = "Mew"
sound GoldenRetriever = "Woof"

Burada size belirtmek zorunda kaçış yok soundiçin GoldenRetriever. Genel olarak en kolay şey

sound GoldenRetriever = sound Dog

ama 20 işlevin varsa imagen! Haskell uzmanı varsa lütfen bize daha kolay bir yol gösterin.

Bununla birlikte, mevcut örnek uygulama yoksa, bir işlevin temel sınıf için varsayılan olacağı yerde, desen eşleştirme ve kalıtımın olması harika olurdu.


5
Yemin ederim, AnimalOOP'nin anti-öğreticisidir.
Telastyn

@Telastyn Örneği Youtube'daki Derek Banas'tan öğrendim. Harika öğretmen. Bence görselleştirmek ve bu konuda mantık yürütmek çok kolay. Yayını daha iyi bir örnekle düzenlemekte özgürsünüz.
Cristian Garcia

bu önceki 10 cevap üzerine önemli bir şey eklemiyor gibi görünüyor
gnat

@gnat "Madde" zaten dışarıdayken, konuyla ilgili yeni bir kişi daha kolay anlaşılır bir örnek bulabilir.
Cristian Garcia

2
Hayvanlar ve şekiller, bu sitede reklam müzesi tartışılan çeşitli nedenlerle polimorfizmin gerçekten kötü uygulamalarıdır. Temel olarak, bir kedinin "hayvan" olduğunu söylemek anlamsızdır, çünkü somut bir "hayvan" yoktur. Modellemek için gerçekten kastettiğin şey kimlik değil davranıştır. Bir miyav ya da ağaç kabuğu olarak uygulanabilecek bir "CanSpeak" arayüzü tanımlamak mantıklıdır. Bir kare ve dairenin uygulayabileceği "HasArea" arabirimini tanımlamak mantıklıdır, ancak genel bir Şekil değildir.
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.