SOLID Prensipleri

Fırat DİKMEN
3 min readJun 23, 2023

--

SOLID prensipleri, yazılım tasarımının temel taşlarıdır ve kodun bakımını, anlaşılabilirliğini ve esnekliğini artırmak için kullanılır. Bu prensipler şunlardır:

SOLID Principle

Single-responsibility principle (Tek Sorumluluk Prensibi): Bir sınıfın sadece bir işi olmalıdır.
Open-closed principle (Açık-Kapalı Prensibi): Bir sınıf mevcut özellikleri korumalı ve değişikliklere kapalı olmalıdır.
Liskov substitution principle: Alt sınıflar, üst sınıflarının yerine geçebilmelidir.
Interface segregation principle: Bir sınıfın tüm işlemlerini tek bir arayüze yüklemek yerine, ihtiyaçlara göre birden çok arayüz oluşturmalıyız.
Dependency Inversion Principle: Üst seviye sınıflar, alt seviye sınıflara doğrudan bağımlı olmamalıdır.

SSingle-responsibility principle (Tek Sorumluluk Prensibi): Bir sınıfın sadece bir işi olmalıdır. Bir sınıfı değiştirme ihtiyacı hissettiğimizde, bu genellikle sınıfın yalnızca bir işi yerine getireceği anlamına gelir.

Örneğin, bir “Araba” sınıfımız olduğunu düşünün. Bu sınıfın içinde “gazVer”, “frenYap” gibi araba ile ilgili fonksiyonlar olabilir. Ancak aynı sınıfta “KDVHesapla” gibi bir işlemi gerçekleştiren bir fonksiyon olmamalıdır. Çünkü bu işlem arabanın sorumluluğu olmamalıdır.

public class Araba
{
public void GazVer()
{
}

public void FrenYap()
{
}
}

public class VergiHesapla
{
public void KDVHesapla()
{
}
}

OOpen-closed principle (Açık-Kapalı Prensibi): Bir sınıf mevcut özellikleri korumalı ve değişikliklere kapalı olmalıdır. Ancak yeni özellikler eklemek için genişlemeye açık olmalıdır.

public abstract class ElektronikAlet
{
public abstract void Calis();
}

public class Telefon : ElektronikAlet
{
public override void Calis()
{
Console.WriteLine("Telefon Çalışıyor");
}
}

public class Bilgisayar : ElektronikAlet
{
public override void Calis()
{
Console.WriteLine("Bilgisayar Çalışıyor");
}
}

LLiskov substitution principle: Alt sınıflar, üst sınıflarının yerine geçebilmelidir. Bu durum, kodunuzun herhangi bir parçasının, üst sınıfın yerine alt sınıflarla çalışabilmesi anlamına gelir.

Örneğin, “ElektronikAlet” adında bir sınıfımız olduğunu varsayalım. Bu sınıfın “calis” adında bir fonksiyonu var. Şimdi “Telefon” ve “Bilgisayar” adında iki sınıf oluşturduk ve “ElektronikAlet” sınıfından türettik. Bu durumda “Telefon” ve “Bilgisayar” sınıfları, “ElektronikAlet” sınıfının “calis” fonksiyonunu kullanabilmeli ve “ElektronikAlet” sınıfının yerine geçebilmelidir.

public abstract class ElektronikAlet
{
public abstract void Calis();
}

public class Telefon : ElektronikAlet
{
public override void Calis()
{
Console.WriteLine("Telefon Çalışıyor");
}
}

public class Bilgisayar : ElektronikAlet
{
public override void Calis()
{
Console.WriteLine("Bilgisayar Çalışıyor");
}
}

// kullanım
ElektronikAlet elektronikAlet = new Telefon();
elektronikAlet.Calis(); // Telefon Çalışıyor

elektronikAlet = new Bilgisayar();
elektronikAlet.Calis(); // Bilgisayar Çalışıyor

IInterface segregation principle: Bir sınıfın tüm işlemlerini tek bir arayüze yüklemek yerine, ihtiyaçlara göre birden çok arayüz oluşturmalıyız.

Örneğin, bir “BaskiMakinesi” arayüzünüz var ve “renkliBaskiYap”, “siyahBeyazBaskiYap”, “taramaYap” gibi metodları var. Ancak bazı makineler yalnızca baskı yapabilir ve tarama yapamaz. Bu durumda, “BaskiMakinesi” ve “TaramaMakinesi” olmak üzere iki ayrı arayüz oluşturmalıyız.

public interface IBaskiMakinesi
{
void RenkliBaskiYap();
}

public interface ITaramaMakinesi
{
void TaramaYap();
}

DDependency Inversion Principle: Üst seviye sınıflar, alt seviye sınıflara doğrudan bağımlı olmamalıdır. Bağımlılıklar, abstraksiyonlar üzerinden sağlanmalıdır.

Buradaki “abstraksiyonlar”, genellikle bir sınıfın ne yaptığını tanımlayan arayüzleri veya bazı dil özelliklerini (örneğin C# dilindeki abstract sınıfları veya Java dilindeki interfaceleri) ifade eder.

Örneğin, “Araba” adında bir sınıfımız var ve “BenzinliMotor” adında başka bir sınıfımız daha var. “Araba” sınıfı, “BenzinliMotor” sınıfına doğrudan bağımlı olmamalı. Bunun yerine, “Motor” adında bir arayüz oluşturmalı ve “Araba” sınıfı bu arayüzü kullanmalıdır. Bu şekilde, motor tipini daha sonra “DizelMotor”, “ElektrikliMotor” gibi farklı türlerle değiştirmek istediğimizde, “Araba” sınıfında herhangi bir değişiklik yapmamız gerekmez.

public interface IMotor
{
void Calistir();
}

public class BenzinliMotor : IMotor
{
public void Calistir()
{
Console.WriteLine("Benzinli motor çalışıyor");
}
}

public class DizelMotor : IMotor
{
public void Calistir()
{
Console.WriteLine("Dizel motor çalışıyor");
}
}

public class ElektrikliMotor : IMotor
{
public void Calistir()
{
Console.WriteLine("Elektrikli motor çalışıyor");
}
}

public class Araba
{
private IMotor _motor;

public Araba(IMotor motor)
{
_motor = motor;
}

public void MotorCalistir()
{
_motor.Calistir();
}
}

// kullanım
Araba benzinliAraba = new Araba(new BenzinliMotor());
benzinliAraba.MotorCalistir(); // Benzinli motor çalışıyor

Araba dizelAraba = new Araba(new DizelMotor());
dizelAraba.MotorCalistir(); // Dizel motor çalışıyor

Araba elektrikliAraba = new Araba(new ElektrikliMotor());
elektrikliAraba.MotorCalistir(); // Elektrikli motor çalışıyor

new Araba(new BenzinliMotor()) ifadesi bir kurucu (constructor) fonksiyon çağrısıdır.

Araba sınıfının kurucu fonksiyonu (constructor), bir IMotor türündeki parametreyi alır. Bu, Araba nesnesi oluşturulurken hangi motor tipinin kullanılacağını belirler.

new BenzinliMotor() ifadesi, BenzinliMotor sınıfının bir örneğini oluşturur. Bu örnekleme, Araba sınıfının kurucu fonksiyonuna parametre olarak geçilir.

Sonuç olarak, new Araba(new BenzinliMotor()) ifadesi Araba sınıfından bir nesne oluşturur ve bu nesne bir BenzinliMotor nesnesiyle başlatılır.

--

--