LSP özet olarak miras alan sınıfın ata sınıfa ait tüm özelliklerini kullanabilmeli ve kullanıyor olmalıdır. Bunun yanında kodlar bir değişikliğe maruz kalmadan alt sınıflar üst sınıfların yerine kullanılabiliyor olmalıdır. LSP, MIT programlama metodolojilerinde grup liderliği yapan Barbara Liskov tarafından öne sürülmüştür.
LSP alt sınıf yani türeyen sınıf için şunu söyler; türetilen (ata) sınıfın tüm özelliklerini ve metotlarını aynı işlevi görecek şekilde kullanmalı ve kendine ait yeni özellikler taşıyabilmelidir. Bu prensib bize base class’da gereksiz kod olmaması gerektiğini söyler. Bunların dışın LSP interface’ler ile ilgilenmez, LSP yalnızca class’lar ile ilgilenir. Interface’ler ile ilgilenen prensip Interface Segregation Principle’dır.
LSP’nin alt sınıfların ata sınıflara ait tüm özellikleri kullanabilmesi ve kullanıyor olmasına dikkat ettiğini farkettik. Burada eğer alt sınıf ata sınıfa ait tüm özelliklerini kullanmayacaksa gereksiz kod bloklarına sahip oluruz tabii bu durumda LSP’ye ve clean code’a karşıdır. LSP türeyen class ile base class arasındaki bağı en gerekli şekilde oluşturmamıza yardımcı olur.
Örnek:
Base class olarak Player sınıfını oluşturduk Goalkeeper ve Striker sınıflarımız Player sınıfından türüyorlar. Player sınıfı türeyen sınıfların ortak özelliklerini barındırmalı.
public abstract class Player
{
public virtual string KickBall()
{
return "Operations";
}
public virtual string KeepBall()
{
return "Operations";
}
}
Goolkeeper ve Striker klaslarımızı oluşturduk ve ata sınıfımızdan miras aldık. LSP’ye göre türeyen ve türetilen arasında gereksiz kod olmamalıdır. Ancak Player sınıfında yer KeepBall metodu sonradan eklenen klaslar için gerekli olsada Striker sınıfı için gereksizdir. Striker sınıfının kullanmayacağı sınıfa erişemiyor olması gerekiyor. LSP’ye uymak için ata sınıflarımızda genelden özele doğru tasarım yapacağız.
public class Goalkeeper : Player
{
public override string KeepBall()
{
return "Operations";
}
public override string KickBall()
{
return "Operations";
}
}
public class Striker : Player
{
public override string KickBall()
{
return "Operations";
}
public override string KeepBall()
{
return "Operations";
}
}
LSP’ye uyumlu olarak:
Player sınıfı içerisine tüm ortal mevki oyuncularını sahip olduğu özellikleri tanımladık. Mevkilere özel metotları ise ilgili mevkiye için tanımlanan interface içerisinde yaptık. Bu şekilde genelden özele doğru hareket etmiş olduk. Goalkeeper kalsı hem Player hemde IKeepBall sınıfından miras alıyor. Striker sınıfı ise yalnızca ihtiyacı olan Player sınıfından biras alıyor. böylelikle gereksiz koddan ve nesnelerin ihtiyacı olmayan özelliklere erişiminin önüne geçmiş olduk.
public abstract class Player
{
public virtual string KickBall()
{
return "Operations";
}
}
public interface IKeepBall
{
string KeepBall();
}
public class Goalkeeper : Player, IKeepBall
{
public string KeepBall()
{
return "Operations";
}
public override string KickBall()
{
return "Operations";
}
}
public class Striker : Player
{
public override string KickBall()
{
return "Operations";
}
}