KategorilerNesne Yönelimli ProgramlamaProgramlama PrensipleriYazılım Mimarisi

Liskov Substitution Prensibi Örnekli Anlatım

SOLID prensiplerinin “L”si olan Liskov Substitution Prensibi(LSP) yani Liskov Yerine Geçme Prensibi ilk olarak Barbara Liskov tarafından 1987 yılında bir konferansta tanıtıldı. Bu prensibe göre B sınıfı eğer A sınıfının alt sınıfıysa A sınıfının objeleri B sınıfının objeleriyle programın çalışmasında herhangi bir değişiklik yaratmadan yer değiştirebilir.

Biliyorum biraz karışık görünüyor bu şekilde anlatınca, daha basit bir şekilde söylemek gerekirse eğer iki sınıf arasında ebeveyn-çocuk ilişkisi yani bir kalıtım varsa ve biz çocuk sınıftan ürettiğimiz bir nesneyi ebeveyn sınıftan ürettiğimiz bir nesneyle değiştirebiliyor ve program çalışması beklendiği gibi sürdürülebiliyorsa, o zaman Liskov Substitution Prensibine uyuyor diyebiliriz.

Hala netleşmediyse bir örnek ile netleştirmeye çalışalım, öncelikle LSP’ye uymayan bir örnekle başlayalım:

// dikdörtgen sınıfı
class Rectangle
{
	public virtual int Width { get; set; }
	public virtual int Height { get; set; }

	public int CalculateArea()
	{
		return Width * Height;
	}
}
// kare sınıfı - kare de bir dikdörtgen olduğundan dikdörtgenden kalıtım alınıyor
class Square : Rectangle
{
	private int side;

	public override int Width
	{
		get { return side; }
		set { side = value; }
	}

	public override int Height
	{
		get { return side; }
		set { side = value; }
	}
}

class Program
{
	static void Main()
	{
                // dikdörtgenin boyunu ve genişliğini ayarlıyoruz.
		Rectangle rectangle = new Rectangle();
		rectangle.Width = 5;
		rectangle.Height = 10;

		Console.WriteLine($"Dikdörtgenin alanı: {rectangle.CalculateArea()}");
                
                // karenin kenar uzunluğunu ayarlıyoruz
		Rectangle square = new Square();
		square.Width = 5;
		square.Height = 5;

		Console.WriteLine($"Karenin alanı: {square.CalculateArea()}");
	}
}

Şu anda her şey düzgün çalışıyor gibi, peki buradaki sorun nedir? Burada Square(kare) sınıfımızı Rectangle(dikdörtgen) sınıfından kalıtım aldığımızdan kare sınıfımız dikdörtgen sınıfının özelliklerini de içeriyor, yani normalde bir karenin alanını hesaplamak için tek bir kenar uzunluğuna ihtiyacımız varken burada en ve boy bilgilerini girmemiz gerekiyor. Bu işlem yapılırken de Dikdörtgen(Rectangle) sınıfının özellikleri olan width(en) ve height(boy) propertylerini override ederek aynı değeri almalarını sağlıyoruz. Ana sınıfın işleyişini değiştiren bu işlem Liskov Substitution Prensibine aykırı bir durum oluşturuyor.

Peki bu durumda ne yapabiliriz?

Basit şekilde bu sorunu çözmek için aradaki kalıtımı iptal ederek kare ve dikdörtgeni ayrı sınıflar olarak değerlendirebiliriz ya da ayrı bir Interface(Arayüz) veya abstract class(soyut sınıf) oluşturarak kare ve dikdörtgenin buradan kalıtım almasını sağlayabiliriz. Örneğin:

public interface IShape
{
    int CalculateArea();
}

class Rectangle : IShape
{
    private int Width { get; set; }
    private int Height { get; set; }

    public Rectangle(int width, int height)
    {
        Width = width;
        Height = height;
    }

    public int CalculateArea()
    {
        return Width * Height;
    }
}

class Square : IShape
{
    private int Side { get; set; }

    public Square(int side)
    {
        Side = side;
    }

    public int CalculateArea()
    {
        return Side * Side;
    }
}

class Program
{
    static void Main()
    {
        IShape rectangle = new Rectangle(5, 10);
        Console.WriteLine($"Dikdörtgen alanı: {rectangle.CalculateArea()}");

        IShape square = new Square(5);
        Console.WriteLine($"Kare alanı: {square.CalculateArea()}");
    }
}

Burada bir IShape(şekil) Interface‘i oluşturarak tanımlanması gereken alan hesaplama metodunu ekledik. ardından dikdörtgen ve kare sınıflarımızda bu Interface‘i kullanıp farklı özelliklerini bu sınıfların kendilerinde tanımladık. Bu şekilde alt sınıfların üst sınıfların özelliklerini etkilemediği, Liskov Substitution Prensibine daha uygun bir yapı elde etmiş olduk.

Dezavantajları nelerdir?

Alt sınıfların üst sınıfların davranışlarını korumaları gerektiğinden kod karmaşıklığı artabilir. Ve yine aynı sebepten, doğru sınıf yapılarını tasarlamak zorlaşabilir.

Önceki yazılar:

Solid Prensipleri
Single Responsibility Prensibi
Open-Closed Prensibi

Sonraki yazılar:

Interface Segregation Prensibi
Dependency Inversion Prensibi

Bir Cevap Yazın