Aikido

Sınıflar neden tek sorumluluk prensibini takip etmelidir?

Okunabilirlik

Kural
Sınıfların tek bir sorumluluğu olmalıdır.
Birden fazla konuyu yürüten sınıflar
Tek Sorumluluk İlkesi'ni ihlaleder.

Desteklenen diller: JS, TS, PY, JAVA,
C/C++, C#, Swift/Objective C, Ruby, PHP, Kotlin, 
Scala, Rust, Haskell, Groovy, Dart, Julia,
Elixir, Clojure, OCaml,Delphi

Giriş

Çok fazla şey yapan sınıflar birer darboğaza dönüşür. Kimlik doğrulama, e-postalar ve doğrulamayı yöneten bir sınıf; herhangi bir konu evrildiğinde değişiklik gerektirir ve ilgili olmayan işlevselliğin bozulma riskini doğurur. Test etme, tek bir yönü test ederken bile tüm sınıfın taklit edilmesini (mocking) gerektirir. Tek Sorumluluk İlkesi, bir sınıfın değişmek için yalnızca bir nedeni olması gerektiğini belirtir.

Neden önemli?

Kod sürdürülebilirliği: Birden fazla sorumluluğa sahip sınıflar daha sık değişir, çünkü herhangi bir konudaki gelişme tüm sınıfı etkiler.

Test etme karmaşıklığı: Çok sorumluluklu sınıfları test etmek, tek bir özelliği test etmek için bile tüm bağımlılıkların taklit edilmesini gerektirir.

Yeniden kullanılabilirlik: Tüm bağımlılıkları beraberinde getirmeden tek bir sorumluluğu ayıklayamazsınız. Geliştiriciler, çok sorumluluklu sınıfları ayıklamak yerine kodun kopyasını oluştururlar.

Takım koordinasyonu: Aynı sınıf üzerinde farklı özellikler için çalışan birden fazla geliştirici, sık sık birleştirme (merge) çakışmaları yaratır. Tek sorumluluklu sınıflar, çakışma olmadan paralel geliştirme yapılmasını sağlar.

Kod örnekleri

❌ Kural dışı:

class UserManager {
    async createUser(userData) {
        const user = await db.users.insert(userData);
        await this.sendWelcomeEmail(user.email);
        await this.logEvent('user_created', user.id);
        await cache.set(`user:${user.id}`, user);
        return user;
    }

    async sendWelcomeEmail(email) {
        const template = this.loadEmailTemplate('welcome');
        await emailService.send(email, template);
    }

    async logEvent(event, userId) {
        await analytics.track(event, { userId, timestamp: Date.now() });
    }
}

Neden yanlış: Bu sınıf veritabanı işlemlerini, e-posta göndermeyi, günlük kaydetmeyi (logging) ve önbelleğe almayı (caching) yönetir. E-posta şablonlarındaki, günlük formatlarındaki veya önbellek stratejisindeki değişikliklerin tümü bu sınıfın değiştirilmesini gerektirir. Kullanıcı oluşturmayı test etmek; e-posta servislerini, analitiği ve önbelleği taklit etmeyi gerektirir, bu da testleri yavaş ve kırılgan hale getirir.

✅ Kurallara uygun:

class UserRepository {
    async create(userData) {
        return await db.users.insert(userData);
    }
}

class EmailNotificationService {
    async sendWelcomeEmail(email) {
        const template = await this.templateLoader.load('welcome');
        return await this.emailSender.send(email, template);
    }
}

class UserEventLogger {
    async logCreation(userId) {
        return await this.analytics.track('user_created', {
            userId,
            timestamp: Date.now()
        });
    }
}

class UserService {
    constructor(repository, emailService, eventLogger, cache) {
        this.repository = repository;
        this.emailService = emailService;
        this.eventLogger = eventLogger;
        this.cache = cache;
    }

    async createUser(userData) {
        const user = await this.repository.create(userData);
        await Promise.all([
            this.emailService.sendWelcomeEmail(user.email),
            this.eventLogger.logCreation(user.id),
            this.cache.set(`user:${user.id}`, user)
        ]);
        return user;
    }
}

Neden önemli: Her sınıfın net bir sorumluluğu vardır: veri kalıcılığı, e-posta gönderimi, olay günlüğü (event logging) veya düzenleme (orchestration). E-posta şablonlarındaki değişiklikler yalnızca EmailNotificationServiceöğesini etkiler. Kullanıcı oluşturma testi, bağımlılıklar için basit sarmalayıcılar (stub) kullanabilir. Sınıflar, farklı özellikler arasında bağımsız olarak yeniden kullanılabilir.

Sonuç

Tek Sorumluluk Prensibi (Single Responsibility Principle), sınıfları olabildiğince küçük yapmaktan ziyade, her sınıfın değişmek için tek bir net sebebe sahip olmasını sağlamakla ilgilidir. Bir sınıf birden fazla sorumluluğu üstlenmeye başladığında, her bir sorumluluğu odaklanmış bir arayüze sahip kendi sınıfına çıkararak yeniden yapılandırın. Bu, kodun ilişkisiz işlevler genelinde zincirleme değişikliklere yol açmadan test edilmesini, sürdürülmesini ve geliştirilmesini kolaylaştırır.

Sık Sorulan Sorular

Sorularınız mı var?

Bir sınıfın çok fazla sorumluluğa sahip olduğunu nasıl anlarım?

Değişmek için birden fazla nedeni olan sınıfları arayın. E-posta mantığını, loglama formatını ve veritabanı şemasını değiştirmek aynı sınıf üzerinde değişiklik yapılmasını gerektiriyorsa, o sınıf çok fazla sorumluluğa sahiptir. Metot adlarını kontrol edin: Aynı sınıfta sendEmail(), logEvent() ve validateData() gibi birbiriyle ilişkisiz fiilleri kapsıyorlarsa bu bir uyarı işaretidir. 300-400 satırdan uzun sınıflar, tek başına boyut belirleyici olmasa da genellikle birden fazla sorumluluğa işaret eder.

Sınıfları bölmek daha fazla dosya ve karmaşıklık yaratmaz mı?

Daha fazla dosya, daha fazla karmaşıklık anlamına gelmez. Her biri 50 satırdan oluşan on odaklanmış sınıfı anlamak, her şeyi yöneten 500 satırlık tek bir sınıftan daha kolaydır. Buradaki anahtar nokta, her sınıfın basit olması ve net bir amaca hizmet etmesidir. Modern IDE'lerdeki gezinme özellikleri dosya sayısını önemsiz kılar. Karmaşıklığın azalması, ilişkisiz konuları hesaba katmadan her bir sınıfı bağımsız olarak değerlendirebilmenizden kaynaklanır.

Doğası gereği birden fazla işlemi koordine etmesi gereken sınıflar ne olacak?

Koordinasyonun kendisi de bir sorumluluktur. Bir UserService sınıfı, bu sorumlulukları bizzat uygulamak zorunda kalmadan UserRepository, EmailService ve EventLogger çağrılarını koordine edebilir. Buna orchestrator (düzenleyici) veya facade deseni denir. Aradaki fark, düzenleyicinin birden fazla konuyu doğrudan uygulamak yerine uzmanlaşmış sınıflara delege etmesidir. Bu, iş mantığı değil, ince bir yapıştırıcı koddur (glue code).

Bu ilke, statik metotlara sahip yardımcı (utility) sınıflara nasıl uygulanır?

Yardımcı sınıflar, birbiriyle ilişkisiz statik metotlar eklemeye devam etmek kolay olduğu için tek sorumluluk kuralını ihlal etmeye özellikle yatkındır. Bir StringUtils sınıfı biçimlendirme yardımcılarıyla başlayıp doğrulama, ayrıştırma, şifreleme ve kodlamayı içerecek şekilde büyüyebilir. Bunları StringFormatter, StringValidator ve StringEncoder gibi odaklanmış yardımcı sınıflara bölün. Her biri ilgili işlemlerden oluşan tutarlı bir sete sahiptir.

Bu kuralı ihlal eden mevcut sınıfları nasıl yeniden yapılandırabilirim?

Sınıf içindeki belirgin sorumlulukları tespit ederek başlayın. En kolay olanı önce yeni bir sınıfa çıkarın, testleri güncelleyin ve her şeyin çalıştığını doğrulayın. Büyük bir yeniden yapılandırma denemek yerine bunu yinelemeli olarak tekrarlayın. Strangler fig (boğucu incir) desenini kullanın: Yeni tek sorumluluk sınıfları oluşturun ve kodu eski sınıftan kademeli olarak taşıyın. Eski sınıf boş veya minimum düzeyde kaldığında onu kullanımdan kaldırın (deprecate). Her adım çalışan, test edilebilir bir artış olmalıdır.

Tek sorumluluk, tek metot anlamına mı gelir?

Hayır. Bir sınıf, hepsi aynı sorumlulukla ilişkili olduğu sürece birden fazla metoda sahip olabilir. Bir UserRepository sınıfı create(), update(), delete() ve findById() metotlarına sahip olabilir çünkü bunların hepsi kullanıcı verisi kalıcılığı gibi tek bir sorumluluğa hizmet eder. Bu metotlar, ayrı ayrı paketlenmiş farklı konular değil, aynı konunun tutarlı varyasyonlarıdır.

Şimdi güvenliğinizi sağlayın

Kodunuzu, bulutunuzu ve çalışma zamanınızı tek bir merkezi sistemde güvenceye alın.
Güvenlik açıklarını otomatik olarak hızla bulun ve düzeltin.

Kredi kartı gerekmez | Tarama sonuçları 32 saniyede.