SOLID Nedir? SOLID Yazılım Prensipleri Nelerdir?
SOLID, yazılım geliştirmede nesne yönelimli programlama yaklaşımını takip eden her hafin ayrı bir prensibi temsil ettiği beş temel prensibin kısaltmasıdır. Yazılımı daha anlaşılır, esnek ve bakımı kolay hale getirmek için geliştirilmiştir.
Erdoğancan Yüksel
4 dakika 39 saniye okuma süresi
SOLID Nedir? SOLID Yazılım Prensipleri Nelerdir?
Erdoğancan Yüksel
4 dakika 39 saniye okuma süresi
SOLID, yazılım geliştirmede nesne yönelimli programlama yaklaşımını takip eden her hafin ayrı bir prensibi temsil ettiği beş temel prensibin kısaltmasıdır. Yazılımı daha anlaşılır, esnek ve bakımı kolay hale getirmek için geliştirilmiştir.
SOLID Nedir?
SOLID, Nesne Yönelimli Programlama'da (Object-Oriented Programming) yazılım tasarımını daha anlaşılır, esnek ve bakım yapmayı kolay hale getirmeyi amaçlayan beş temel prensibi ifade etmektedir. Bu prensipler Robert C. Martin tarafından öne sürülmüş prensiplerdir ve yazılım dünyasında geniş kabul görmüştür.
SOLID prensipleri şu şekilde sıralanmaktadır:
- Single Responsibility Principle (SRP) - (Tek Sorumluluk Prensibi)
- Open/Closed Principle (OCP) - (Açık/Kapalı Prensibi)
- Liskov Substitution Principle (LSP) - (Liskov Yerine Geçme Prensibi)
- Interface Segregation Principle (ISP) - (Arayüz Ayrımı Prensibi)
- Dependency Inversion Principle (DIP) - (Bağımlılığı Tersine Çevirme Prensibi)
1.Single Responsibility Principle (Tek Sorumluluk Prensibi)
Bu prensip bir sınıfın yalnızca bir sorumluluğu olması gerektiğini belirtmektedir. Her sınıf yalnızca tek bir işlevi gerçekleştirmeli ve bu işlevin değişmesi gerektiğinde ilgili sınıf değiştirilmelidir.
Single Responsibility Principle kod örneği:
// Kötü Tasarım
public class Order
{
public decimal Price { get; set; }
public Order(decimal price)
{
Price = price;
}
public void ProcessOrder(decimal price)
{
Console.WriteLine($"Order process has been started. Order price: {price}");
}
public void SendConfirmationEmail(string email)
{
Console.WriteLine($"Confirmation email sent to {email}");
}
}
// İyi Tasarım
public class Order
{
public decimal Price { get; set; }
public Order(decimal price)
{
Price = price;
}
}
public class OrderRepository
{
public void ProcessOrder(decimal price)
{
Console.WriteLine($"Order process has been started. Order price: {price}");
}
}
public class EmailService
{
public void SendConfirmationEmail(string email)
{
Console.WriteLine($"Confirmation email sent to {email}");
}
}
Yukarıdaki kodda kötü kullanım örneğinde Order sınıfı hem sipariş bilgilerini tutmak, hem siparişi işlemek hem de e-posta göndermek gibi birden fazla sorumluluğa sahiptir. İyi kullanım örneğinde ise bu sorumluluklar ayrı sınıflara bölünerek her bir sınıfın bir sorumluluğu ve işlevi olması sağlanmıştır.
2.Open/Closed Principle (Açık/Kapalı Prensibi)
Bu prensibe göre yazılım varlıkları (sınıflar, modüller, fonksiyonlar gibi) genişletmeye açık, ancak değişime kapalı olmalıdır. Yani mevcut kodu değiştirmeden yeni işlevler eklenebilir olmalıdır.
Open/Closed Principle kod örneği:
// Kötü Tasarım
public class LogisticService
{
public enum TransportType
{
SeaTransportation,
LandTransportation
}
public void CreateTransport(TransportType transportType)
{
if (transportType == TransportType.SeaTransportation)
{
Console.WriteLine("Transportation with shipping.");
}
else if (transportType == TransportType.LandTransportation)
{
Console.WriteLine("Transportation with vehicle.");
}
}
}
// İyi Tasarım
public interface ITransport
{
void CreateTranstport();
}
public class LandTransportationService : ITransport
{
public void CreateTranstport()
{
Console.WriteLine("Transportation with vehicle.");
}
}
public class SeaTransportation : ITransport
{
public void CreateTranstport()
{
Console.WriteLine("Transportation with shipping.");
}
}
public class LogisticService
{
private readonly ITransport _transport;
public LogisticService(ITransport transport)
{
_transport = transport;
}
public void ProcessTransportation()
{
_transport.CreateTranstport();
}
}
Yukarıdaki kodda kötü kullanım örneğinde yeni bir taşımacılık yöntemi eklediğimizde mevcut sınıfın kodunu değiştirmemiz gerekmektedir. İyi kullanım örneğinde ise taşıma yöntemlerini ayrı bir sınıf haline getirerek mevcut sınıfı değiştirmemiz gerekmemektedir.
3.Liskov Substitution Principle (Liskov Yerine Geçme Prensibi)
Bu prensibe göre bir alt sınıf üst sınıfının yerine kullanılabilmelidir. Yani alt sınıflar üst sınıfların tüm özelliklerini desteklemeli ve bu sınıfları bozacak bir davranış sergilememelidir.
Liskov Substitution Principle kod örneği:
// Kötü Tasarım
public class Bird
{
public virtual void Fly()
{
Console.WriteLine("Bird is flying!");
}
}
public class Penguin : Bird
{
public override void Fly()
{
throw new NotSupportedException("Penguins can't fly!");
}
}
// İyi Tasarım
public abstract class Bird
{
public abstract void Movement();
}
public class Eagle : Bird
{
public override void Movement()
{
Console.WriteLine("Eagle is flying!");
}
}
public class Penguin : Bird
{
public override void Movement()
{
Console.WriteLine("Penguin is swimming!");
}
}
Yukarıdaki kodda kötü kullanım örneğinde Penguin sınfı Bird sınıfının yerine kullanılamaz çünkü Penguin sınıfı Bird sınıfının Fly fonksiyonunun mantığını bozmuştur. İyi kullanım örneğinde ise Bird sınıfı soyut hale getirilmiş ve genel bir fonksiyon kullanılmıştır. Bu şekilde her bir alt sınıf üst sınıfın yerine geçebilir ve beklenen davranışı sergileyebilmektedir.
4.Interface Segregation Principle (Arayüz Ayrımı Prensibi)
Bu prensibe göre büyük ve genel arayüzler kullanmak yerine daha küçük ve özelleşmiş arayüzlerin tercih edilmelidir. Bu şekilde bir sınıf ihtiyaç duymadığı metotları içeren arayüzlere bağımlı olmamaktadır.
Interface Segregation Principle kod örneği:
// Kötü Tasarım
public interface IWorker
{
void Work();
void Eat();
void Sleep();
}
public class Human : IWorker
{
public void Work()
{
Console.WriteLine("Human is working.");
}
public void Eat()
{
Console.WriteLine("Human is eating.");
}
public void Sleep()
{
Console.WriteLine("Human is resting.");
}
}
public class Robot : IWorker
{
public void Work()
{
Console.WriteLine("Robot is working.");
}
public void Eat()
{
throw new NotImplementedException("Robots don't eat!");
}
public void Sleep()
{
throw new NotImplementedException("Robots don't sleep!");
}
}
// İyi Tasarım
public interface IWorkable
{
void Work();
}
public interface IEatable
{
void Eat();
}
public interface ISleepable
{
void Sleep();
}
public class Human : IWorkable, IEatable, ISleepable
{
public void Work()
{
Console.WriteLine("Human is working.");
}
public void Eat()
{
Console.WriteLine("Human is eating.");
}
public void Sleep()
{
Console.WriteLine("Human is resting.");
}
}
public class Robot : IWorkable
{
public void Work()
{
Console.WriteLine("Robot is working.");
}
}
Yukarıdaki kodda kötü kullanım örneğinde Robot sınıfı ihtiyaç duymadığı Eat ve Sleep metotlarını uygulamak zorunda kalmaktadır. İyi kullanım örneğinde ise arayüzle daha küçük ve spesifik hale getirilerek her sınıfın ihtiyaç duyduğu arayüzleri uygulaması imkanı sağlanmıştır.
5.Dependency Inversion Principle (Bağımlılığı Tersine Çevirme Prensibi)
Bu prensibe üst seviye modüllerin alt seviye modüllere bağımlı olmaması gerektiğini belirtmektedir. Her iki modül de bir soyutlamaya bağımlı olmalıdır.
Dependency Inversion Principle kod örneği:
// Kötü Tasarım
// Alt seviye model
public class EmailService
{
public void SendEmail()
{
Console.WriteLine("E-mail sent!");
}
}
// Üst seviye model
public class Notification
{
private EmailService _emailService = new EmailService();
public void SendNotification()
{
_emailService.SendEmail();
}
}
// İyi tasarım
// Soyutlama (Interface)
public interface INotificationSender
{
void Send();
}
// Alt seviye model
public class EmailService : INotificationSender
{
public void Send()
{
Console.WriteLine("E-mail sent!");
}
}
// Alt seviye model
public class SmsService : INotificationSender
{
public void Send()
{
Console.WriteLine("SMS sent!");
}
}
// Üst seviye model
public class Notification
{
private readonly INotificationSender _notificationSender;
public Notification(INotificationSender notificationSender)
{
_notificationSender = notificationSender;
}
public void SendNotification()
{
_notificationSender.Send();
}
}
// Kullanımı:
INotificationSender emailSender = new EmailService();
Notification emailNotification = new Notification(emailSender);
emailNotification.SendNotification();
INotificationSender smsSender = new SmsService();
Notification smsNotification = new Notification(smsSender);
smsNotification.SendNotification();
// Output:
// E-mail sent!
// SMS sent!
Yukarıdaki kodda kötü kullanım örneğinde Notification sınıfı EmailService sınıfına bağımlıdır. Eğer SMS bildirimi de göndermek isteseydik bu sınıfı değiştirmemiz gerekirdi. İyi kullanım örneğinde Notification sınıfı EmailService'doğrudan bağımlı değildir, INotificationSender arayüzüne bağımlıdır.
Sonuç olarak SOLID prensipleri yazılım geliştirme sürecinde kodun daha açık, esnek ve sürdürülebilir olmasını sağlamaktadır. SOLID prensiplerinden her bir prensip farklı bir sorunu çözmeye odaklanarak birlikte uygulandığında daha modüler ve genişletilebilir sistemler oluşturulmasını sağlamaktadır.