“Liskov Substitution Principle” (LSP) Kullanımı

Bu yazımızda, SOLID ilkeleri arasında üçüncü sırada yer alan ve “L” harfiyle gösterilen “Liskov Substitution Principle” kavramına odaklanacağız.

“Liskov Substitution Prensibi” Nedir?

İlk olarak, “Barbara Liskov” tarafından 1987’de tanıtılan bu prensip daha sonra Jeannette Wing‘in de katkılarıyla daha detaylı bir şekilde geliştirilmiştir.

Prensibimizdeki “Liskov” adının nereden geldiği anlaşıldığı üzere “substitution” kelimesine odaklanabiliriz; Türkçeye “yerine koyma” veya “ikame” olarak çevrilebilen “substitution” kelimesi, genel anlamıyla, bir şeyi başka bir şeyin yerine koymak veya bir şeyi başka bir şey ile değiştirmek anlamına gelmektedir.

Kelime anlamını da öğrendiğimize göre artık prensibin ne iş yaptığını inceleyebiliriz.

Bir programın doğruluğunun alt sınıfların üst sınıfların yerine geçebilmesiyle bozulmaması gerektiğini ifade eder. Başka bir deyişle, bir sınıf hiyerarşisinde, alt sınıfların (derived veya child classes) üst sınıfların (base veya parent classes) tüm özelliklerini ve davranışlarını koruyarak yerine geçebilmesi gerektiğini anlatır.

Bir programın doğruluğunun alt sınıfların üst sınıfların yerine geçebilmesiyle bozulmaması gerektiğini ifade eder. Başka bir deyişle, bir sınıf hiyerarşisinde, alt sınıfların (derived veya child classes) üst sınıfların (base veya parent classes) tüm özelliklerini ve davranışlarını koruyarak yerine geçebilmesi gerekir. Bu, alt sınıfların üst sınıfların işlevlerini değiştirmeden genişletebilmesi anlamına gelir.

Prensibin nasıl çalıştığını daha da anlaşılır kılabilmek adına bunu aktör-dublör ilişkisi şeklinde yorumlayabiliriz.

Bir filmde bir aktörün (üst sınıf) belirli bir sahnede oynayacağını düşünün. Ancak bu sahne tehlikeli veya aktörün yapamayacağı hareketler içeriyor olsun. İşte böylesi durumlarda, yönetmen bir dublör (alt sınıf) kullanmaya karar verebilir. Liskov Substitution Principle’e göre, bu dublör aktörün yerine geçtiğinde, sahnenin genel akışını veya filmin hikayesini bozmamalıdır. Yani dublör, aktörün yapacağı tüm hareketleri, aynı şekilde yapabilmelidir.

Bu benzetmede, dublörün (alt sınıfın) aktörün (üst sınıfın) yerine geçmesi, programlamada bir alt sınıfın üst sınıfın yerine geçmesine karşılık gelmektedir. Dublör, aktörün tüm rollerini üstlenebildiği (aktörün tüm metodlarını desteklemeli) gibi, ekstra hareketler de yapabilmeli (yani ek metodlar veya özellikler ekleyebilir). Ancak, dublör sahneyi öyle bir şekilde değiştirmemelidir ki, bu durum ne hikayenin bütünlüğünü bozsun ne de izleyiciler tarafından aslında başka bir aktör tarafından oynandığı fark edilebilsin (yani alt sınıf, üst sınıfın beklenen davranışını değiştirmemelidir).

Örnek üzerinden anlatırsak ziyadesiyle anlaşılacağını düşünüyorum. İlk olarak LSP’ye uyulmayan bir Python kodunu inceleyelim:

class Bird:
    def fly(self):
        pass

class Eagle(Bird):
    def fly(self):
        return "Eagle can fly"

class Ostrich(Bird):
    def fly(self):
        raise Exception("Ostriches cannot fly")

Bu örnekte, Bird sınıfından türetilen Eagle ve Ostrich sınıfları bulunmaktadır. Eagle, Bird‘ün fly metodunu uygun şekilde genişletirken, Ostrich bu metodu tamamen değiştiriyor ve uçamayacağını ifade eden bir hata fırlatıyor. Eğer bir fonksiyon Bird tipinde bir nesne bekliyor ve bu nesne Ostrich ise, bu fonksiyonun beklenmedik bir hata ile karşılaşması muhtemeldir. Bu, LSP’ye aykırıdır, çünkü alt sınıfın davranışı, üst sınıfın beklenen davranışını değiştirmektedir.

Bu sefer de LSP’ye uygun bir durumu ele alalım. Burada bir üst sınıfımız (Shape) ve bu sınıftan türetilmiş iki alt sınıfımız (Rectangle ve Square) olacak. Bakınız:

class Shape:
    def area(self):
        pass

class Rectangle(Shape):
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

class Square(Rectangle):
    def __init__(self, size):
        super().__init__(size, size)

Yukarıdaki örneğimizde Square sınıfı, Rectangle sınıfından türetilmiştir. Square sınıfı, Rectangle‘ın tüm özelliklerini ve davranışlarını koruyor (yani area metodunu), ancak kendi özelliği olan eşit kenar uzunluklarını da ekliyor. Haliyle, Rectangle nesnesinin yerine Square nesnesi kullanılabilir ve programın işleyişi bozulmaz. İşte bu, tam da LSP’ye uygun bir durum olmuştur.

Görüleceği üzere, LSP’ye uygun örnekte, alt sınıfın (Square) üst sınıfın (Rectangle) yerine geçmesi durumunda herhangi bir sorun oluşmamaktadır. Yani, Rectangle için yazılmış kod Square ile de uyumlu çalıştığından kodun yeniden kullanılabilirliğini ve genişletilebilirliğini artırdığı gibi, aynı zamanda bakımını da kolaylaştırmaktadır.

LSP’ye uyulmayan örnekte ise, Ostrich sınıfının Bird sınıfının yerine geçmesi durumunda program beklenmeyen bir hata ile karşılaşabilir. Bu durum, kodun tahmin edilebilirliğini ve güvenilirliğini azaltır. Yani, bir fonksiyonun Bird tipinde bir nesne beklediği her yerde, Ostrich nesnesinin bu fonksiyonu bozabileceği anlamına gelir. Haliyle büyük ve karmaşık sistemlerde ciddi sorunlar ortaya açabilir.

İlgili Makaleler

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Başa dön tuşu