Takıldığınız bir şey mi var?

.NET, front-end development, data structures veya desing patterns gibi konularda sorular açın ve en kısa zamanda cevaplayalım...


27
Ocak

Liskov Substitution Principle önderliğinde abstract özellikler ve Interface.

Nerede interface nerede inheritance-abstract kullanılmalı konusu yazılımcıların kanayan yarası gibi görünüyor. Aslında böyle olmasının sebebi SOLID tasarım prensiplerini yeteri kadar anlamamaktan kaynaklanıyor. Bu noktada Liskov Substitution Principle hakkında bir şeyler bilmek bu sorunun ortadan kalmakmasına yardımcı olacaktır diye düşünüyorum. Liskov Substitution Principle(kısaca LSP), üst sınıftan gelen tüm özelliklerin, alt seviyedeki tüm sınıflarca kullanılabilir olmasını öngören bir solid tasarım prensibidir. Her nesne yönelimli uygulama dizaynında uyulması gereken LSP kuralına göre miras veren sınıftan gelen bir özelliğin, daima miras alan sınıflarca kullanılabilir olması beklenir. Bunu anlamak adına aşağıdaki hatalı tasarımı ve akabinde doğru kodlama biçimini görelim.


public abstract class Phone
{
    public abstract void Call(string phoneNumner);
    public abstract void TakePhoto();
}


Telefon isimli sınıfımız temsili olarak arama yapmak ve fotoğraf çekmek ile görevli olan iki fonksiyonu abstract olarak barındırıyor. Bu sınıftan miras alan Iphone ve Nokia3310 sınıflarını kodlarken Nokia3310 için TakePhoto fonksiyonunun kullanılamaz olacağı için LSP’ye aykırı bir tasarım yapılmış olunacaktır.


public class Iphone : Phone
{
      public override void Call(string phoneNumner) 
          => Console.WriteLine($"{phoneNumner} calling...");

      public override void TakePhoto() 
          => Console.WriteLine("Photo taked"); 
}
    
public class Nokia3310 : Phone
{
        public override void Call(string phoneNumner) 
            => Console.WriteLine($"{phoneNumner} calling...");
                  
        public override void TakePhoto()
            => throw new Exception("This device can not take a photo!");
}  

Iphone isimli mirasçı sınıf için hiçbir sorun yok iken Nokia3310 modelinin fotoğraf çekme özelliğine sahip olmaması sebebiyle LSP’ye aykırı bir sonuç doğuracaktır.Bu sonucu ört pas etmek adına genellikle ya ortama bir exceptio fırlatma ya da ilgili fonksiyonun gövdesini boş bırakmak gibi abuk subuk yollara başvurulur. Aslında sorunun gerçek çözümü, nerede abstract, nerede interface kullanacağını acilen öğrenmektir.

Bir sınıf için –dır,-dir,-dur soneklerini kullanabilorsanız burada bir inheritance kokusu almanız gerekir. Örneğimiz için konuşursak “Iphone bir telefon-dur” ifadesini kullanabildiğimiz için iphone, telefon sınıfından miras alamalıdır. Benzer şekilde –ebilir,-abilir soneklerini alabiliyor ise burada da bir interface aklımıza gelmelidir. Örneğimizde “İphone fotoğraf çek-ebilir” cümlesini kurabiliyorsam burada bir interface olmasının gerektiğine dair bir fikre kapılmalıyım. Her telefonun fotoğraf çekmek gibi bir özelliği olamayacağından ötürü farklı mirasçı sınıfların LSP’ye aykırı sonuçlar doğruabilecek olması sebebiyle fotoğraf çekme özelliği sınıfıma üst sınıftan değil, bağımsız bir ara birimden gelmelidir.

public interface IPhoto
{
     void TakePhoto();
}

public abstract class Phone
{
     public abstract void Call(string phoneNumner);
}


IPhoto isimli bir ara birim oluşturmak ilk işim olmalı… Sonrasında LSP’ye ters düşecek olan özellikleri üst seviyedeki sınıftan çıkarmalıyım. Bu adımdan sonra fotoğraf çekebilme özelliği olan sınıflara, bu özelliği tanımladığım ara birim ile kazandırmalıyım.

public class Iphone : Phone, IPhoto
{
         public override void Call(string phoneNumner) 
             => Console.WriteLine($"{phoneNumner} calling...");
         public void TakePhoto() 
             => Console.WriteLine("Photo taked");
}

public class Nokia3310 : Phone
{
        public override void Call(string phoneNumner) 
            => Console.WriteLine($"{phoneNumner} calling...");
}