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 üst sınıf davranışını genişletebilmeli,
  • is-a ile has-a ayrımını kullanarak kalıtımın uygunluğunu tartışabilmeli,
  • alt türün üst türün beklenen davranışını bozup bozmadığını sorgulayabilmeli,
  • yanlış kalıtım örneğini fark edebilmelisiniz.

İlk kalıtım örneği

Developer, Employee sınıfından describe() davranışını miras alır.

Bu ilişki anlamlıysa şu cümle doğal okunmalıdır:

Bir Developer, Employee’dir. (Developer is an Employee)

Ancak yalnızca bu cümlenin kulağa doğru gelmesi yeterli değildir. Daha güçlü ikinci soru şudur:

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

Alt sınıf, üst sınıfın beklenen davranışını anlamsızlaştırıyorsa hiyerarşi sözdizimsel olarak kurulabilse bile tasarım zayıftır.

Alt sınıfa yeni davranış eklemek

Alt sınıf üst sınıf davranışlarını alıp yeni davranış ekleyebilir.

Override: davranışı özelleştirmek

Alt sınıftaki send, üst sınıftaki aynı adlı metodu ezer (override).

Override yaparken yalnızca metot adının aynı olmasına değil, çağıranın beklediği davranış sözleşmesinin korunmasına da dikkat edin. Örneğin üst sınıfta send(message) bir string döndürüyorsa alt sınıfın aynı çağrıda bambaşka türde ve ilgisiz bir sonuç üretmesi ortak kullanımı zorlaştırabilir.

Employee sınıfından türeyen Developer sınıfının describe() metodunu yeniden tanımlayarak davranışı ezmesi.

super() ile genişletmek

super().__init__(name), üst sınıfın başlangıç kurulumunu tekrar yazmak yerine yeniden kullanır.

super().__init__() ile üst sınıfın name alanını kurup alt sınıfta language alanını ekleme.

Her tekrar kalıtım nedeni değildir

Şu düşünce tehlikelidir:

“İki sınıfta aynı metot var, hemen ortak üst sınıf yapalım.”

Kalıtım, yalnızca kod tekrarını azaltmak için değil, anlamsal alt tür ilişkisini ifade etmek için kullanılmalıdır.

Örneğin Car ve Driver nesnelerinin ikisinde de name niteliği olabilir; ama Driver is a Car anlamsızdır. Ortak alan bulunması kalıtım gerekçesi değildir.

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

Bir sınıf başka bir nesnenin davranışını kullanmak istiyor diye onun alt sınıfı olmak zorunda değildir.

Kırılgan hiyerarşi sezgisi

Derin kalıtım zincirleri:

Entity
  └── Person
       └── Employee
            └── TechnicalEmployee
                 └── Developer

başlangıçta düzenli görünebilir; ancak üst sınıftaki küçük bir değişiklik birçok alt sınıfı etkileyebilir. Bu derste iki-üç seviyeden daha derin hiyerarşiler tasarlamayacağız.

Warning“Kalıtım öğrendik, her yerde kullanalım” yanılgısı

OOP’nin hedefi mümkün olduğunca çok sınıf hiyerarşisi üretmek değildir. İyi tasarım bazen hiç kalıtım kullanmaz. Kompozisyonu her zaman gerçek bir alternatif olarak değerlendirin.

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

Her çift için is-a, has-a veya “ikisi de değil” seçin:

  1. DogAnimal
  2. OrderCustomer
  3. LaptopBattery
  4. TeacherCourse
  5. AdminUserUser

Kısa gerekçe yazın. is-a seçtiğiniz örneklerde ayrıca şu soruyu yanıtlayın: “Üst tür beklenen yerde alt tür güvenle kullanılabilir mi?”

Alıştırma — Override tamamla

return self.width * self.height
Note

Shape.area() içindeki NotImplementedError, burada “alt sınıf bu davranışı sağlamalı” beklentisini görünür kılan basit bir öğretim örneğidir. Bu kullanım sınıfı teknik olarak soyut yapmaz. Python’ın abc modülüyle tanımlanan gerçek soyut temel sınıf mekanizmasını gelecek hafta yalnızca kavram düzeyinde göreceğiz.

Alıştırma — super() ekle

Aşağıdaki alt sınıfta name niteliği kurulmamış:

Hata neden oluşur? AdminUser.__init__ içinde üst sınıf kurulumunu çağıracak tek satırı ekleyin.

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

Bir Payment üst sınıfı ve iki alt sınıf tasarlayın:

  • CardPayment
  • CashPayment

Her ikisi de pay(amount) davranışı sunsun; sonuç mesajları farklı olsun. Gerekirse alt sınıflara özgü nitelikler ekleyin.

Ardından şu soruyu cevaplayın:

Bu örnekte kalıtım gerçekten gerekli mi, yoksa aynı arayüzü sunan bağımsız sınıflar da yeterli olabilir mi?

Bu soru gelecek haftanın polimorfizm ve duck typing konusuna geçiştir.

Kendinizi kontrol edin

  1. Kalıtım hangi ilişkiyi ifade etmelidir?
  2. is-a cümlesine ek olarak hangi davranışsal soruyu sormak gerekir?
  3. Override nedir?
  4. super() ne sağlar?
  5. Ortak kod bulunması tek başına kalıtım gerekçesi midir?
  6. Derin hiyerarşilerin bakım riski nedir?

Bu haftadan akılda kalması gerekenler

  • Kalıtım bir is-a ilişkisidir; ancak alt türün üst türün sözleşmesini koruması da gerekir.
  • Alt sınıf davranışı miras alabilir, ekleyebilir veya override edebilir.
  • super() üst sınıf davranışını yeniden kullanmaya yardımcı olur.
  • Kalıtım, kod tekrarını azaltmanın evrensel çözümü değildir.
  • Kompozisyon çoğu durumda daha gevşek ve esnek bağ kurar.
Back to top