Logo
Ana Sayfa
ÖZEL KODLAMA DERSLERİ
Yazılım Özel Ders
Tüm birebir programlara genel bakış
Python Yazılım Kursu
Sıfırdan ileri seviyeye birebir Python
Java Yazılım Kursu
OOP odaklı birebir Java eğitimi
AP Computer Science Principles
AP CSP sınav hazırlığı
GRUP DERSLERİ
Python & Django Masterclass
SINIRLI KONTENJAN
Java & Spring Boot Masterclass
SINIRLI KONTENJAN
C# .NET Masterclass
SINIRLI KONTENJAN
VİDEO DERSLER
Sıfırdan Temel Python Kursu
Kendi Hızında Öğren
ÜCRETSİZ
Seviye Testi
Ücretsiz — Python, Java, algoritma seviye testleri
Kariyerini Keşfet
Sertifika Doğrula
Belge numarası ve soyad ile doğrulama

Java'da SOLID Prensipleri: Kırılgan Kodu Sağlam Mimariye Dönüştürmek

Yazar: Berk Keskin 28.08.2026 ~15 dk okuma 6 Okunma
java-solid-tasarim-prensipleri-srp-ocp-lsp-isp-dip

Java'da SOLID prensipleri, nesne yönelimli kodun zamanla neden kırılgan hale geldiğini açıklayan ve buna karşı beş somut çözüm öneren bir tasarım rehberidir. Kısaca SRP bir sınıfın tek bir değişme nedeni olmasını, OCP mevcut kodu bozmadan genişletmeyi, LSP alt sınıfların üst sınıfın sözleşmesini bozmamasını, ISP gereksiz metotlara zorlanmamayı ve DIP somut sınıflar yerine soyutlamalara bağlanmayı ifade eder. Bu beş prensip, "küçük bir değişiklik yaptım, üç sınıf daha bozuldu" cümlesinin neden bu kadar sık kurulduğunu anlamak isteyen her Java geliştiricisi için pratik bir teşhis aracıdır.

Kırılgan Kodun Belirtileri: SOLID Aslında Neyin Çözümü?

SOLID prensiplerinin tanımına geçmeden önce, bu prensiplerin hangi acıyı dindirmek için ortaya çıktığını görmek gerekir. Kırılgan kod, kendini genellikle üç belirtiyle gösterir: küçük bir değişiklik yapıldığında görünüşte ilgisiz sınıfların bozulması, sınıfların birbirine gereğinden fazla bağlı (tight coupling) olması ve unit test yazmanın giderek imkansız hale gelmesi.

Gerçek projelerde bu belirtiler somut ve sinir bozucu bir şekle bürünür. Örneğin bir e-ticaret uygulamasında sepet hesaplama mantığına yeni bir indirim kuralı eklemek istediğinizde; hesaplama sınıfının yanı sıra fatura oluşturan sınıf, bildirim gönderen servis ve raporlama modülü de değiştirilmek zorunda kalınır. Tek bir metoda yeni bir özellik eklerken beş farklı sınıfı açıp düzenlemek, kodun sorumlulukların net ayrılmadığı ve bağımlılıkların gizlice birbirine dolandığı anlamına gelir.

Bu durumun bir diğer belirtisi test yazmaya çalışırken ortaya çıkar. Bir sınıfı test etmek için önce beş farklı bağımlılığı sahte (mock) nesnelerle beslemeniz gerekiyorsa, o sınıf muhtemelen birden fazla iş yapıyordur ya da somut sınıflara doğrudan bağımlıdır. Değişiklik yapıldığında testlerin de zincirleme kırılması, ekiplerin zamanla "test yazmaya değmiyor" noktasına gelmesine yol açar; bu da uzun vadede hataların üretime daha kolay sızmasına neden olur.

Bu tekrar eden hata desenleri -- yayılan değişiklikler, sıkı bağımlılıklar, test edilemeyen sınıflar -- rastgele değildir. Farklı projelerde, farklı dililler ve farklı ekiplerde neredeyse aynı şekilde ortaya çıkarlar. SOLID prensipleri de tam olarak bu tekrar eden desenlere verilmiş beş ayrı cevap olarak düşünülebilir: her prensip, kırılganlığın belirli bir kaynağını hedef alır ve kodun değişime karşı daha dayanıklı hale gelmesini sağlar. Aşağıdaki bölümlerde bu beş prensibin her birini, önce kötü bir Java örneği üzerinden teşhis ederek, sonra da düzeltilmiş haliyle inceleyeceğiz.

Tek Sorumluluk Prensibi (SRP): Bir Sınıf Bir Nedenle Değişmeli

Tek Sorumluluk Prensibi (SRP), bir sınıfın yalnızca bir nedenle değişmesi gerektiğini söyler. Yani bir sınıf, birden fazla işi birden yapmaya değil, tek bir görevi iyi yapmaya odaklanmalıdır. Bu, sınıfın kısa olması gerektiği anlamına gelmez; sınıfın değişmesine sebep olacak "aktörün" veya "nedenin" tek olması gerektiği anlamına gelir.

Aşağıdaki Employee sınıfı, çalışan verisini tutmakla birlikte rapor oluşturma ve veritabanına kayıt işlemlerini de kendi içinde barındırır:

public class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String generateReport() {
        return "Rapor: " + name + " - Maas: " + salary;
    }

    public void saveToDatabase() {
        System.out.println(name + " veritabanina kaydedildi.");
    }
}

Bu sınıfın bakım maliyeti görünenden daha yüksektir. Rapor formatı PDF'e dönüştürülmek istendiğinde Employee sınıfı değişir; veritabanı katmanı değiştirildiğinde yine Employee sınıfı değişir. Oysa çalışan verisiyle hiç ilgisi olmayan bu iki değişiklik nedeni, aynı sınıfı sürekli riske atar. Ayrıca sadece raporlama mantığını test etmek istediğinizde, veritabanı bağımlılığını da devreye almak zorunda kalırsınız; bu da testi hem yavaşlatır hem de kırılgan hale getirir.

Sorumlulukları ayırarak bu sorunu çözebiliriz:

public class Employee {
    private String name;
    private double salary;

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    public String getName() { return name; }
    public double getSalary() { return salary; }
}

public class EmployeeReportGenerator {
    public String generateReport(Employee employee) {
        return "Rapor: " + employee.getName() + " - Maas: " + employee.getSalary();
    }
}

public class EmployeeRepository {
    public void save(Employee employee) {
        System.out.println(employee.getName() + " veritabanina kaydedildi.");
    }
}

Artık her sınıf tek bir nedenle değişir: Employee sadece veri modelini temsil eder, EmployeeReportGenerator raporlama biçimini, EmployeeRepository ise kalıcılık mantığını yönetir. Bu ayrım, özellikle SOLID prensiplerini ilk kez uygulamaya çalışan geliştiricilerin en çok zorlandığı yerdir; sorumlulukları doğru sınırlarla ayırmak deneyim gerektirir. Bu tür yapısal refleksleri sağlam bir temelde geliştirmek isteyenler için 1-1 özel Java dersleri kapsamında, gerçek kod üzerinde bu tür ayrıştırma çalışmaları adım adım uygulanabilir.

Açık/Kapalı Prensibi (OCP): Değiştirmeden Genişletebilmek

Açık/Kapalı Prensibi (OCP): Değiştirmeden Genişletebilmek

Açık/Kapalı Prensibi (OCP), bir sınıfın genişletmeye açık ama değiştirmeye kapalı olması gerektiğini söyler. Pratikte bu, yeni bir davranış eklemek istediğinizde mevcut, zaten test edilmiş kodu değiştirmek yerine yeni bir sınıf ekleyerek sistemi genişletebilmeniz anlamına gelir.

Aşağıdaki PaymentProcessor sınıfı, ödeme türüne göre dallanan bir if/else zinciriyle çalışır:

public class PaymentProcessor {
    public void process(String type, double amount) {
        if (type.equals("CREDIT_CARD")) {
            System.out.println(amount + " tutari kredi karti ile odendi.");
        } else if (type.equals("WALLET")) {
            System.out.println(amount + " tutari cuzdan ile odendi.");
        }
        // Yeni odeme yontemi eklendikce buraya yeni else-if satirlari eklenir
    }
}

Bu tasarımın teşhisi açıktır: yeni bir ödeme yöntemi (örneğin havale veya kripto para) eklendiğinde, mevcut ve zaten üretimde çalışan PaymentProcessor sınıfı tekrar açılıp düzenlenmek zorunda kalır. Her düzenleme, önceden çalışan ve test edilmiş kredi kartı veya cüzdan akışını da riske atar; çünkü aynı metot içinde yapılan her değişiklik, ilgisiz görünen dalları da etkileyebilir.

Bunun yerine bir soyutlama üzerinden genişleme yapılabilir:

public interface PaymentMethod {
    void pay(double amount);
}

public class CreditCardPayment implements PaymentMethod {
    public void pay(double amount) {
        System.out.println(amount + " tutari kredi karti ile odendi.");
    }
}

public class WalletPayment implements PaymentMethod {
    public void pay(double amount) {
        System.out.println(amount + " tutari cuzdan ile odendi.");
    }
}

public class PaymentProcessor {
    public void process(PaymentMethod method, double amount) {
        method.pay(amount);
    }
}

Yeni bir ödeme yöntemi eklemek istendiğinde artık sadece PaymentMethod arayüzünü uygulayan yeni bir sınıf yazılır; PaymentProcessor sınıfına hiç dokunulmaz. Bu, mevcut ve test edilmiş kodun güvenliğini korurken sistemin büyümesine izin verir. Bu tür arayüz tabanlı yeniden yapılandırmaların canlı bir sınıf ortamında adım adım uygulanması, kalıcı bir alışkanlık kazanmak açısından önemlidir; canlı Java dersleri bu tür pratik refactoring senaryolarını gerçek kod üzerinde işleyecek şekilde kurgulanabilir. Yine de OCP'nin her sınıfta önceden bir arayüz öngörmeyi gerektirmediğini, genişleme ihtiyacı somutlaştığında devreye girmesi gerektiğini akılda tutmak gerekir.

Liskov Yerine Geçme Prensibi (LSP): Alt Sınıf Sözleşmeyi Bozmamalı

Liskov Yerine Geçme Prensibi, bir üst sınıfın referansı üzerinden çalışan kodun, o referansa herhangi bir alt sınıf nesnesi verildiğinde de aynı şekilde ve güvenle çalışabilmesi gerektiğini söyler. Yani alt sınıf, üst sınıfın vaat ettiği sözleşmeyi bozmamalı; beklenmedik istisnalar fırlatmamalı, davranışı sessizce değiştirmemelidir. Bu prensip ihlal edildiğinde kod derlenir, testler bile yüzeysel bakıldığında geçebilir; ama gerçek kullanımda sürpriz hatalar ortaya çıkar.

Aşağıdaki bildirim gönderme hiyerarşisi bu ihlali net gösterir. Üst sınıf tüm alt sınıfların anında bildirim gönderebileceğini vaat eder, ama posta yoluyla bildirim gönderen sınıf bu vaadi tutamaz:

abstract class NotificationSender {
    abstract void sendImmediately(String message);
}

class EmailNotificationSender extends NotificationSender {
    void sendImmediately(String message) {
        System.out.println("E-posta gonderildi: " + message);
    }
}

class PostalMailNotificationSender extends NotificationSender {
    void sendImmediately(String message) {
        throw new UnsupportedOperationException("Posta aninda gonderilemez");
    }
}

Teşhis şudur: bir yerde NotificationSender tipi üzerinden döngüyle tüm göndericilere sendImmediately() çağrısı yapan bir kod yazıldığında, listeye bir PostalMailNotificationSender eklenince program çalışma anında istisna fırlatır. Kod üst sınıf tipiyle yazılmıştır, hatanın kaynağı ise alt sınıfın üst sınıfın davranış vaadini yerine getirememesidir. Bu, klasik "alt sınıf üst sınıfın yerine geçemiyor" durumudur.

Doğru çözüm, sınıf hiyerarşisini gerçek davranış farkına göre yeniden kurmaktır. Anında gönderim yapabilenler ile zamanlanmış gönderim yapanlar ayrı sözleşmelere bağlanmalıdır:

interface ImmediateNotificationSender {
    void sendImmediately(String message);
}

interface ScheduledNotificationSender {
    void schedule(String message, java.time.LocalDate date);
}

class EmailNotificationSender implements ImmediateNotificationSender {
    public void sendImmediately(String message) {
        System.out.println("E-posta gonderildi: " + message);
    }
}

class PostalMailNotificationSender implements ScheduledNotificationSender {
    public void schedule(String message, java.time.LocalDate date) {
        System.out.println("Posta " + date + " tarihinde gonderilecek: " + message);
    }
}

Artık hiçbir sınıf, gerçekleştiremeyeceği bir davranışı üstlenmek zorunda kalmıyor. Kod hangi arayüzü kullanıyorsa, o arayüzün vaat ettiği her davranış güvenle çalışır; sürpriz istisna riski ortadan kalkar.

Arayüz Ayrımı Prensibi (ISP): Gereksiz Metotlara Zorlanmamak

Arayüz Ayrımı Prensibi, bir sınıfın ihtiyacı olmayan metotlara bağımlı kalmaması gerektiğini savunur. Geniş ve her şeyi kapsayan bir arayüz tasarlamak pratik görünse de, o arayüzü implemente eden her sınıf, işine yaramayan metotları da taşımak zorunda kalır. Bu durum kodu gereksiz yere kırılgan ve okunması zor hale getirir.

Aşağıdaki Worker arayüzü, insan çalışanlar için mantıklı olsa da bir robot işçi için anlamsız metotlar barındırır:

interface Worker {
    void work();
    void eat();
    void sleep();
}

class RobotWorker implements Worker {
    public void work() {
        System.out.println("Robot calisiyor");
    }

    public void eat() {
        throw new UnsupportedOperationException("Robot yemek yemez");
    }

    public void sleep() {
        throw new UnsupportedOperationException("Robot uyumaz");
    }
}

Teşhis noktası şudur: RobotWorker, eat() ve sleep() metotlarını sadece arayüzü implemente etmek için yazmak zorunda kalmış, ama içlerini anlamsız istisnalarla doldurmuştur. Daha büyük bir sorun da şudur: Worker arayüzüne örneğin takeVacation() gibi yeni bir metot eklendiğinde, bununla hiç ilgisi olmayan RobotWorker sınıfı da bu değişiklikten etkilenir ve derleme hatası alır. Arayüz, ilgisiz sınıfları birbirine zincirlemiştir.

Çözüm, geniş arayüzü gerçek sorumluluklara göre küçük parçalara bölmektir:

interface Workable {
    void work();
}

interface Eatable {
    void eat();
}

interface Sleepable {
    void sleep();
}

class HumanWorker implements Workable, Eatable, Sleepable {
    public void work() { System.out.println("Insan calisiyor"); }
    public void eat() { System.out.println("Insan yemek yiyor"); }
    public void sleep() { System.out.println("Insan uyuyor"); }
}

class RobotWorker implements Workable {
    public void work() { System.out.println("Robot calisiyor"); }
}

Bu yapıda RobotWorker sadece ihtiyacı olan Workable sözleşmesine bağlıdır; küçük ve odaklı arayüzler sayesinde ilgisiz bir değişiklik onu artık etkilemez.

Bağımlılığın Tersine Çevrilmesi Prensibi (DIP): Somut Sınıfa Değil Soyutlamaya Bağlanmak

Bağımlılığın Tersine Çevrilmesi Prensibi (DIP): Somut Sınıfa Değil Soyutlamaya Bağlanmak

Bağımlılığın Tersine Çevrilmesi Prensibi, üst seviye iş mantığının, alt seviye somut uygulama detaylarına doğrudan bağımlı olmaması gerektiğini söyler. Bunun yerine her iki taraf da bir soyutlamaya (arayüze) bağlanmalıdır. Bu prensip ihlal edildiğinde kod çalışır ama değişime ve teste karşı direnç gösterir; her küçük altyapı değişikliği iş mantığı sınıflarına kadar yayılır.

Aşağıdaki OrderService sınıfı, veritabanı erişimini doğrudan somut bir sınıf üzerinden yaparak bu ihlali gösterir:

class MySqlOrderRepository {
    void save(String order) {
        System.out.println("Siparis MySQL'e kaydedildi: " + order);
    }
}

class OrderService {
    private MySqlOrderRepository repository = new MySqlOrderRepository();

    void placeOrder(String order) {
        repository.save(order);
    }
}

Teşhis açıktır: OrderService, MySqlOrderRepository'yi kendi içinde new ile ürettiği için veritabanı teknolojisi değişse (örneğin PostgreSQL'e geçilse) ya da bu sınıf sahte bir repository ile test edilmek istense, OrderService koduna dokunmadan bunu yapmak mümkün değildir. Somut sınıfa bağımlılık, hem esnekliği hem test edilebilirliği ortadan kaldırır.

Doğru yaklaşım, OrderService'in bir arayüze bağımlı olması ve somut uygulamanın dışarıdan constructor aracılığıyla verilmesidir:

interface OrderRepository {
    void save(String order);
}

class MySqlOrderRepository implements OrderRepository {
    public void save(String order) {
        System.out.println("Siparis MySQL'e kaydedildi: " + order);
    }
}

class OrderService {
    private final OrderRepository repository;

    OrderService(OrderRepository repository) {
        this.repository = repository;
    }

    void placeOrder(String order) {
        repository.save(order);
    }
}

Artık OrderService hangi veritabanı teknolojisinin kullanıldığını bilmez; test sırasında sahte bir OrderRepository uygulaması vermek, üretimde ise gerçek veritabanı sınıfını vermek yeterlidir. Bu tür kavramların kâğıt üzerinde anlaşılması ile kod üzerinde uygulanabilmesi arasında genellikle bir fark olur; bu farkı görmek isteyenler Java bilgi seviyesi ölçme testi üzerinden kendi kavrayışlarını kısa sorularla sınayabilir. Bağımlılığın tersine çevrilmesi, SOLID'in belki de en somut biçimde esneklik kazandıran prensibidir; çünkü değişimi doğrudan koddan soyutlamaya taşır.

Prensipler Birbirinden Bağımsız Değil: SOLID Bir Bütün Olarak Çalışır

SOLID prensiplerine tek tek bakıldığında her biri ayrı bir kural gibi görünür, ama gerçek Java kod tabanlarında bu beş prensip birbirini besler ve birbirinin eksikliğini büyütür. Bir projede sadece SRP uygulanıp diğerleri göz ardı edildiğinde, kod bir noktaya kadar düzelir ama kırılganlığın kaynağı tam olarak ortadan kalkmaz. Bu yüzden SOLID'i beş ayrı görev listesi gibi değil, birbirine bağlı bir sistem gibi düşünmek gerekir.

En açık bağımlılık OCP ile DIP arasındadır. Bir sınıf somut bir sınıfa doğrudan bağımlıysa, yeni bir davranış eklemek için o somut sınıfı ya da onu çağıran kodu değiştirmek zorunda kalırsınız; bu da OCP'nin "değiştirmeden genişletme" hedefini fiilen imkânsız kılar. DIP'nin soyutlamaya bağlanma kuralı uygulanmadan OCP'yi gerçek anlamda hayata geçirmek çok güçtür, çünkü genişletme noktası ancak bir arayüz veya soyut sınıf üzerinden açılabilir.

ISP ile LSP arasındaki ilişki de benzer şekilde işler. Bir arayüz gereğinden büyük ve çok metotlu olduğunda, alt sınıfların bu metotların bir kısmını anlamsız veya sahte biçimde uygulama olasılığı artar; bu da davranışsal sözleşmenin bozulması riskini büyütür. Arayüzler küçük ve odaklı tutulduğunda (ISP) her alt sınıfın gerçekten uygulayabileceği, anlamlı bir sözleşmesi olur ve bu da LSP ihlallerinin önünü büyük ölçüde keser.

SRP ise bu zincirin başlangıç noktası gibi çalışır. Bir sınıf tek bir sorumluluğa sahip olduğunda, o sınıfı soyutlamak (DIP), genişletmek (OCP) ve alt sınıflarla değiştirmek (LSP) çok daha kolaylaşır; çünkü sınıfın sınırları net ve tahmin edilebilirdir.

  • SRP netleşmeden OCP için doğru genişletme noktası bulmak zorlaşır.
  • DIP uygulanmadan OCP sadece kâğıt üzerinde kalır, kodda somut bağımlılıklar devam eder.
  • ISP küçük arayüzler ürettiğinde LSP ihlali riski azalır, çünkü alt sınıflar sahte metot uygulamaya zorlanmaz.
  • LSP ihlalleri genellikle SRP'si bozuk, birden çok işi birden yapan sınıflarda ortaya çıkar.

Aşırı Mühendislik Tuzağı: SOLID Amaç Değil Araçtır

SOLID prensiplerinin en yaygın yanlış kullanımı, onları her satır kod için zorunlu bir ritüel gibi uygulamaktır. Üç metotluk basit bir yardımcı sınıfı, hiçbir zaman ikinci bir uygulaması olmayacak bir işlem için sırf "DIP'e uyayım" diyerek bir arayüzün ardına saklamak, kodu daha sağlam yapmaz; sadece okuyan kişinin bir katman daha dolaşmasını gerektirir. Aynı şekilde değişme ihtimali neredeyse sıfır olan bir hesaplama mantığını "ileride genişler" varsayımıyla soyut sınıflar, strateji nesneleri ve fabrika katmanlarıyla örmek, OCP'yi uygulamak değil, gereksiz karmaşıklık üretmektir.

Örneğin bir uygulamada yalnızca metrik birimden santimetreye çeviri yapan tek bir UnitConverter sınıfı varsa ve bu dönüşüm mantığının değişme ya da başka bir birim sistemine genişleme ihtimali yoksa, bu sınıf için UnitConverterInterface, UnitConverterFactory ve AbstractUnitConverter üretmek okunabilirliği artırmaz, aksine üç dosyaya dağılmış bir mantığı takip etmeyi zorlaştırır. Burada sorulması gereken soru "SOLID'e uyuyor mu" değil, "bu kod gerçekten değişecek mi ve şu anki hâli bir bakım sorunu yaratıyor mu" sorusudur.

SOLID prensipleri, gözlemlenen bir bakım sıkıntısına — sürekli değişen kod, testi zor sınıflar, yeni özellik eklerken kırılan davranışlar — çözüm olarak devreye girmelidir; kodun henüz hiçbir sorun göstermediği aşamada önsel bir kural olarak dayatılmamalıdır. Bir sınıfın gerçekten birden fazla sorumluluğu var mı, gerçekten birden fazla uygulaması olacak bir davranış mı içeriyor, bu sorular yanıtlanmadan soyutlama eklemek, çözülmemiş bir problem için fazladan karmaşıklık maliyeti ödemektir. Eğitmenimiz Berk Keskin'in koddan önce düşünmeyi önceleyen eğitim yaklaşımı da tam olarak bu noktaya işaret eder: yazılım eğitimi yaklaşımı öğrenciyi ezber kurallar uygulayan değil, neden-sonuç ilişkisini kurarak karar veren bir geliştirici olmaya yönlendirir. SOLID de bu muhakeme sürecinin bir aracı olarak anlam kazanır, kendi başına bir hedef olarak değil.

SOLID Prensiplerini Karşılaştırma ve Uygulamaya Geçirme Rehberi

Karşılaştırma Tablosu

Beş prensibin hangi hata türüne karşılık geldiğini ve kod tabanında hangi belirtiyle kendini gösterdiğini bir arada görmek, hangi durumda hangi prensibe bakmanız gerektiğini hızlıca ayırt etmenizi sağlar.

Prensip Önlediği Hata Türü Tipik Belirti
SRP Tek sınıfa yığılmış çok görevli mantık Sınıf her değişiklikte birçok farklı nedenle güncelleniyor
OCP Yeni özellik için mevcut kodu değiştirme zorunluluğu Her yeni durum için if/else veya switch bloğu büyüyor
LSP Alt sınıfın üst sınıf sözleşmesini bozması Alt sınıf metodu istisna fırlatıyor veya boş bırakıyor
ISP Kullanılmayan metotlara zorunlu bağımlılık Sınıf, arayüzdeki bazı metotları anlamsız biçimde uyguluyor
DIP Somut sınıfa sıkı bağımlılık Test yazarken gerçek nesneyi taklit etmek (mock) zorlaşıyor

Uygulama Adımları

SOLID'i mevcut bir Java projesine uygularken rastgele değişiklik yapmak yerine sistematik bir sıra izlemek, hem riski azaltır hem de gereksiz refactor'ün önüne geçer.

  1. Kod kokusunu (code smell) fark edin: sürekli değişen sınıflar, uzun metotlar, çok fazla parametre veya sık kırılan testler bir sinyaldir.
  2. Hangi prensibin ihlal edildiğini teşhis edin: sorun bir sınıfın çok iş yapmasından mı, yoksa somut bir bağımlılıktan mı kaynaklanıyor, karşılaştırma tablosuyla eşleştirin.
  3. Minimum değişiklikle düzeltin: tüm mimariyi yeniden yazmak yerine sorunlu noktayı ayıklayın, gerekmeyen soyutlamalara girmeyin.
  4. Değişikliği testle doğrulayın: refactor sonrası davranışın aynı kaldığını birim testleriyle kontrol edin.
  5. Aşırı soyutlamaya kaçmadığınızı kontrol edin: eklediğiniz her arayüz veya katmanın gerçek bir ihtiyacı karşıladığından emin olun.

Bu adımları kendi Java kodunuz üzerinde denemeden önce SOLID kavramlarına ne kadar hâkim olduğunuzu ölçmek isterseniz, ücretsiz kodlama bilgisi testi ile mevcut seviyenizi kısa sürede görebilirsiniz.

Sık Sorulan Sorular

SOLID prensipleri her Java projesinde zorunlu mudur?

Hayır, zorunlu değildir. SOLID; kod büyüdüğünde ve değişim sıklığı arttığında bakım kolaylığı sağlayan bir rehberdir, küçük ve değişmeyen kod parçalarına zorla uygulanması gerekmez.

SRP ile tek metotlu sınıflar yazmak arasındaki fark nedir?

SRP, bir sınıfın tek bir değişim nedeni olmasını ister; bu, sınıfın tek metotlu olması gerektiği anlamına gelmez. Birden fazla metodu olan bir sınıf, bu metotlar aynı sorumluluğa hizmet ettiği sürece SRP'ye uygun kalabilir.

OCP prensibini uygularken en sık yapılan hata nedir?

En sık hata, henüz ihtiyaç doğmadan her olası genişleme senaryosu için soyutlama katmanı eklemektir. OCP, mevcut kodu bozmadan genişletme imkânı sunmalıdır; bunu gelecekteki her ihtimali önceden tahmin ederek yapmaya çalışmak gereksiz karmaşıklık yaratır.

LSP ihlali kodda nasıl fark edilir?

Bir alt sınıf, üst sınıftan miras aldığı bir metodu boş bırakıyorsa, beklenmeyen bir istisna fırlatıyorsa veya üst sınıfın davrandığı biçimden farklı bir sonuç üretiyorsa bu genellikle bir LSP ihlalidir.

ISP ile SRP birbirine karıştırılır mı?

Evet, sıkça karıştırılır. SRP bir sınıfın sorumluluk sınırıyla ilgilidir, ISP ise bir arayüzün istemcilere gereğinden fazla metot dayatıp dayatmadığıyla ilgilidir. İkisi de "gereğinden büyük olmama" fikrini paylaşır ama farklı yapılara (sınıf ve arayüz) uygulanır.

DIP uygulamak için mutlaka bir framework mü gerekir?

Hayır. DIP, saf Java ile de arayüz tanımlayıp bağımlılığı constructor veya setter üzerinden dışarıdan vererek uygulanabilir. Bağımlılık enjeksiyonu framework'leri bu süreci otomatikleştirir ama prensibin kendisi framework'e bağlı değildir.

SOLID prensiplerini öğrenmeye nereden başlamalıyım?

SRP ile başlamak, çünkü çoğu diğer prensip ihlali kökeninde bir sınıfın birden fazla sorumluluk üstlenmesi yatar. SRP'yi kavramak diğer dört prensibin mantığını anlamayı da kolaylaştırır.

SOLID prensipleri, doğru yerde ve doğru zamanda uygulandığında Java kod tabanınızı gerçek anlamda daha bakımı kolay ve test edilebilir hâle getirir; ancak bunu bir kural listesi gibi ezberlemek yerine, kod okuma ve teşhis etme becerisi olarak geliştirmek gerekir. Bu beceriyi Java'nın temel nesne yönelimli kavramlarından başlayarak sistemli bir şekilde inşa etmek isteyenler Java özel ders programını inceleyebilir.

Bu içeriğin üretilmesinde yapay zeka araçlarından destek alınmıştır.

İlgili Eğitimler

Berk Keskin — Yazılım Geliştirici ve Eğitmen
Yazar

Berk Keskin Kimdir?

Yazılıma 12 yaşında başladı; bugün öğrencinin seviyesine ve hedefine göre şekillenen sürdürülebilir öğrenme sistemleri tasarlıyor. 300'den fazla kişiye ezber değil, düşünerek kod yazmayı öğretti — Berk Akademi'de izlemeye değil üretmeye dayalı öğrenme kültürünü o kuruyor.

WhatsApp Hemen Ara