SOLID Prensipleri : 03 Liskov Substitution Principle

By Burak Tungut - 15.6.2016 - 5 Yorum - Kategori Design / Architectural Patterns

Selamlar herkese,

Son 1 haftadır bazı çalışma arkadaşlarımın "Yazmaya neden ara verdin?" tepkilerine daha fazla dayanamıyor ve bu yazımızda SOLID prensiplerinden Liskov Substitution - Liskov'un Yerine Geçme prensibini inceliyor olacağız. Yazmam için sürekli beni dürten sevgili çalışma arkadaşım Barış BIYIK'a teşekkürlerimi buradan da iletmek isterim :)

Liskov Substitution aslında Prof. Barbara Liskov ismindeki bir bilim insanının tasarladığı bir prensip.
Prensip bize der ki ;

"Aynı base sınıftan üretilen tüm alt sınıflar, birbirlerinin yerine kullanılabilir olmalıdır. Alt sınıflara özel bir istisnai durum kesinlikle oluşmamalıdır."

Bazen abstract class'lar ile interface'ler arasındaki farkın ne olduğuna dair sorularda alıyor olabilirsiniz. Aslınd bu prensibin amacı bu iki tip abstraction yapmamızı sağlayan tip arasındaki en büyük farkı göstermekte. Birazdan yapacağımız örnek ile bunu çok daha iyi anlıyor olacağız.

 

Liskov Substitution Principle

 

Küçük Bir Senaryo İle Liskov Substitution Principle

Farz edelimki ürünlerin ve ürünler üzerinde çeşitli business'ları işletebilecek üyelik tiplerinin olduğu bir proje geliştiriyoruz. Çok minimal bir ürün tipi tasarlayalım. Ben aşağıdaki gibi bir şeyler tasarladım;

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public List<string> Features { get; set; }
}

Birde projede birden fazla, çeşitli haklara sahip ya da var olan hakları farklı şekilde davranışlarını değiştirecek üyelik tiplerimiz olsun. Öyle bir base tipimiz olsun ki şimdilik 2 soyut methodu olsun. Bunlardan ilki verilen ürünlerin toplam fiyatını sunarken diğeri yine verilen ürünlerin satışlarına ait çeşitli raporlar sunsun.

Bunun için aşağıdaki gibi bir tip tasarlayabiliriz;

public abstract class MembershipBase
{
    public virtual decimal GetPrice(List<Product> products)
    {
        return products.Sum(p => p.Price);
    }

    public abstract Stream GetReport(List<Product> products);
}

 

Liskov Substitution Principle

Şimdi bu abstract tipin öyle iki tane concrete implementation'ını yaratalım. Bunlardan biri tüm yetkilere sahip (TraditionalMembership) olurken diğeri (TrialMembership) kısıtlı yetkilere sahip olsun. Mesela GetReport methodunu kullamasın çünkü rapor gibi yetkileri ücretsiz vermek istemeyiz.

Öyleyse aşağıdaki gibi bir implementation gerçekleştirsek nasıl olur bir bakalım;

 

 

 

public class TraditionalMembership : MembershipBase
{
    public override Stream GetReport(List<Product> products)
    {
        //Raporlama business'ları
        //Sonuçların PDF haline getirilmesi
        return new MemoryStream();
    }
}

public class TrialMembership : MembershipBase
{
    public override Stream GetReport(List<Product> products)
    {
        throw new NotImplementedException();
    }
}

İzlediğimiz bu yaklaşım ne yazık ki Liskov Substitution prensibini ezmiş bulunuyoruz. Dikkat ederseniz TrialMembership tipimiz yazımızın başında açıkladığımız prensibin temelini yıkmakta.

Refactoring Zamanı

GetReport methodumuz akışta davranışsal bozukluklara neden olacak. Öyleyse onu başka bir şekilde implemente etmeye çalışalım. Yine dikkat edersek GetPrice veya yanına gelebilecek ortak özellikler bizler için problem oluşturmuyor. Ancak tipe göre farklılık gösterecek olan method, property (üyeleri) base tiplerden çıkarmalıyız. Çözüm olarakta bunları birer interface'e aktarabiliriz.
Böylece alt tiplerimize bazı yetenekler vermiş olacağız.

Öyleyse GetReport methodunu MembershipBase tipiniden çıkartalım ve aşağıdaki gibi yeni bir tip yaratalım;

public interface IReportSupport
{
    Stream GetReport(List<Product> products);
}

Hal böyleyken TraditionalMembership ve TrialMembership tiplerimizde aşağıdaki gibi olup ilgili tüm sınıfların son diyagramıda hemen aşağıdaki gibi olacaktır;

public class TraditionalMembership : MembershipBase, IReportSupport
{
    public Stream GetReport(List<Product> products)
    {
        return new MemoryStream();
    }
}

public class TrialMembership : MembershipBase
{
}

Böyle yaparak aslında TraditionalMembership tipimize bir interface ile rapor alabilme yeteneği eklemiş olduk.

 Liskov Substitution Principle

 

Sonraki yazılarda görüşmek dileğiyle :)

Burak, ellerine sağlık çok güzel ve sade bir şekilde açıklamışsın. Teşekkürler.
Selamlar, Teşekkürler güzel yazın için. Yalnız verdiğin örnek daha ziyade Interface Segregation prensibini açıklamıyor mu, ne dersin?
Yorum Bırak

Facebook
Son Yorumlar