
Şöyle bir senaryo düşünelim. Sabah işe geldiniz, product owner koşarak yanınıza geliyor: "E-posta şablonunu değiştirmemiz lazım, müşteriler şikâyet etti." Tamam, basit bir değişiklik. Kodu açıyorsunuz, değişikliği yapıyorsunuz, deploy ediyorsunuz. Akşam Slack'te alarm: transfer işlemleri bozulmuş.
E-posta şablonunu değiştirdiniz. Transfer nasıl bozuldu?
Cevap çoğu zaman aynı: her şeyi tek bir sınıfa tıkmışız.
Nedir bu prensip aslında?
Single Responsibility Principle çok gösterişli bir isim ama söylediği şey aslında çok sade: bir sınıfın değişmesi için tek bir neden olmalı.
"Tek neden" derken felsefi bir şey kastetmiyoruz. Pratik olarak şunu soruyoruz: bu sınıfı kim değiştirmek ister? Muhasebe ekibi mi, pazarlama mı, DevOps mu? Eğer cevap "herkes" ise bir sorun var demektir.
❌ Önce yanlışı görelim
Banking projelerinde sıkça karşılaştığımız o meşhur "God Object":
class Account {
private BigDecimal balance;
void transfer(BigDecimal amount, String targetId) {
// İş mantığı
if (amount.compareTo(balance) > 0)
throw new IllegalStateException("Yetersiz bakiye");
balance = balance.subtract(amount);
// Loglama — dur, bu burada ne arıyor?
System.out.println("[LOG] " + LocalDateTime.now()
+ " Transfer: " + amount + " → " + targetId);
// E-posta — ciddi misiniz?
System.out.println("[EMAIL] ali@banka.com: "
+ amount + " TL transfer edildi.");
// Fraud kontrolü — artık bu da mı?
if (amount.compareTo(new BigDecimal("50000")) > 0)
System.out.println("[UYARI] Şüpheli işlem!");
}
}
Bu sınıfı değiştirmek için dört farklı neden var: iş kuralları değişirse, log formatı değişirse, e-posta şablonu değişirse, fraud eşiği değişirse. Yani teorik olarak dört farklı ekip bu dosyaya dokunabilir. Ve her dokunuşta bir şeylerin kırılma ihtimali var.
Üstüne bir de test yazmaya çalışın. Sadece bakiyeyi test etmek istiyorsunuz ama e-posta kodu da çalışıyor, log da yazılıyor. Neyi test ettiğinizi siz de bilmiyorsunuz artık.
// Tek sorumluluğu: bakiye ve hesap işlemleri
class Account {
private BigDecimal balance;
Account(BigDecimal balance) {
this.balance = balance;
}
void credit(BigDecimal amount) { // Para yatır — bakiyeyi artır
this.balance = this.balance.add(amount);
}
void debit(BigDecimal amount) { // Para çek — bakiyeyi düş
if (amount.compareTo(balance) > 0)
throw new IllegalStateException("Yetersiz bakiye");
this.balance = this.balance.subtract(amount);
}
BigDecimal getBalance() { return balance; }
}
// Tek sorumluluğu: audit kayıtlarını tutmak
class AuditLogger {
void log(String message) {
System.out.println("[LOG] " + LocalDateTime.now() + " — " + message);
}
}
// Tek sorumluluğu: kullanıcıya bildirim göndermek
class Notification {
void send(String recipient, String message) {
System.out.println("[EMAIL] " + recipient + ": " + message);
}
}Koordinasyonu da ayrı bir sınıfa veriyoruz. O sınıfın tek işi "süreci yönetmek":
class TransferService {
private final AuditLogger logger;
private final Notification notification;
TransferService(AuditLogger logger, Notification notification) {
this.logger = logger;
this.notification = notification;
}
void transfer(Account source, Account target,
BigDecimal amount, String recipientEmail) {
source.debit(amount); // iş mantığı → Account'ta
target.credit(amount);
logger.log("Transfer: " + amount); // loglama → AuditLogger'da
notification.send(recipientEmail, // bildirim → Notification'da
amount + " TL hesabınıza aktarıldı.");
}
}Kullanalım
Account sender = new Account(new BigDecimal("2000"));
Account receiver = new Account(new BigDecimal("500"));
TransferService service = new TransferService(
new AuditLogger(),
new Notification()
);
service.transfer(sender, receiver,
new BigDecimal("300"), "ali@banka.com");
// [LOG] 2024-01-15T10:30:00 — Transfer: 300
// [EMAIL] ali@banka.com: 300 TL hesabınıza aktarıldı.
System.out.println(sender.getBalance()); // 1700
System.out.println(receiver.getBalance()); // 800O sabahki senaryoya dönelim
E-posta şablonunu değiştirmeniz gerekiyor. Şimdi tek yapmanız gereken şu:
class Notification {
void send(String recipient, String message) {
// Tek değişen yer burası
System.out.println("[SMS] " + recipient + ": " + message);
}
}Account'a dokunmadınız. AuditLogger'a dokunmadınız. TransferService'e dokunmadınız. Değişikliğin etki alanı tamamen izole kaldı. Artık akşam Slack alarmı gelmiyor.
Kısaca
Her sınıfın değişmesi için tek bir neden olsun. Bu kural kodu daha küçük, daha okunabilir ve çok daha kolay test edilebilir hale getiriyor. Üstelik bir ekip büyüdükçe bu izolasyon hayat kurtarıcı oluyor — kim neye dokunacak belli, çakışma yok, sürpriz yok.
Bir sonraki bölümde Open/Closed Principle'a bakacağız — yeni bir ücret modeli eklerken mevcut koda tek satır dokunmadan nasıl implemente ediyoruz, birlikte göreceğiz.
Seriyi kaçırmamak için takipte kalın.
