Kalıtım (nesne yönelimli programlama) - Inheritance (object-oriented programming)

Olarak nesne yönelimli programlama , kalıtım , bir dayalı mekanizmasıdır nesne veya sınıf başka bir nesne üzerine ( prototip tabanlı miras ) veya sınıf ( sınıf tabanlı kalıtım benzer istinat) uygulanmasını . Ayrıca , süper sınıf veya temel sınıf gibi mevcut olanlardan yeni sınıflar ( alt sınıflar ) türetme ve daha sonra bunları bir sınıflar hiyerarşisi haline getirme olarak tanımlanır. : En Sınıf temelli nesneye yönelik dillerde, bir nesne, bir "çocuk nesne", tüm özellikleri ve "üst nesnenin" nin davranışları, istisna ile elde miras yoluyla oluşturulan kurucular , yıkıcının, aşırı operatörler ve arkadaşı fonksiyonları arasında temel sınıf. Kalıtım, programcıların mevcut sınıflar üzerine inşa edilmiş sınıflar oluşturmasına, aynı davranışları korurken ( bir arabirim gerçekleştirerek ) yeni bir uygulama belirtmesine , kodu yeniden kullanmasına ve ortak sınıflar ve arabirimler aracılığıyla orijinal yazılımı bağımsız olarak genişletmesine olanak tanır . Nesnelerin veya sınıfların kalıtım yoluyla ilişkileri, yönlendirilmiş bir grafiğin oluşmasını sağlar .

Kalıtım 1969'da Simula için icat edildi ve şimdi Java , C++ , PHP ve Python gibi birçok nesne yönelimli programlama dilinde kullanılmaktadır .

Miras alınan bir sınıf, üst sınıfının veya üst sınıfının bir alt sınıfı olarak adlandırılır . "Devralma" terimi, hem sınıf tabanlı hem de prototip tabanlı programlama için gevşek bir şekilde kullanılır, ancak dar kullanımda, sınıf tabanlı programlama için ayrılmıştır (bir sınıf diğerinden miras alır ), prototip tabanlı programlamada karşılık gelen teknik, bunun yerine delegasyon olarak adlandırılır (bir nesne diğerine delege eder ).

Kalıtım, alt tipleme ile karıştırılmamalıdır . Bazı dillerde kalıtım ve alt tipleme aynı fikirdeyken diğerlerinde farklılık gösterir; genel olarak, alt tipleme bir is-a ilişkisi kurarken , kalıtım yalnızca uygulamayı yeniden kullanır ve sözdizimsel bir ilişki kurar, mutlaka anlamsal bir ilişki kurmaz (kalıtım davranışsal alt tiplemeyi sağlamaz). Bu kavramları ayırt etmek için, alt tiplemeye bazen arayüz kalıtımı denir (tür değişkenlerinin uzmanlaşmasının da bir alt tipleme ilişkisine neden olduğu kabul edilmeden), oysa burada tanımlanan kalıtım , uygulama kalıtımı veya kod kalıtımı olarak bilinir . Yine de kalıtım, alt tür ilişkileri kurmak için yaygın olarak kullanılan bir mekanizmadır.

Kalıtım, bir nesnenin başka bir nesneyi içerdiği (veya bir sınıfın nesnelerinin başka bir sınıfın nesnelerini içerdiği) nesne kompozisyonu ile karşılaştırılır ; kalıtım üzerindeki kompozisyona bakın . Kompozisyon , alt tiplemenin bir ilişkisinin aksine, bir vardır ilişkisini uygular .

Türler

Tek miras
Çoklu kalıtım

Paradigma ve belirli bir dile dayalı çeşitli kalıtım türleri vardır.

Tek miras
alt sınıfların bir üst sınıfın özelliklerini devraldığı yer. Bir sınıf, başka bir sınıfın özelliklerini alır.
Çoklu kalıtım
burada bir sınıfın birden fazla üst sınıfı olabilir ve özellikleri tüm üst sınıflardan devralabilir.

"Çoklu kalıtımın  ... verimli bir şekilde uygulanmasının çok zor olduğu düşünülüyordu. Örneğin, Objective C hakkındaki kitabında C++'ın bir özetinde , Brad Cox aslında C++'a çoklu kalıtım eklemenin imkansız olduğunu iddia etti. Bu nedenle, çoklu kalıtım gibi görünüyordu. 1982 gibi erken bir tarihte çoklu kalıtımı düşündüğüm ve 1984'te basit ve verimli bir uygulama tekniği bulduğum için, meydan okumaya karşı koyamadım.Bunun, modanın olayların sırasını etkilediği tek vaka olduğundan şüpheleniyorum. "

Çok düzeyli kalıtım
burada bir alt sınıf başka bir alt sınıftan miras alınır. "Çok düzeyli kalıtım" şeklinde gösterildiği gibi bir sınıfın başka bir türetilmiş sınıftan türetilmesi nadir değildir.
Çok düzeyli kalıtım
Sınıf A bir olarak hizmet temel sınıf için türetilmiş sınıf B kendisi de bir şekilde karşılık vermektedir, temel sınıf için türetilmiş sınıf C . B sınıfı , A ve C arasındaki kalıtım için bir bağlantı sağladığı için ara temel sınıf olarak bilinir . ABC zinciri , kalıtım yolu olarak bilinir .
Çok düzeyli kalıtımla türetilmiş bir sınıf aşağıdaki gibi bildirilir:
Class A(...);      // Base class
Class B : public A(...);   // B derived from A
Class C : public B(...);   // C derived from B
Bu süreç herhangi bir sayıda seviyeye genişletilebilir.
hiyerarşik miras
Burası, bir sınıfın birden fazla alt sınıf için bir üst sınıf (temel sınıf) görevi gördüğü yerdir. Örneğin, bir üst sınıf A, iki alt sınıf B ve C'ye sahip olabilir. Hem B hem de C'nin üst sınıfı A'dır, ancak B ve C iki ayrı alt sınıftır.
hibrit kalıtım
Hibrit kalıtım, yukarıdaki kalıtım türlerinden iki veya daha fazlasının bir karışımının meydana gelmesidir. Bunun bir örneği, A sınıfının, C ve D olmak üzere iki alt sınıfı olan bir B alt sınıfına sahip olmasıdır. Bu, hem çok düzeyli kalıtımın hem de hiyerarşik kalıtımın bir karışımıdır.

Alt sınıflar ve üst sınıflar

Alt sınıflar , türetilmiş sınıflar , mirasçı sınıflar veya alt sınıflar , bir veya daha fazla dil varlığını bir veya daha fazla sınıftan ( üst sınıf , temel sınıflar veya üst sınıflar olarak adlandırılır) devralan modüler türev sınıflardır . Sınıf mirasının semantiği dilden dile değişir, ancak genellikle alt sınıf, üst sınıflarının örnek değişkenlerini ve üye işlevlerini otomatik olarak devralır .

Türetilmiş bir sınıfı tanımlamanın genel biçimi şöyledir:

class SubClass: visibility SuperClass
{
    // subclass members
};
  • İki nokta üst üste, alt sınıfın üst sınıftan miras aldığını gösterir. Görünürlük isteğe bağlıdır ve varsa özel veya genel olabilir . Varsayılan görünürlük olan özel . Görünürlük, temel sınıfın özelliklerinin özel olarak mı yoksa genel olarak mı türetildiğini belirtir .

Bazı diller, diğer yapıların mirasını da destekler. Örneğin, Eiffel , sözleşmeler bir sınıfın özelliklerini tanımlayan da varisleri tarafından devralınır. Üst sınıf, özelleşmiş alt sınıfların devralabileceği, değiştirebileceği ve tamamlayabileceği ortak bir arabirim ve temel işlevsellik oluşturur. Bir alt sınıf tarafından devralınan yazılım , alt sınıfta yeniden kullanılmış olarak kabul edilir . Bir sınıfın örneğine yapılan bir referans, aslında onun alt sınıflarından birine atıfta bulunuyor olabilir. Başvurulan nesnenin gerçek sınıfı derleme zamanında tahmin edilemez . Bir dizi farklı sınıftaki nesnelerin üye işlevlerini çağırmak için tek tip bir arabirim kullanılır. Alt sınıflar, üst sınıf işlevlerini, aynı yöntem imzasını paylaşması gereken tamamen yeni işlevlerle değiştirebilir .

Alt sınıflanamayan sınıflar

Bazı dillerde , sınıf bildirimine belirli sınıf değiştiricileri eklenerek bir sınıf alt sınıflanamaz olarak bildirilebilir. Örnekler , Java ve C++11'deki anahtar kelimeyi veya C#'daki anahtar kelimeyi içerir. Bu tür değiştiriciler, anahtar kelimeden ve sınıf tanımlayıcısı bildiriminden önce sınıf bildirimine eklenir . Bu tür alt-sınıflandırılamayan sınıflar , özellikle geliştiricilerin kaynak koduna değil , yalnızca önceden derlenmiş ikili dosyalara erişimi olduğunda, yeniden kullanılabilirliği kısıtlar . finalsealedclass

Alt sınıflanamayan bir sınıfın alt sınıfları yoktur, bu nedenle derleme zamanında , o sınıfın nesnelerine yapılan referansların veya işaretçilerin aslında o sınıfın örneklerine atıfta bulunduğu ve alt sınıf örneklerine (mevcut değiller) veya süper sınıf örneklerine atıfta bulunmadığı kolayca çıkarılabilir. ( bir referans türünü yükseltmek , tür sistemini ihlal eder). Başvurulan nesnenin tam türü yürütmeden önce bilindiğinden, birden çok kalıtımın olup olmadığına bağlı olarak bir veya daha fazla sanal yöntem tablosu araması gerektiren geç bağlama ( dinamik gönderme olarak da adlandırılır ) yerine erken bağlama ( statik gönderme olarak da adlandırılır ) kullanılabilir. veya kullanılan programlama dilinde yalnızca tek bir devralma desteklenir.

Geçersiz kılınamayan yöntemler

Tıpkı sınıfların alt-sınıflandırılamaması gibi, yöntem bildirimleri, yöntemin geçersiz kılınmasını (yani, bir alt sınıfta aynı ada ve tip imzasına sahip yeni bir işlevle değiştirilmesini) önleyen yöntem değiştiricileri içerebilir. Bir özel bunun bir üyesi fonksiyonudur sınıfından başka sınıfları tarafından erişilebilir olmadığı için yöntem olup (bu da C ++ için doğru değildir) un geçersiz kılınabilir basitçe. Bir finalJava yöntem, bir sealedC # yöntemi veya bir frozenEiffel özellik geçersiz kılınan olamaz.

sanal yöntemler

Üst sınıf yöntemi sanal bir yöntemse, üst sınıf yönteminin çağrıları dinamik olarak gönderilir . Bazı diller, yöntemlerin özel olarak sanal olarak bildirilmesini gerektirir (örn. C++) ve diğerlerinde tüm yöntemler sanaldır (örn. Java). Sanal olmayan bir yöntemin çağrılması her zaman statik olarak gönderilir (yani işlev çağrısının adresi derleme zamanında belirlenir). Statik gönderim, dinamik gönderimden daha hızlıdır ve satır içi genişletme gibi optimizasyonlara izin verir .

Devralınan üyelerin görünürlüğü

Aşağıdaki tablo, sınıf türetilirken verilen görünürlüğe bağlı olarak hangi değişkenlerin ve işlevlerin devralındığını gösterir.

Temel sınıf görünürlüğü Türetilmiş sınıf görünürlüğü
Genel türetme Özel türetme Korumalı türetme
  • Özel →
  • Korumalı →
  • Genel →
  • Miras alınmadı
  • Korumalı
  • Halk
  • Miras alınmadı
  • Özel
  • Özel
  • Miras alınmadı
  • Korumalı
  • Korumalı

Uygulamalar

Kalıtım, iki veya daha fazla sınıfı birbiriyle ilişkilendirmek için kullanılır.

geçersiz kılma

Yöntemi geçersiz kılmanın resmi

Birçok nesne yönelimli programlama dili, bir sınıfın veya nesnenin, miras aldığı bir yönün (genellikle bir davranışın) yerine getirilmesine izin verir. Bu işleme geçersiz kılma denir . Geçersiz kılma bir karmaşıklığa neden olur: devralınan sınıfın bir örneği davranışın hangi sürümünü kullanır - kendi sınıfının bir parçası mı yoksa üst (temel) sınıftan olanı mı? Cevap, programlama dilleri arasında değişir ve bazı diller, belirli bir davranışın geçersiz kılınmayacağını ve temel sınıf tarafından tanımlandığı gibi davranması gerektiğini belirtme yeteneği sağlar. Örneğin, C#'ta temel yöntem veya özellik yalnızca bir alt sınıfta sanal, soyut veya geçersiz kılma değiştiricisiyle işaretlenmişse geçersiz kılınabilirken, Java gibi programlama dillerinde diğer yöntemleri geçersiz kılmak için farklı yöntemler çağrılabilir. Geçersiz kılmanın bir alternatifi , devralınan kodu gizlemektir .

Kodun yeniden kullanımı

Uygulama kalıtımı, bir alt sınıfın bir temel sınıftaki kodu yeniden kullandığı mekanizmadır . Varsayılan olarak alt sınıf, temel sınıfın tüm işlemlerini korur, ancak alt sınıf, temel sınıf uygulamasını kendisininkiyle değiştirerek bazı veya tüm işlemleri geçersiz kılabilir .

Aşağıdaki Python örnekte, alt sınıfları SquareSumComputer ve CubeSumComputer geçersiz dönüşümü () temel sınıf yöntemini SumComputer . Temel sınıf, iki tamsayı arasındaki karelerin toplamını hesaplamak için işlemler içerir . Alt sınıf, bir sayıyı karesine dönüştüren ve onun yerine bir sayıyı sırasıyla karesine ve küpüne dönüştüren bir işlemle değiştirme dışında, temel sınıfın tüm işlevlerini yeniden kullanır . Bu nedenle alt sınıflar, iki tamsayı arasındaki karelerin/küplerin toplamını hesaplar.

Aşağıda bir Python örneği verilmiştir.

class SumComputer:
    def __init__(self, a, b):
        self.a = a
        self.b = b

    def transform(self, x):
        raise NotImplementedError

    def inputs(self):
        return range(self.a, self.b)

    def compute(self):
        return sum(self.transform(value) for value in self.inputs())

class SquareSumComputer(SumComputer):
    def transform(self, x):
        return x * x

class CubeSumComputer(SumComputer):
    def transform(self, x):
        return x * x * x

Çoğu çevrede, yalnızca kodun yeniden kullanılması amacıyla sınıf mirası gözden düşmüştür. Birincil endişe, uygulama mirasının herhangi bir polimorfik ikame garantisi sağlamamasıdır - yeniden kullanan sınıfın bir örneği, devralınan sınıfın bir örneğinin yerine mutlaka ikame edilemez. Alternatif bir teknik olan açık yetkilendirme , daha fazla programlama çabası gerektirir, ancak ikame edilebilirlik sorununu ortadan kaldırır. C++'da özel kalıtım, ikame edilebilirlik olmaksızın bir uygulama kalıtımı biçimi olarak kullanılabilir . Genel kalıtım bir "is-a" ilişkisini temsil ederken ve delegasyon bir "vardır" ilişkisini temsil ederken, özel (ve korunan) kalıtım, "açısından uygulanan" bir ilişki olarak düşünülebilir.

Kalıtımın başka bir sık ​​kullanımı, sınıfların belirli bir ortak arabirimi korumasını garanti etmektir; yani aynı yöntemleri uygularlar. Üst sınıf, uygulanan işlemler ve alt sınıflarda uygulanacak işlemlerin bir kombinasyonu olabilir. Çoğu zaman, üst tip ve alt tip arasında arayüz değişikliği yoktur; çocuk, ana sınıfı yerine açıklanan davranışı uygular.

Kalıtım vs alt tipleme

Kalıtım, alt tiplemeye benzer ancak ondan farklıdır. Alt tipleme, belirli bir türün başka bir türün veya soyutlamanın yerine geçmesini sağlar ve dil desteğine bağlı olarak, alt tür ile mevcut bazı soyutlamalar arasında, örtük veya açık olarak bir is- ilişkisi kurduğu söylenir . İlişki, bir alt tipleme mekanizması olarak kalıtımı destekleyen dillerde kalıtım yoluyla açıkça ifade edilebilir. Örneğin, aşağıdaki C ++ kod sınıfları arasında açık bir kalıtım ilişki kurar B ve A , B , bir alt ve bir alt tipi hem de A ve bir olarak kullanılabilir A bir yerde B referans, bir işaretçi veya üzeri (belirtilir nesnenin kendisi).

class A {
 public:
  void DoSomethingALike() const {}
};

class B : public A {
 public:
  void DoSomethingBLike() const {}
};

void UseAnA(const A& a) {
  a.DoSomethingALike();
}

void SomeFunc() {
  B b;
  UseAnA(b);  // b can be substituted for an A.
}

Alt tipleme mekanizması olarak kalıtımı desteklemeyen programlama dillerinde, temel sınıf ile türetilmiş sınıf arasındaki ilişki, türler arasındaki ilişkiye kıyasla yalnızca uygulamalar arasındaki ilişkidir (kodun yeniden kullanımı için bir mekanizma) . Kalıtım, alt tipleme mekanizması olarak kalıtımı destekleyen programlama dillerinde bile, davranışsal alt tiplemeyi zorunlu olarak gerektirmez . Üst sınıfın beklendiği bir bağlamda kullanıldığında nesnesi yanlış davranacak bir sınıf türetmek tamamen mümkündür; bkz Liskov ikame prensibi . ( Çağrımı/düzenlemeyi karşılaştırın .) Bazı OOP dillerinde, kodun yeniden kullanımı ve alt tipleme kavramları çakışır, çünkü bir alt tür bildirmenin tek yolu, bir başkasının uygulamasını miras alan yeni bir sınıf tanımlamaktır.

Tasarım kısıtlamaları

Bir programın tasarımında kalıtımın yaygın olarak kullanılması belirli kısıtlamalar getirir.

Örneğin, bir kişinin adını, doğum tarihini, adresini ve telefon numarasını içeren bir Kişi sınıfını düşünün . Biz bir alt sınıfı tanımlayabilirsiniz Kişi denilen Öğrenci kişinin not ortalaması ve sınıflar alınan içerir ve diğer bir alt kümesi Kişi denilen Çalışan kişinin iş unvanı, işveren ve maaş içerir.

Bu kalıtım hiyerarşisini tanımlarken, tümü arzu edilmeyen bazı kısıtlamaları zaten tanımladık:

bekarlık
Tek kalıtım kullanarak, bir alt sınıf yalnızca bir üst sınıftan miras alabilir. Yukarıda verilen örnekten devam Kişi bir ya olabilir Öğrenci veya Çalışan , ancak ikisini. Bir sonra, bir tanımlayabilir olarak birden çok devralma kullanılarak kısmen, bu sorunu çözer StudentEmployee sınıfı her iki devralır Öğrenci ve Çalışan . Ancak çoğu uygulamada, yine de her bir üst sınıftan yalnızca bir kez miras alabilir ve bu nedenle, bir öğrencinin iki işi olduğu veya iki kuruma gittiği durumları desteklemez. Eyfel'de bulunan kalıtım modeli, tekrarlanan kalıtımı destekleyerek bunu mümkün kılar .
Statik
Bir nesnenin kalıtım hiyerarşisi , nesnenin türü seçildiğinde örnekleme sırasında sabitlenir ve zamanla değişmez. Örneğin, devralma grafiği , Person üst sınıfının durumunu korurken Student nesnesinin bir Çalışan nesnesi olmasına izin vermez . (Bu tür bir davranış, ancak dekoratör deseni ile elde edilebilir .) Bazıları, geliştiricileri orijinal tasarım standartlarına kilitlediğini iddia ederek kalıtımı eleştirdi.
görünürlük
İstemci kodunun bir nesneye erişimi olduğunda, genellikle nesnenin tüm üst sınıf verilerine erişimi olur. Üst sınıf kamuoyuna deklare edilmemiş olsa bile, istemci yine olabilir döküm onun üst sınıf tipine nesneyi. Örneğin, bir işleve , öğrencinin Kişi üst sınıfında depolanan tüm kişisel verilere erişim izni vermeden, bir Öğrencinin not ortalamasına ve not dökümüne bir işaretçi vermenin bir yolu yoktur . C++ ve Java da dahil olmak üzere birçok modern dil, alt sınıfların verilere erişmesine izin veren "korumalı" bir erişim değiştiricisi sağlar, bu da kalıtım zincirinin dışındaki herhangi bir kodun verilere erişmesine izin vermez.

Kompozit yeniden prensibi miras için bir alternatiftir. Bu teknik, davranışları birincil sınıf hiyerarşisinden ayırarak ve herhangi bir iş alanı sınıfında gerektiği gibi belirli davranış sınıflarını dahil ederek polimorfizmi ve kodun yeniden kullanımını destekler. Bu yaklaşım, çalışma zamanında davranış değişikliklerine izin vererek sınıf hiyerarşisinin statik doğasından kaçınır ve bir sınıfın, ata sınıflarının davranışlarıyla sınırlandırılmak yerine, davranışları açık büfe stili uygulamasına izin verir.

Sorunlar ve alternatifler

Uygulama kalıtımı, en azından 1990'lardan beri nesne yönelimli programlamanın programcıları ve teorisyenleri arasında tartışmalıdır. Bunların arasında , arayüz mirasını savunan ve miras yerine kompozisyonu tercih eden Design Patterns yazarları vardır . Örneğin, dekoratör modeli ( yukarıda bahsedildiği gibi ), sınıflar arasındaki kalıtımın statik doğasının üstesinden gelmek için önerilmiştir. Aynı soruna daha temel bir çözüm olarak, rol yönelimli programlama , kalıtım ve kompozisyon özelliklerini yeni bir kavramda birleştirerek oynadığı farklı bir ilişki sunar .

Allen Holub'a göre , uygulama kalıtımı ile ilgili temel sorun , "kırılgan temel sınıf sorunu" biçiminde gereksiz birleştirme getirmesidir: temel sınıf uygulamasında yapılan değişiklikler, alt sınıflarda istenmeyen davranış değişikliklerine neden olabilir. Arayüzleri kullanmak bu sorunu önler çünkü hiçbir uygulama paylaşılmaz, yalnızca API paylaşılır. Bunu belirtmenin bir başka yolu da "miras, kapsüllemeyi bozar ". Sorun , istemci kodunun sistem tarafından sağlanan sınıflardan miras alması ve ardından algoritmalarında sistemin sınıflarının yerine geçmesi beklenen çerçeveler gibi açık nesne yönelimli sistemlerde açıkça ortaya çıkar .

Bildirildiğine göre, Java mucidi James Gosling , Java'yı yeniden tasarlayacak olsaydı dahil etmeyeceğini belirterek uygulama mirasına karşı konuştu. Kalıtımı alt tiplemeden (arayüz mirası) ayıran dil tasarımları 1990 gibi erken bir tarihte ortaya çıktı; Bunun modern bir örneği Go programlama dilidir.

Yeterince olgun olmayan bir tasarımda kullanılan karmaşık kalıtım veya kalıtım, yo-yo sorununa yol açabilir . 1990'ların sonlarında bir sistemde kod yapılandırmak için birincil yaklaşım olarak kalıtım kullanıldığında, geliştiriciler sistem işlevselliği arttıkça doğal olarak kodu birden çok kalıtım katmanına ayırmaya başladılar. Bir geliştirme ekibi, birden çok kalıtım katmanını tek sorumluluk ilkesiyle birleştirirse, çoğu her katmanda yalnızca 1 veya 2 satır kod içeren birçok süper ince kod katmanı oluşturur. Hangi katmanın hatalarının ayıklanması gerektiğini belirlemek zorlaştığından, çok fazla katman hata ayıklamayı önemli bir zorluk haline getirir.

Kalıtımla ilgili diğer bir sorun, alt sınıfların kodda tanımlanması gerektiğidir; bu, program kullanıcılarının çalışma zamanında yeni alt sınıflar ekleyemediği anlamına gelir. Diğer tasarım desenleri ( Entity–component–system gibi ), program kullanıcılarının çalışma zamanında bir varlığın varyasyonlarını tanımlamasına izin verir.

Ayrıca bakınız

Notlar

Referanslar

daha fazla okuma