Kalıtım ve metot ezme

Kalıtım (inheritance), bir sınıfın başka bir sınıfın davranışlarını ve arayüzünü temel alarak özelleşmesini sağlar. Güçlü bir araçtır; ancak yanlış kullanıldığında sıkı bağımlı ve kırılgan sınıf hiyerarşileri oluşturabilir. Bu hafta kalıtımı is-a ilişkisi üzerinden öğrenecek ve kompozisyon alternatifiyle karşılaştıracağız.

Bu hafta neleri yapabilmelisiniz?

  • Üst sınıf ve alt sınıf kavramlarını açıklayabilmeli,
  • class Child(Parent) sözdizimini kullanabilmeli,
  • Miras alınan metodu çağırabilmeli,
  • Metot ezme (override) yapabilmeli,
  • super() ile hem __init__ hem normal bir üst sınıf davranışını genişletebilmeli,
  • isinstance() ile alt tür ilişkisinin etkisini gözlemleyebilmeli,
  • Liskov yerine geçme ilkesinin temel sorusunu açıklayabilmeli,
  • is-a ile has-a ayrımını kullanarak kalıtımın uygunluğunu tartışabilmeli,
  • Çoklu kalıtım ve MRO kavramlarını tanıma düzeyinde açıklayabilmelisiniz.

İlk kalıtım örneği

Developer, Employee sınıfından davranış alır. Bir Developer nesnesi aynı zamanda Employee tür ilişkisinin de içindedir; bu nedenle iki isinstance() çağrısı da True olur.

is-a yeterli mi? Liskov yerine geçme ilkesi

Kalıtım için ilk sezgi:

Bir Developer, bir Employee’dir.

Ama daha güçlü soru şudur:

Bir Employee beklenen yerde Developer kullanıldığında üst sınıfın beklenen sözleşmesi bozulmadan program çalışıyor mu?

Bu düşünce yazılım tasarımında Liskov Substitution Principle (LSP) / Liskov yerine geçme ilkesi ile ilişkilidir. Bu derste ilkenin formal tanımını ezberlemiyoruz; alt türün üst türün beklenen davranışını bozup bozmadığını sorguluyoruz.

Override: davranışı özelleştirmek

Alt sınıftaki send, üst sınıftaki aynı adlı metodu ezer (override). Override ederken çağıranın beklediği davranış sözleşmesini korumaya çalışın.

Alt sınıfın üst sınıftan davranış devralması ve bir metodu override ederek özelleştirmesini gösteren diyagram.

Kalıtım ve override

super() yalnızca __init__ için değildir

Önce kurucuda yeniden kullanım:

super().__init__(name), üst sınıf kurulumunu tekrar yazmamayı sağlar.

Alt sınıf kurucusundan super().__init__ çağrısıyla üst sınıf kurucusuna geçişi gösteren akış.

super() çağrı akışı

Asıl davranışı genişletme örneği normal metotta da görülebilir:

Burada alt sınıf üst davranışı tamamen silip yeniden yazmak yerine çağırır ve sonucu genişletir.

is-a ve has-a

İlişki Örnek Tipik araç
is-a Developer bir Employee’dir kalıtım
has-a Car bir Engine’e sahiptir kompozisyon

Ortak alan veya metot bulunması tek başına kalıtım gerekçesi değildir. Driver ve Car ikisinde de name bulunabilir; ama Driver is a Car anlamsızdır.

Kırılgan hiyerarşi ve MRO kavram radarı

Derin kalıtım zincirleri üst sınıftaki küçük değişikliklerin çok sayıda alt sınıfa yayılmasına yol açabilir. Bu derste iki-üç seviyeden daha derin hiyerarşiler tasarlamayacağız.

Python ayrıca çoklu kalıtıma izin verir:

class C(A, B):
    pass

Bu durumda aynı adlı davranışın hangi sırayla aranacağını method resolution order (MRO) belirler. C.mro() ile bu sıra görülebilir. Çoklu kalıtım ve MRO bu dersin uygulama/ölçme kapsamı değildir; yalnızca internette karşılaştığınızda terimi tanımanız beklenir.

WarningKalıtım öğrendik diye her yerde kullanmayın

İyi OOP tasarımı mümkün olduğunca çok hiyerarşi üretmek değildir. Kompozisyon çoğu durumda daha gevşek ve değiştirilebilir bir bağ kurar.

Alıştırma — Uygun ilişkiyi seç

Dog–Animal, Order–Customer, Laptop–Battery, Teacher–Course, AdminUser–User çiftlerinde is-a, has-a veya “ikisi de değil” seçin. is-a seçtiğiniz her durumda ayrıca “üst tür beklenen yerde alt tür güvenle kullanılabilir mi?” sorusunu yanıtlayın.

Alıştırma — Override tamamla

return self.width * self.height
Note

NotImplementedError burada “alt sınıf bu davranışı sağlamalı” beklentisini görünür kılan öğretim örneğidir; sınıfı teknik olarak soyut yapmaz. 12. haftada NotImplemented ile NotImplementedError arasındaki kritik farkı ayrıca göreceğiz.

Alıştırma — Normal metotta super() kullan

Bir Logger.log(message) metodu "LOG: ..." döndürsün. TimestampLogger, log() metodunu override etsin; önce super().log(message) çağırıp sonra sonuca " [zaman damgalı]" eklesin. Üst metodu kopyalamadan davranışı genişletin.

Küçük üretim görevi — Ödeme türleri

Bir Payment üst sınıfı ile CardPayment ve CashPayment alt sınıflarını tasarlayın. Her ikisi pay(amount) sunsun. En az bir alt sınıfta super() ile normal bir üst metodu genişletin.

Çalışır kod teslimi: İki alt türü aynı listede tutup pay() çağrılarını çalıştırın; ayrıca isinstance(child, Parent) sonucunu assert ile doğrulayın. Bir geçersiz kalıtım örneği yazıp neden LSP açısından sorunlu olduğunu iki cümleyle açıklayın.

Kendinizi kontrol edin

  1. Kalıtım hangi ilişkiyi ifade etmelidir?
  2. Liskov yerine geçme ilkesinin bu dersteki temel sorusu nedir?
  3. isinstance(dev, Employee) neden True olabilir?
  4. Override nedir?
  5. super() normal bir metotta ne işe yarayabilir?
  6. Ortak kod bulunması tek başına kalıtım gerekçesi midir?
  7. MRO hangi problemle ilgilidir?

Bu haftadan akılda kalması gerekenler

  • Kalıtım bir is-a ilişkisidir; alt tür üst türün beklenen sözleşmesini korumalıdır.
  • Bu davranışsal beklenti Liskov yerine geçme ilkesiyle ilişkilidir.
  • Alt sınıf davranışı miras alabilir, ekleyebilir veya override edebilir.
  • super() yalnızca kurucuda değil, normal davranışı genişletirken de kullanılabilir.
  • Alt sınıf örneği üst sınıf açısından da isinstance olabilir.
  • Çoklu kalıtımda MRO önemlidir; bu derste tanıma düzeyindedir.
  • Kompozisyon her zaman gerçek bir alternatiftir.
Back to top