froquiz-logoFroquiz
  • Anasayfa
  • Quizler
  • Blog
  • Hakkımızda
Froquiz

Yazılım mühendisleri için en kapsamlı quiz platformu. 5000+ soru ile kendinizi test edin ve kariyerinizi geliştirin.

LinkedIn

Platform

  • Quizlere Başla
  • Konular
  • Blog
  • Profilim
  • Giriş Yap

Hakkında

  • Biz Kimiz?
  • İletişim

Yasal

  • Gizlilik Politikası
  • Kullanım Koşulları

© 2026 Froquiz. Tüm hakları saklıdır.Teknoloji tutkuyla yapıldı
Blog/SOLID Prensipleri: Okunabilir ve Sürdürülebilir Kod Yazmanın 5 Kuralı

SOLID Prensipleri: Okunabilir ve Sürdürülebilir Kod Yazmanın 5 Kuralı

SOLID, iyi yazılım tasarımının temel taşlarını tanımlayan 5 prensiptir. Bu prensipleri anlamak, altı ay sonra kendi kodunuzu okuyabilmeniz ile okuyamamanız arasındaki farktır.

Yusuf SeyitoğluYusuf Seyitoğlu|
5 Mart 20265 Mar
|
97 görüntülenme
|
10 dk okuma

SOLID Prensipleri: Okunabilir ve Sürdürülebilir Kod Yazmanın 5 Kuralı

Altı ay önce yazdığınız koda bakıyorsunuz. Anlamıyorsunuz. Değiştirmeye çalışıyorsunuz, başka bir yer bozuluyor. Test yazmak istiyorsunuz, her şey birbirine öyle bağlı ki nereden başlayacağınızı bilmiyorsunuz.

Bu his tanıdık geliyorsa yalnız değilsiniz. Ve SOLID prensipleri tam olarak bu problemi çözmek için var.

SOLID Nedir?

SOLID, Robert C. Martin (Uncle Bob) tarafından tanımlanan beş tasarım prensibinin baş harflerinden oluşur. Nesne yönelimli programlama için yazılmış ama özü dil ve paradigmadan bağımsız:

  • S — Single Responsibility Principle
  • O — Open/Closed Principle
  • L — Liskov Substitution Principle
  • I — Interface Segregation Principle
  • D — Dependency Inversion Principle

Her biri ayrı bir problem çözüyor. Birlikte uygulandığında kod daha az kırılgan, daha test edilebilir ve daha kolay değiştirilebilir hale geliyor.

S — Single Responsibility Principle

"Bir sınıfın değişmesi için yalnızca bir sebebi olmalı."

Başka deyişle: bir sınıf yalnızca bir şey yapmalı ve onu iyi yapmalı.

// YANLIŞ: Bir sınıf çok fazla şey yapıyor class User { constructor(public name: string, public email: string) {} // Kullanıcı verisi — tamam getProfile() { return { name: this.name, email: this.email }; } // Veritabanı işlemi — burada ne işi var? save() { db.query("INSERT INTO users ..."); } // Email gönderme — hiç burada olmamalı sendWelcomeEmail() { emailClient.send(this.email, "Hoş geldiniz!"); } // PDF oluşturma — ciddi misiniz? generateReport() { return pdfGenerator.create(this.getProfile()); } }

Bu sınıfı değiştirmek için dört farklı sebep var: kullanıcı verisi değişirse, veritabanı şeması değişirse, email şablonu değişirse, rapor formatı değişirse. Her değişiklik diğerlerini etkileme riski taşıyor.

// DOĞRU: Her sınıf tek bir sorumluluğa sahip class User { constructor(public name: string, public email: string) {} getProfile() { return { name: this.name, email: this.email }; } } class UserRepository { save(user: User) { db.query("INSERT INTO users ...", user); } findById(id: string) { return db.query("SELECT * FROM users WHERE id = ?", id); } } class EmailService { sendWelcome(user: User) { emailClient.send(user.email, "Hoş geldiniz!"); } } class UserReportService { generate(user: User) { return pdfGenerator.create(user.getProfile()); } }

Şimdi her sınıfın değişmesi için tek bir sebep var. Email şablonu değişirse sadece EmailService değişir. Veritabanı sorgusu değişirse sadece UserRepository değişir.

O — Open/Closed Principle

"Yazılım varlıkları genişlemeye açık, değişime kapalı olmalı."

Yeni özellik eklendiğinde mevcut kodu değiştirmek yerine yeni kod eklemelisiniz.

// YANLIŞ: Yeni ödeme yöntemi eklendiğinde bu fonksiyon değişmek zorunda function processPayment(order: Order, method: string) { if (method === "credit_card") { // kredi kartı işlemi } else if (method === "paypal") { // PayPal işlemi } else if (method === "crypto") { // kripto işlemi — bu elif zinciri büyümeye devam eder } }
// DOĞRU: Yeni yöntem = yeni sınıf, mevcut kod değişmez interface PaymentMethod { process(order: Order): Promise<PaymentResult>; } class CreditCardPayment implements PaymentMethod { async process(order: Order) { /* kredi kartı mantığı */ } } class PayPalPayment implements PaymentMethod { async process(order: Order) { /* PayPal mantığı */ } } class CryptoPayment implements PaymentMethod { async process(order: Order) { /* kripto mantığı */ } } // Bu fonksiyon hiç değişmez async function processPayment(order: Order, method: PaymentMethod) { return method.process(order); }

Yeni ödeme yöntemi eklendi mi? Yeni bir sınıf yazıyorsunuz. Mevcut hiçbir koda dokunmuyorsunuz. Mevcut hiçbir testi bozmuyorsunuz.

L — Liskov Substitution Principle

"Alt sınıflar, üst sınıflarının yerine geçebilmeli."

Bir fonksiyon Animal bekliyor ve siz Dog veriyorsunuz — her şey beklendiği gibi çalışmalı. Dog, Animal'ın davranışlarını bozmadan genişletmeli.

// YANLIŞ: Alt sınıf üst sınıfın beklentisini bozuyor class Rectangle { constructor(public width: number, public height: number) {} setWidth(w: number) { this.width = w; } setHeight(h: number) { this.height = h; } area() { return this.width * this.height; } } class Square extends Rectangle { setWidth(w: number) { this.width = w; this.height = w; // Kare olması için yüksekliği de değiştir } setHeight(h: number) { this.width = h; this.height = h; } } // Bu fonksiyon Rectangle bekliyor ama Square verilince bozuluyor function testRectangle(rect: Rectangle) { rect.setWidth(5); rect.setHeight(10); console.log(rect.area()); // Rectangle: 50 beklenir, Square: 100 gelir }

Square, Rectangle'ın yerine geçemiyor çünkü davranışı farklı. LSP ihlali.

// DOĞRU: Ortak interface, ayrı implementasyonlar interface Shape { area(): number; } class Rectangle implements Shape { constructor(private width: number, private height: number) {} area() { return this.width * this.height; } } class Square implements Shape { constructor(private side: number) {} area() { return this.side * this.side; } } function printArea(shape: Shape) { console.log(shape.area()); // Her zaman doğru çalışır }

I — Interface Segregation Principle

"İstemciler kullanmadıkları interface'lere bağımlı olmaya zorlanmamalı."

Büyük interface'leri küçük, odaklı interface'lere bölün.

// YANLIŞ: Her servis bu devasa interface'i implement etmek zorunda interface Worker { work(): void; eat(): void; sleep(): void; attendMeeting(): void; writeReport(): void; } // Robot yemek yemez ve uyumaz — ama implement etmek zorunda class Robot implements Worker { work() { /* çalış */ } eat() { throw new Error("Robotlar yemek yemez"); } // Saçmalık sleep() { throw new Error("Robotlar uyumaz"); } // Saçmalık attendMeeting() { /* toplantıya katıl */ } writeReport() { /* rapor yaz */ } }
// DOĞRU: Küçük, odaklı interface'ler interface Workable { work(): void; } interface Eatable { eat(): void; } interface Sleepable { sleep(): void; } interface Reportable { writeReport(): void; } class Human implements Workable, Eatable, Sleepable, Reportable { work() { /* çalış */ } eat() { /* ye */ } sleep() { /* uyu */ } writeReport() { /* rapor yaz */ } } class Robot implements Workable, Reportable { work() { /* çalış */ } writeReport() { /* rapor yaz */ } // Yemek ve uyku yok — çünkü gerekmez }

D — Dependency Inversion Principle

"Üst seviye modüller alt seviye modüllere bağımlı olmamalı. Her ikisi de soyutlamalara bağımlı olmalı."

// YANLIŞ: Üst seviye kod doğrudan alt seviye implementasyona bağımlı class OrderService { private db = new MySQLDatabase(); // Doğrudan MySQL'e bağımlı createOrder(order: Order) { this.db.save(order); // MySQL değişirse bu kod değişmek zorunda } }
// DOĞRU: Soyutlamaya bağımlı interface Database { save(data: any): Promise<void>; findById(id: string): Promise<any>; } class MySQLDatabase implements Database { async save(data: any) { /* MySQL implementasyonu */ } async findById(id: string) { /* MySQL implementasyonu */ } } class MongoDatabase implements Database { async save(data: any) { /* MongoDB implementasyonu */ } async findById(id: string) { /* MongoDB implementasyonu */ } } class OrderService { constructor(private db: Database) {} // Interface'e bağımlı, implementasyona değil async createOrder(order: Order) { await this.db.save(order); } } // MySQL ile kullan const service = new OrderService(new MySQLDatabase()); // MongoDB'ye geç — OrderService hiç değişmedi const service2 = new OrderService(new MongoDatabase()); // Test'te in-memory kullan const testService = new OrderService(new InMemoryDatabase());

DIP'in en büyük faydası test edilebilirlik. Gerçek veritabanı yerine in-memory implementasyon inject edilebilir.

SOLID Bir Hedef, Dogma Değil

SOLID prensipleri kural değil, yol göstericidir. Her prensibi her sınıfa uygulamaya çalışmak gereksiz karmaşıklık yaratır.

Küçük script'ler ve tek kullanımlık araçlar için SOLID overkill olabilir. Büyüyen, bakımı yapılan, ekipte geliştirilen kod için SOLID yatırımın karşılığını verir.

Kodunuzu okuyan birinin — altı ay sonraki kendiniz dahil — "bu mantıklı" diyebileceği şekilde yazmak, tüm prensiplerin özü.

Yazar Hakkında

Yusuf Seyitoğlu

Yusuf Seyitoğlu

View Profile →

Diğer Yazılar

  • CI/CD Pipeline Nedir? Kod Yazmaktan Production'a Giden Otomatik Yol

    6 Mar

  • TypeScript Nedir? JavaScript Geliştiricisinin Tip Sistemine Gerçek Giriş

    6 Mar

  • Kubernetes Nedir? Container Orkestrasyon'u Sıfırdan Anlamak

    6 Mar

← Tüm Bloglar