Serimizin 2. bölümünde metotları inceleyeceğiz.
Metotlar olabildiğince küçük, anlaşılır ve tek bir iş yapan yapılar olmalı. Eğer bir metoda çok fazla sorumluluk yüklerseniz hem okunabilirliği düşer hem de bakımını zorlaştırırsınız. Bir değişiklik yapmak istediğinizde birçok yer etkilenecektir. Küçük metotlar ise tekrar kullanılabilir, daha anlaşılır ve hata ayıklaması kolaydır.
Şimdi 2 örnek göstereceğim. İlk örnekte tek bir metot kullanılacak ama kod biraz dağınık duracak. İkinci örnekte ise kod birkaç küçük metoda bölünmüş olacak ve böylece farkını daha iyi anlayacağız.
Birinci örnek:
int[] numbers = { 15, 3, 8, 20, 6 };
Console.WriteLine("Original Numbers:");
foreach (int num in numbers)
    Console.WriteLine(num);
Console.WriteLine("Even Numbers:");
foreach (int num in numbers)
{
    if (num % 2 == 0)
        Console.WriteLine(num);
}
Console.WriteLine("Odd Numbers:");
foreach (int num in numbers)
{
    if (num % 2 != 0)
        Console.WriteLine(num);
}
Burada hem yazdırma işlemi hem de filtreleme mantığı tek bir yerde duruyor. Kod tekrarları var (diziyi 3 defa dolaştık), ayrıca okunabilirliği düşük.
Düzenli olan ikinci örnek:
int[] numbers = { 15, 3, 8, 20, 6 };
WriteItems(numbers, "Original Numbers");
WriteItems(FilterEvenNumbers(numbers), "Even Numbers");
WriteItems(FilterOddNumbers(numbers), "Odd Numbers");
Console.Read();
void WriteItems(int[] arr, string title)
{
    Console.WriteLine(title);
    foreach (int num in arr)
        Console.WriteLine(num);
}
int[] FilterEvenNumbers(int[] arr)
{
    return arr.Where(n => n % 2 == 0).ToArray();
}
int[] FilterOddNumbers(int[] arr)
{
    return arr.Where(n => n % 2 != 0).ToArray();
}
WriteItems metodu sayesinde tekrar eden yazdırma kodunu kaldırdık. FilterEvenNumbers ve FilterOddNumbers metotları tek bir sorumluluk taşıyor (sadece filtreleme yapıyor). Yani bu metotların yaptıkları iş belirli, amacı neyse onu yapıyorlar. Kod okunabilir, yeniden kullanılabilir ve daha kolay test edilebilir hale getirdik.
Clean code prensibine göre metotlar birden çok eylem yapmaları durumunda karmaşıklık doğar. Bu çok anlaşılabilir bir durumdur. Sonuç olara bir metot yalnızca tek bir iş yapmalı. Tekrar eden kodları ayrı metotlara taşımak bakım kolaylığı sağlar.
Şimdi aynı örneği “HttpClient” ile göstereceğim ki bu sınıf C# içerisinde en çok kullanılan sınıftır. Web isteklerini yönetmemizi sağlar. Örneğimizle de çok iyi uyuşuyor çünkü karmaşık kodları burada çok görüyoruz.
Bakın bu örnekte bir adrese istek yapıp JSON cevap alıyoruz, sonra aynı blok içerisinde farklı bir endpoint’e istek yapıp yine cevap alıyoruz.
using (HttpClient client = new HttpClient())
        {
            // Kullanıcıları al
            HttpResponseMessage response1 = await client.GetAsync("https://recepserit.com/users");
            string usersJson = await response1.Content.ReadAsStringAsync();
            Console.WriteLine("Users:");
            Console.WriteLine(usersJson);
            // Gönderileri al
            HttpResponseMessage response2 = await client.GetAsync("https://recepserit.com/posts");
            string postsJson = await response2.Content.ReadAsStringAsync();
            Console.WriteLine("Posts:");
            Console.WriteLine(postsJson);
        }Şimdi bunu refactor yapalım.
static async Task Main()
    {
        await WriteJsonAsync("https://recepserit.com/users", "Users");
        await WriteJsonAsync("recepserit.com", "Posts");
    }
    static async Task WriteJsonAsync(string url, string title)
    {
        string data = await FetchAsync(url);
        PrintData(data, title);
    }
    static async Task<string> FetchAsync(string url)
    {
        using HttpClient client = new HttpClient();
        HttpResponseMessage response = await client.GetAsync(url);
        response.EnsureSuccessStatusCode(); // hata varsa exception fırlatır
        return await response.Content.ReadAsStringAsync();
    }
    static void PrintData(string data, string title)
    {
        Console.WriteLine($"--- {title} ---");
        Console.WriteLine(data);
        Console.WriteLine();
    }WriteJsonAsync metodu tüm akışı yönetiyor. Hem HttpClient ile istek yapıyor (yani fetch yapıyor) hem de aldığı cevabı ekrana yazdırıyor. FetchAsync sadece HTTP isteği yapıyor. PrintData sadece çıktıyı yazdırıyor. Yani hepsi tek iş yapıyor.
Parametreler nasıl olmalı?
Metot parametreleri mümkünse hiç olmamalı. Eğer parametre gerekiyorsa en fazla 3 ya da 4 olmalı. Bnulardan fazla parametre alması, genelde fazla sorumluluk taşıdığını veya daha iyi tasarlanabileceğini gösterir. Parametre sayısını düşük tutmak, hem test etmeyi kolaylaştırır hem de bakımı kolaylaştırır.
Bir metod çok fazla parametre alıyorsa bu aslında metot fazla şey biliyordur veya fazla sorumluluk alıyordur. Ayrıca parametrelerin birbiriyle ilişkili olması ihtimali yüksek. O zaman onları tek bir model sınıfı altında toplamak en mantıklı çözüm.
Bu modele genelde Input/Output (örneğin algoritmalarda) veya Request/Response (özellikle domain service sınıfları, API veya business logic tarafında) kullanılır.
Request / Response modeli oluşturarak bir örnek yapalım.
public class SortRequest
{
    public int[] Array { get; set; }
    public int StartIndex { get; set; } = 0;
    public int EndIndex { get; set; }
    public bool Ascending { get; set; } = true;
    public bool LogToConsole { get; set; } = false;
}
public class SortResponse
{
    public int[] Result { get; set; }
    public TimeSpan Duration { get; set; }
}
Şimdi bu modelleri bir metoda uygulayarak anlamlı bir cevap döndürmesini sağlayalım.
// Burası metodu oluşturduğumuz ayrı bir sınıftır. Örneğin "SortBuilder" gibi düşünebilirsiniz
public SortResponse Sort(SortRequest request)
{
    var arr = request.Array;
    var watch = System.Diagnostics.Stopwatch.StartNew();
    for (int j = request.StartIndex; j < request.EndIndex; j++)
    {
        for (int i = request.StartIndex; i < request.EndIndex; i++)
        {
            if ((request.Ascending && arr[i] > arr[i + 1]) ||
                (!request.Ascending && arr[i] < arr[i + 1]))
            {
                (arr[i], arr[i + 1]) = (arr[i + 1], arr[i]);
            }
        }
    }
    watch.Stop();
    return new SortResponse
    {
        Result = arr,
        Duration = watch.Elapsed
    };
}
Metotlarımız hazır. Şimdi bunu nasıl kullanacağız? Çok basit.
// Burası uygulamayı çağırdığımız ana yerdir
var request = new SortRequest
{
    Array = new int[] { 10, 5, 1, 7, 2 },
    StartIndex = 0,
    EndIndex = 4,
    Ascending = true
};
var response = Sort(request);
Console.WriteLine("Sorted array: " + string.Join(", ", response.Result));
Console.WriteLine("Duration: " + response.Duration);Daha net anlaşıldığı kanaatindeyim.
Önemli tavsiye: Bir metoda bool parametre (true/false) vermek yerine iki ayrı metot yazmak daha temizdir. Çünkü DoWork(true) gibi bir çağrıdan ne yaptığı anlaşılmaz. Ama DoWorkInParallel() ile DoWorkSequential() isimli iki ayrı metot olursa okunabilirlik artar.
if, else, while, for gibi yapılar iç içe çok fazla girinti oluşturursa kod okunması zor hale gelir. Genellikle bunu “if” yazarken yaparlar ama bu okunabilirliği düşürüyor. Oysa ne kadar erken “return” yaptırabilirsek metot daha net oalcaktır. Bu sebeple bir metodun içinde maksimum 2 seviye, idealde 1 seviye girinti olmalı.
Bölümü özetleyelim:
Basit veri için özel metodlar yazın. Metodun işini net ve tek sorumluluklu tutmak gerekiyor.
Bir metodun parametresi bir veri koleksiyonunu temsil ediyorsa (örneğin username, phoneNumber, postalCode, address gibi), her alanı ayrı parametre yapmak yerine bir obje kullanmak daha temizdir. Bu sayede tek parametrede bir model ile veriyi taşımış olursunuz.
Sorumlulukları ayırmanız gerekiyor. Metodun adı GetUser ise, sadece veriyi döndürmeli, veriyi güncellememelidir.
Hata kodu döndürmek yerine exception fırlatmak daha iyidir. Bunu “exception handling” bölümünde ele alacağız.
Aynı işlemi birden fazla yerde yapıyorsan, ayrı bir metot oluştur. “Don’t repeat yourself” prensibi burada devreye girer.


 
							
 
							
Yorum bırakın