← Kursa Dön
📄 Text · 12 min

Metot Tasarım İlkeleri

Giriş — İyi Metot Nedir?

Metot yazmayı öğrendin. Parametre alıyor, bir iş yapıyor, değer döndürüyor. Ama "çalışıyor" yetmez. İyi bir metot:

  • Okunabilir — Ne yaptığını ismine bakarak anlarsın

  • Test edilebilir — Bağımsız olarak doğrulanabilir

  • Yeniden kullanılabilir — Farklı bağlamlarda çağrılabilir

  • Bakımı kolay — Değişiklik yapmak kolay ve güvenli

Bu ders, profesyonel düzeyde metot tasarlamak için gereken ilkeleri öğretecek.

Analoji: Bir metot bir LEGO parçası gibi olmalı. Her parça belirli bir şekle ve işleve sahip. Parçalar birleşerek karmaşık yapılar oluşturur. Ama her parça tek başına anlamlı ve farklı yapılarda kullanılabilir.


Single Responsibility Principle (Tek Sorumluluk)

Bir metot tek bir iş yapmalı ve o işi iyi yapmalı. Bu, yazılım tasarımının en temel ilkesidir.

Nasıl Anlarım?

Metodu bir cümleyle açıklamaya çalış. Eğer "ve" kelimesini kullanmak zorunda kalıyorsan, metot birden fazla iş yapıyor demektir.

// KÖTÜ — iki iş yapıyor: hesaplama VE yazdırma
public static void notHesaplaVeYazdir(int[] puanlar) {
    double toplam = 0;
    for (int p : puanlar) toplam += p;
    double ortalama = toplam / puanlar.length;
    
    String harf;
    if (ortalama >= 90) harf = "AA";
    else if (ortalama >= 80) harf = "BA";
    else if (ortalama >= 70) harf = "BB";
    else harf = "FF";
    
    System.out.println("Ortalama: " + ortalama);
    System.out.println("Not: " + harf);
}

Sorunlar:

  • Test etmek zor — konsola yazdırıyor, sonucu yakalayamıyorsun

  • Yeniden kullanım zor — sadece ortalama lazımsa tamamını çağırmalısın

  • Yazdırma formatı değişirse hesaplama da etkilenir

İYİ — Her Metot Tek İş

public static double ortalamaHesapla(int[] puanlar) {
    double toplam = 0;
    for (int p : puanlar) toplam += p;
    return toplam / puanlar.length;
}

public static String notHarfiBelirle(double ortalama) {
    if (ortalama >= 90) return "AA";
    if (ortalama >= 80) return "BA";
    if (ortalama >= 70) return "BB";
    return "FF";
}

public static void sonucYazdir(double ortalama, String harf) {
    System.out.println("Ortalama: " + ortalama);
    System.out.println("Not: " + harf);
}

// Birleştir
public static void main(String[] args) {
    int[] puanlar = {85, 92, 78, 90};
    double ort = ortalamaHesapla(puanlar);
    String harf = notHarfiBelirle(ort);
    sonucYazdir(ort, harf);
}

Şimdi:

  • Her metot bağımsız test edilebilir

  • ortalamaHesapla başka yerlerde de kullanılabilir

  • Yazdırma formatı değişirse sadece sonucYazdir güncellenir

💡 İpucu: Bir metot 20-30 satırı geçiyorsa, muhtemelen birden fazla iş yapıyordur. Parçalamayı düşün.


İyi İsimlendirme (Naming)

Metot ismi, ne yaptığını açıkça belirtmeli. İyi isim, dokümantasyon ihtiyacını azaltır.

Kurallar

  1. Fiille başla — eylem belirten kelime

  2. camelCase kullan

  3. Kısaltma kullanma — açık ve tam yaz

  4. Boolean döndürüyorsa: is, has, can, should ile başla

  5. Dönüşüm yapıyorsa: to prefix'i kullan

İyi ve Kötü İsimler

// KÖTÜ → İYİ
void d(int[] a)              → void diziSirala(int[] dizi)
int h(int x, int y)          → int enBuyukBul(int x, int y)
void proc(String s)          → void dosyaOku(String dosyaYolu)
boolean chk(String e)        → boolean emailGecerliMi(String email)
String conv(int n)            → String sayiyiMetneÇevir(int sayi)

Boolean Metot İsimleri

// İyi boolean isimleri — doğal okuma sağlar
public static boolean asalMi(int sayi) { ... }
public static boolean isEmpty(String metin) { ... }
public static boolean hasPermission(String kullanici) { ... }
public static boolean canDrive(int yas, boolean ehliyetVar) { ... }

// Kullanımda doğal okuma
if (asalMi(n)) { ... }           // "n asal mı?"
if (isEmpty(liste)) { ... }      // "liste boş mu?"
if (canDrive(yas, true)) { ... } // "sürebilir mi?"

Getter/Setter İsimleri (OOP Hazırlık)

// Getter — değer döndürür
public String getIsim() { return this.isim; }
public int getYas() { return this.yas; }

// Setter — değer atar
public void setIsim(String isim) { this.isim = isim; }
public void setYas(int yas) { this.yas = yas; }

// Boolean getter → is prefix
public boolean isAktif() { return this.aktif; }

Tutarlılık

Aynı kavram için aynı kelimeyi kullan. Bir yerde get, başka yerde fetch, başka yerde retrieve kullanma.

// TUTARSIZ
getKullanici();
fetchSiparis();
retrieveUrun();

// TUTARLI
getKullanici();
getSiparis();
getUrun();

Parametre Sayısı

Bir metot ne kadar az parametre alırsa o kadar iyidir. Çok parametre, metodu anlamayı ve kullanmayı zorlaştırır.

Kural

Parametre SayısıDeğerlendirme
0 (niladic)İdeal
1 (monadic)Çok iyi
2 (dyadic)İyi
3 (triadic)Kabul edilebilir
4+Sorunlu — refactor et

Çok Parametre Varsa Ne Yapmalı?

Teknik 1: İlişkili Parametreleri Nesneye Grupla

// KÖTÜ — 6 parametre
public static void siparisOlustur(String urunAdi, double fiyat, 
    int adet, String musteriAdi, String adres, String telefon) {
    // ...
}

// İYİ — parametreleri grupla
public static void siparisOlustur(Urun urun, Musteri musteri) {
    // ...
}

Teknik 2: Builder Pattern (İleri Seviye)

// Çok parametreli nesne oluşturma
Siparis siparis = new Siparis.Builder()
    .urun("Laptop")
    .fiyat(15000)
    .adet(1)
    .musteri("Ali")
    .adres("İstanbul")
    .build();

Teknik 3: Varsayılan Değerlerle Overloading

public static String formatla(String metin) {
    return formatla(metin, true, 80);
}

public static String formatla(String metin, boolean buyukHarf, int maxUzunluk) {
    if (buyukHarf) metin = metin.toUpperCase();
    if (metin.length() > maxUzunluk) metin = metin.substring(0, maxUzunluk);
    return metin;
}

⚠️ Dikkat: Boolean parametre genellikle kötü bir işaret. yazdir(metin, true) ne anlama geliyor? Okuyan kişi true'nun ne yaptığını bilemiyor. Bunun yerine iki ayrı metot yaz veya enum kullan:

// KÖTÜ — true ne demek?
yazdir(metin, true);

// İYİ — iki ayrı metot
buyukHarfleYazdir(metin);
kucukHarfleYazdir(metin);

// VEYA — enum
enum HarfStili { BUYUK, KUCUK, ORIJINAL }
yazdir(metin, HarfStili.BUYUK);

return vs void — Hangisini Seçelim?

Değer Döndür (return) Eğer:

  • Hesaplama yapıyorsan

  • Sonucun başka yerlerde kullanılması gerekiyorsa

  • Test edilebilirlik istiyorsan

// return — sonucu yakala ve kullan
public static double daireAlani(double r) {
    return Math.PI * r * r;
}

double alan = daireAlani(5);
if (alan > 100) { ... }

void Kullan Eğer:

  • Yan etki yapıyorsan (yazdırma, dosya yazma, loglama)

  • Bir nesnenin durumunu değiştiriyorsan

// void — yan etki
public static void logYaz(String mesaj) {
    System.out.println("[LOG] " + mesaj);
}

Altın Kural: Hesaplama ve Yan Etkiyi Ayır

// KÖTÜ — hem hesaplıyor hem yazdırıyor
public static void fiyatHesapla(double tutar, double kdv) {
    double toplam = tutar * (1 + kdv);
    System.out.println("Toplam: " + toplam);
}

// İYİ — hesaplama ayrı, yazdırma ayrı
public static double fiyatHesapla(double tutar, double kdv) {
    return tutar * (1 + kdv);
}

// Kullanım
double toplam = fiyatHesapla(100, 0.18);
System.out.println("Toplam: " + toplam);

Bu ayrım neden önemli? Hesaplama metodu test edilebilir (input → output), yazdırma metodu test etmek zor (konsol çıktısını yakalamak gerekir).


Helper Metotlar (Yardımcı Metotlar)

Büyük bir metodu parçalarken çıkan küçük, özel amaçlı metotlara helper (yardımcı) metot denir.

Refactoring Örneği

// ÖNCE — uzun, karmaşık metot
public static void raporOlustur(int[] puanlar) {
    // 1. İstatistik hesapla
    double toplam = 0;
    int enYuksek = Integer.MIN_VALUE;
    int enDusuk = Integer.MAX_VALUE;
    for (int p : puanlar) {
        toplam += p;
        if (p > enYuksek) enYuksek = p;
        if (p < enDusuk) enDusuk = p;
    }
    double ortalama = toplam / puanlar.length;
    
    // 2. Not dağılımı
    int gecen = 0, kalan = 0;
    for (int p : puanlar) {
        if (p >= 50) gecen++;
        else kalan++;
    }
    
    // 3. Rapor yazdır
    System.out.println("=== SINAV RAPORU ===");
    System.out.println("Öğrenci sayısı: " + puanlar.length);
    System.out.println("Ortalama: " + ortalama);
    System.out.println("En yüksek: " + enYuksek);
    System.out.println("En düşük: " + enDusuk);
    System.out.println("Geçen: " + gecen);
    System.out.println("Kalan: " + kalan);
}
// SONRA — helper metotlarla parçalanmış
public static void raporOlustur(int[] puanlar) {
    double ortalama = ortalamaHesapla(puanlar);
    int enYuksek = enBuyukBul(puanlar);
    int enDusuk = enKucukBul(puanlar);
    int gecen = esikUstunuSay(puanlar, 50);
    int kalan = puanlar.length - gecen;
    
    raporYazdir(puanlar.length, ortalama, enYuksek, enDusuk, gecen, kalan);
}

// Helper metotlar
private static double ortalamaHesapla(int[] dizi) {
    double toplam = 0;
    for (int d : dizi) toplam += d;
    return toplam / dizi.length;
}

private static int enBuyukBul(int[] dizi) {
    int max = dizi[0];
    for (int d : dizi) if (d > max) max = d;
    return max;
}

private static int enKucukBul(int[] dizi) {
    int min = dizi[0];
    for (int d : dizi) if (d < min) min = d;
    return min;
}

private static int esikUstunuSay(int[] dizi, int esik) {
    int sayac = 0;
    for (int d : dizi) if (d >= esik) sayac++;
    return sayac;
}

private static void raporYazdir(int ogrenciSayisi, double ort, 
    int enYuksek, int enDusuk, int gecen, int kalan) {
    System.out.println("=== SINAV RAPORU ===");
    System.out.println("Öğrenci sayısı: " + ogrenciSayisi);
    System.out.printf("Ortalama: %.1f%n", ort);
    System.out.println("En yüksek: " + enYuksek);
    System.out.println("En düşük: " + enDusuk);
    System.out.println("Geçen: " + gecen + ", Kalan: " + kalan);
}

Ana metot raporOlustur artık hikaye gibi okunan yüksek seviyeli bir metot. Detaylar helper'larda gizli.

Helper metotların avantajları:

  • Yeniden kullanılabilirortalamaHesapla başka raporlarda da kullanılabilir

  • Test edilebilir — her biri bağımsız test edilebilir

  • Okunabilir — ana metot bir özet gibi


Metot Uzunluğu

Bir metot kaç satır olmalı? Kesin bir sayı yok ama rehberler:

KaynakÖnerilen Uzunluk
Robert C. Martin (Clean Code)5-10 satır
Google Java StyleKısıtlama yok ama kısa tercih
Genel kabul20-30 satır

Kural: Bir Ekrana Sığmalı

Bir metot, IDE'de scroll yapmadan okunabilmeli. 30-40 satırı aşıyorsa, muhtemelen parçalanabilir.

Ne Zaman Parçalama?

  1. Metot birden fazla iş yapıyorsa → Tek sorumluluk

  2. Yorum yazarak bölüm ayırıyorsan → Her bölüm bir metot olabilir

  3. Derin iç içe yapı varsa → Guard clause veya metoda ayır

  4. Kodun bir bölümü başka yerde de kullanılabilirse → Helper metot

// Yorum bölümler = refactor ipucu
public static void islem() {
    // Veriyi hazırla          → veriHazirla() metodu
    ...
    
    // Hesaplama yap           → hesaplamaYap() metodu
    ...
    
    // Sonucu kaydet           → sonucKaydet() metodu
    ...
}

Metot Tasarım Kontrol Listesi

Bir metot yazarken veya review ederken şu soruları sor:

İsimlendirme

  • [ ] İsim fiille mi başlıyor?

  • [ ] İsim ne yaptığını açıklıyor mu?

  • [ ] Boolean metotsa is/has/can prefix'i var mı?

Tek Sorumluluk

  • [ ] Metot tek bir iş mi yapıyor?

  • [ ] Bir cümleyle açıklanabilir mi? ("ve" olmadan)

  • [ ] 30 satırı aşıyor mu? Parçalanabilir mi?

Parametreler

  • [ ] 3'ten az parametre mi?

  • [ ] Boolean parametre kullanılıyor mu? (red flag)

  • [ ] Parametreler gruplanabilir mi?

Dönüş

  • [ ] Hesaplama mı yan etki mi? İkisi ayrı mı?

  • [ ] null döndürmek yerine Optional veya boş koleksiyon dönülebilir mi?

  • [ ] Tüm yollarda return var mı?

Genel

  • [ ] Guard clause kullanılmış mı? (null, boş kontrolleri)

  • [ ] Sihirli sayılar sabit mi?

  • [ ] Metot bağımsız test edilebilir mi?


Gerçek Dünya Örneği: Şifre Doğrulama

Tüm ilkeleri birleştiren bir örnek:

public class SifreDogrulama {
    
    private static final int MIN_UZUNLUK = 8;
    private static final int MAX_UZUNLUK = 64;
    
    public static boolean sifreGecerliMi(String sifre) {
        if (sifre == null) return false;
        
        return uzunlukGecerliMi(sifre)
            && buyukHarfIcerir(sifre)
            && kucukHarfIcerir(sifre)
            && rakamIcerir(sifre)
            && ozelKarakterIcerir(sifre);
    }
    
    // Helper metotlar — her biri tek kontrol
    
    private static boolean uzunlukGecerliMi(String sifre) {
        return sifre.length() >= MIN_UZUNLUK 
            && sifre.length() <= MAX_UZUNLUK;
    }
    
    private static boolean buyukHarfIcerir(String sifre) {
        for (char c : sifre.toCharArray()) {
            if (Character.isUpperCase(c)) return true;
        }
        return false;
    }
    
    private static boolean kucukHarfIcerir(String sifre) {
        for (char c : sifre.toCharArray()) {
            if (Character.isLowerCase(c)) return true;
        }
        return false;
    }
    
    private static boolean rakamIcerir(String sifre) {
        for (char c : sifre.toCharArray()) {
            if (Character.isDigit(c)) return true;
        }
        return false;
    }
    
    private static boolean ozelKarakterIcerir(String sifre) {
        String ozelKarakterler = "!@#$%^&*()_+-=[]{}|;':\",./<>?";
        for (char c : sifre.toCharArray()) {
            if (ozelKarakterler.indexOf(c) != -1) return true;
        }
        return false;
    }
    
    // Detaylı hata mesajı döndüren versiyon
    public static String sifreDogrula(String sifre) {
        if (sifre == null) return "Şifre boş olamaz";
        if (!uzunlukGecerliMi(sifre)) 
            return "Şifre " + MIN_UZUNLUK + "-" + MAX_UZUNLUK + " karakter olmalı";
        if (!buyukHarfIcerir(sifre)) return "En az bir büyük harf gerekli";
        if (!kucukHarfIcerir(sifre)) return "En az bir küçük harf gerekli";
        if (!rakamIcerir(sifre)) return "En az bir rakam gerekli";
        if (!ozelKarakterIcerir(sifre)) return "En az bir özel karakter gerekli";
        return null;  // Geçerli
    }
    
    public static void main(String[] args) {
        String[] testler = {"abc", "abcdefgh", "Abcdefgh1!", "Kisa1!"};
        
        for (String sifre : testler) {
            String hata = sifreDogrula(sifre);
            System.out.printf("%-15s → %s%n", sifre, 
                hata == null ? "✅ Geçerli" : "❌ " + hata);
        }
    }
}

Bu kodda:

  • Single Responsibility: Her helper tek bir kontrol yapıyor

  • İyi isimlendirme: sifreGecerliMi, buyukHarfIcerir — okunabilir

  • Guard clause: null kontrolü başta

  • Sabitler: MIN_UZUNLUK sihirli sayı değil

  • return vs void: Doğrulama sonucu döndürüyor, yazdırmıyor

  • Helper metotlar: Ana metot kısa ve okunaklı

  • Overloading: sifreGecerliMi (boolean) ve sifreDogrula (detaylı mesaj)


Fonksiyonel Stil: Saf Metotlar (Pure Functions)

Saf metot, sadece parametrelerine bağlı olan ve yan etkisi olmayan metottur. Aynı girdi her zaman aynı çıktıyı verir.

// SAF — sonuç sadece parametrelere bağlı, yan etki yok
public static int topla(int a, int b) {
    return a + b;
}

// SAF DEĞİL — dış değişkene bağlı
static int offset = 10;
public static int toplaOffset(int a) {
    return a + offset;  // offset değişirse sonuç değişir
}

// SAF DEĞİL — yan etki var
public static int toplaVeLogla(int a, int b) {
    System.out.println("Toplama yapılıyor");  // Yan etki!
    return a + b;
}

Saf metotların avantajları:

  • Test etmek kolay — mock'a gerek yok, input → output

  • Thread-safe — paylaşılan duruma erişmiyor

  • Cache'lenebilir — aynı girdiye aynı çıktı (memoization)

  • Anlaşılması kolay — dışarıdan etkilenmiyor, dışarıyı etkilemiyor

Kural: Mümkün olduğunca saf metot yaz. Yan etki (I/O, state değişikliği) gerektiğinde, onu ayrı bir metotta topla.


Metot Dokümantasyonu (Javadoc)

Önemli metotları Javadoc ile belgele:

/**
 * Verilen dizideki elemanların ortalamasını hesaplar.
 * 
 * @param dizi Ortalaması hesaplanacak dizi (boş olmamalı)
 * @return Dizideki elemanların aritmetik ortalaması
 * @throws IllegalArgumentException Dizi null veya boşsa
 */
public static double ortalamaHesapla(int[] dizi) {
    if (dizi == null || dizi.length == 0) {
        throw new IllegalArgumentException("Dizi boş olamaz");
    }
    double toplam = 0;
    for (int d : dizi) toplam += d;
    return toplam / dizi.length;
}

Ne zaman Javadoc yaz:

  • public metotlarda

  • Davranışı isimden anlaşılmayan metotlarda

  • Kütüphane/API metotlarında

Ne zaman yazma:

  • private helper metotlarda (isim yeterince açıksa)

  • getter/setter gibi basit metotlarda

💡 İpucu: Eğer bir metodu Javadoc ile açıklamak çok zorsa, muhtemelen metot çok karmaşık. İsim veya yapıyı düzelt — en iyi dokümantasyon, okunabilir koddur.


Anti-Pattern'ler

1. God Method (Tanrı Metot)

Her şeyi yapan devasa metot. Yüzlerce satır, onlarca parametre, karar, döngü, I/O, hesaplama hepsi bir arada. Parçala!

2. Side Effect Sürprizi

// KÖTÜ — topla deniyor ama değişkeni de güncelliyor
public static int topla(int a, int b) {
    sonucDegiskeni = a + b;  // Gizli yan etki!
    return a + b;
}

Metot adı "topla" ama gizliden bir sınıf değişkeni de güncelliyor. Bunu çağıran kişi beklemez.

3. Aşırı Parametre

// KÖTÜ — 8 parametre
void kaydet(String ad, String soyad, int yas, String email,
    String telefon, String adres, String sehir, String ulke) {
    // ...
}

Bir Kisi nesnesi oluşturup tek parametre olarak geç.

4. İç İçe if-else İçinde İş Mantığı

// KÖTÜ — iş mantığı 5 seviye iç içe
if (a) {
    if (b) {
        if (c) {
            if (d) {
                isYap();
            }
        }
    }
}

// İYİ — guard clause veya boolean birleştir
if (!a || !b || !c || !d) return;
isYap();

Özet

  • Single Responsibility: Her metot tek bir iş yapsın. "Ve" ile açıklıyorsan, parçala.

  • İsimlendirme: Fiille başla, camelCase, açıklayıcı. Boolean → is/has/can. Tutarlı ol.

  • Parametre sayısı: 3'ü aşmasın. Çok parametrede nesneye grupla veya overloading kullan.

  • return vs void: Hesaplama → return, yan etki → void. İkisini aynı metotta karıştırma.

  • Helper metotlar: Büyük metotları küçük, tek sorumluluklu yardımcılara ayır. Ana metot bir özet gibi okunsun.

  • Metot uzunluğu: 20-30 satır ideal. Scroll gerektiriyorsa parçala. Yorum bölümleri = refactoring fırsatı.