SOLID Prensipleri Nesne Yonelimli Tasarimin Temelleri

SOLID Prensipleri: Nesne Yönelimli Tasarımın Temelleri

Nesne yönelimli programlama (OOP), yazılım geliştirme sürecinde karmaşıklığı yönetmek ve sürdürülebilir kod yazmak için güçlü bir paradigmadır. SOLID prensipleri, Robert C. Martin tarafından ortaya konulan ve OOP’nin daha anlaşılır, esnek ve bakımı kolay olmasını sağlayan beş temel tasarım kuralıdır. Bu makalede, SOLID prensiplerini detaylı bir şekilde inceleyecek ve her birini örneklerle açıklayacağız.

SOLID Prensiplerine Genel Bakış

SOLID, beş prensibin baş harflerinden oluşan bir kısaltmadır:

  • Single Responsibility Principle (SRP): Bir sınıfın yalnızca bir sorumluluğu olmalıdır.
  • Open/Closed Principle (OCP): Sınıflar genişletmeye açık, değişikliğe kapalı olmalıdır.
  • Liskov Substitution Principle (LSP): Alt sınıflar, üst sınıfların yerine geçebilmelidir.
  • Interface Segregation Principle (ISP): İstemciler, kullanmadıkları arayüzlere bağımlı olmamalıdır.
  • Dependency Inversion Principle (DIP): Üst seviye modüller, alt seviye modüllere bağımlı olmamalıdır.

Şimdi bu prensipleri tek tek ele alalım.

Single Responsibility Principle (SRP)

Bir sınıfın yalnızca bir sorumluluğu olmalıdır. Yani bir sınıf, yalnızca bir işi yapmalı ve o işi iyi yapmalıdır. Bu prensip, kodun daha modüler ve test edilebilir olmasını sağlar.

Örnek olarak, bir User sınıfı düşünelim. Bu sınıf, kullanıcı bilgilerini tutmanın yanı sıra veritabanı işlemlerini de yönetiyorsa, SRP’yi ihlal ediyor demektir.

// SRP'yi ihlal eden kötü tasarım  
public class User {  
    private String name;  
    private String email;  

    public void saveToDatabase() {  
        // Veritabanına kaydetme işlemi  
    }  

    public void sendEmail() {  
        // Email gönderme işlemi  
    }  
}  

Bunun yerine, her sorumluluğu ayrı sınıflara ayırmalıyız:

// SRP'ye uygun tasarım  
public class User {  
    private String name;  
    private String email;  
}  

public class UserRepository {  
    public void save(User user) {  
        // Veritabanına kaydetme işlemi  
    }  
}  

public class EmailService {  
    public void sendEmail(User user) {  
        // Email gönderme işlemi  
    }  
}  

Open/Closed Principle (OCP)

Sınıflar, genişletmeye açık ancak değişikliğe kapalı olmalıdır. Yani mevcut kodu değiştirmeden yeni özellikler ekleyebilmeliyiz.

Örneğin, bir şekil alanı hesaplayan bir sistem düşünelim:

// OCP'yi ihlal eden kötü tasarım  
public class AreaCalculator {  
    public double calculateArea(Object shape) {  
        if (shape instanceof Circle) {  
            Circle circle = (Circle) shape;  
            return Math.PI * circle.radius * circle.radius;  
        } else if (shape instanceof Rectangle) {  
            Rectangle rectangle = (Rectangle) shape;  
            return rectangle.width * rectangle.height;  
        }  
        throw new IllegalArgumentException("Geçersiz şekil");  
    }  
}  

Bu tasarımda, yeni bir şekil eklemek için AreaCalculator sınıfını değiştirmemiz gerekir. OCP’ye uygun bir çözüm, her şeklin kendi alan hesaplama metodunu uyguladığı bir arayüz kullanmaktır:

// OCP'ye uygun tasarım  
public interface Shape {  
    double calculateArea();  
}  

public class Circle implements Shape {  
    private double radius;  

    @Override  
    public double calculateArea() {  
        return Math.PI * radius * radius;  
    }  
}  

public class Rectangle implements Shape {  
    private double width;  
    private double height;  

    @Override  
    public double calculateArea() {  
        return width * height;  
    }  
}  

public class AreaCalculator {  
    public double calculateArea(Shape shape) {  
        return shape.calculateArea();  
    }  
}  

Liskov Substitution Principle (LSP)

Alt sınıflar, üst sınıfların yerine geçebilmelidir. Yani bir alt sınıf, üst sınıfın davranışını bozmamalıdır.

Örneğin, bir dikdörtgen ve kare sınıfı düşünelim:

// LSP'yi ihlal eden kötü tasarım  
public class Rectangle {  
    protected int width;  
    protected int height;  

    public void setWidth(int width) {  
        this.width = width;  
    }  

    public void setHeight(int height) {  
        this.height = height;  
    }  
}  

public class Square extends Rectangle {  
    @Override  
    public void setWidth(int width) {  
        super.setWidth(width);  
        super.setHeight(width);  
    }  

    @Override  
    public void setHeight(int height) {  
        super.setWidth(height);  
        super.setHeight(height);  
    }  
}  

Burada SquareRectangle‘ın davranışını değiştirir. LSP’ye göre, bu bir ihlaldir. Bunun yerine, farklı bir hiyerarşi kullanılmalıdır.

Interface Segregation Principle (ISP)

İstemciler, kullanmadıkları arayüzlere bağımlı olmamalıdır. Yani arayüzler, mümkün olduğunca küçük ve özelleştirilmiş olmalıdır.

Örneğin, bir çok amaçlı arayüz yerine, özelleştirilmiş arayüzler kullanılmalıdır:

// ISP'yi ihlal eden kötü tasarım  
public interface Worker {  
    void work();  
    void eat();  
    void sleep();  
}  

public class HumanWorker implements Worker {  
    public void work() { /* ... */ }  
    public void eat() { /* ... */ }  
    public void sleep() { /* ... */ }  
}  

public class RobotWorker implements Worker {  
    public void work() { /* ... */ }  
    public void eat() { /* Robot yemek yemez */ }  
    public void sleep() { /* Robot uyumaz */ }  
}  

Bunun yerine, arayüzleri ayırmalıyız:

// ISP'ye uygun tasarım  
public interface Workable {  
    void work();  
}  

public interface Eatable {  
    void eat();  
}  

public interface Sleepable {  
    void sleep();  
}  

public class HumanWorker implements Workable, Eatable, Sleepable {  
    public void work() { /* ... */ }  
    public void eat() { /* ... */ }  
    public void sleep() { /* ... */ }  
}  

public class RobotWorker implements Workable {  
    public void work() { /* ... */ }  
}  

Dependency Inversion Principle (DIP)

Üst seviye modüller, alt seviye modüllere bağımlı olmamalıdır. Her ikisi de soyutlamalara bağlı olmalıdır.

Örneğin, bir mesaj gönderme servisi düşünelim:

// DIP'yi ihlal eden kötü tasarım  
public class EmailService {  
    public void sendEmail(String message) {  
        // Email gönderme işlemi  
    }  
}  

public class Notification {  
    private EmailService emailService;  

    public Notification() {  
        this.emailService = new EmailService();  
    }  

    public void sendNotification(String message) {  
        emailService.sendEmail(message);  
    }  
}  

Bu tasarımda, Notification sınıfı EmailService‘e doğrudan bağımlıdır. DIP’ye uygun bir çözüm, bağımlılığı soyut bir arayüz üzerinden yönetmektir:

// DIP'ye uygun tasarım  
public interface MessageService {  
    void sendMessage(String message);  
}  

public class EmailService implements MessageService {  
    public void sendMessage(String message) {  
        // Email gönderme işlemi  
    }  
}  

public class Notification {  
    private MessageService messageService;  

    public Notification(MessageService messageService) {  
        this.messageService = messageService;  
    }  

    public void sendNotification(String message) {  
        messageService.sendMessage(message);  
    }  
}  

SOLID prensipleri, yazılım tasarımında esneklik, sürdürülebilirlik ve okunabilirlik sağlar. Bu prensipleri uygulayarak, daha temiz ve bakımı kolay kodlar yazabiliriz.

Previous Article

Temiz Kod (Clean Code) Yazmanın 10 Altın Kuralı

Next Article

Go (Golang) Dili: Avantajları ve Kullanım Alanları

Write a Comment

Leave a Comment

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir