Skip to content

Latest commit

 

History

History
182 lines (139 loc) · 8.23 KB

File metadata and controls

182 lines (139 loc) · 8.23 KB

Template Method Design Pattern

Yazılım geliştirme sürecinde, benzer işlem adımlarından oluşan ancak bu adımların bazılarının farklı biçimlerde gerçekleştirildiği algoritmalarla sıklıkla karşılaşılır. Örneğin farklı kaynaklardan veri çekip raporlayan birden fazla sınıf düşünüldüğünde; veriyi çekme, işleme, biçimlendirme ve kaydetme adımlarının sırası hep aynıdır; yalnızca her sınıfa özgü ayrıntılar değişir. Bu ortak iskelet kod tabanında tekrar ettiğinde hem kod tekrarı (code duplication) oluşur hem de iskeletin farklı yerlerde birbirinden bağımsız değişme riski ortaya çıkar.

Template Method Pattern, bir algoritmanın iskeletini (template) bir base (temel) class'ta tanımlayarak algoritmanın belirli adımlarının alt sınıflarca (subclass) override edilmesine olanak tanıyan davranışsal bir tasarım desenidir. Böylece algoritmanın genel akışı bozulmadan yalnızca değişkenlik gösteren adımlar alt sınıflara bırakılır.

Bu pattern, Strategy Pattern ile karıştırılabilir. Aralarındaki temel fark şudur: Strategy tüm algoritmayı değiştirilebilir bir nesneye taşır ve composition (bileşim) kullanır; Template Method ise algoritmanın iskeletini inheritance (kalıtım) yoluyla base class'ta sabitler, yalnızca belirli adımları alt sınıflara devreder.

GoF şemasına göre AbstractClass içinde TemplateMethod() adı verilen ve algoritmanın adımlarını sırasıyla çağıran bir metot bulunur. Bu adımların bir kısmı abstract (zorunlu override) olabilirken bir kısmı virtual (isteğe bağlı override, varsayılan davranışa sahip) olabilir. ConcreteClass'lar ise yalnızca kendi sorumluluklarına düşen adımları override eder.

public abstract class AbstractClass
{
    // Template method — algoritmanın iskeletini tanımlar, override edilmez
    public void TemplateMethod()
    {
        Step1();
        Step2();
        Hook();
        Step3();
    }

    protected abstract void Step1();
    protected abstract void Step2();

    // Hook — isteğe bağlı adım, varsayılan implementasyon içerir
    protected virtual void Hook()
    {
        // Varsayılan davranış; alt sınıflar override edebilir veya etmeyebilir
    }

    protected abstract void Step3();
}

public class ConcreteClassA : AbstractClass
{
    protected override void Step1() =>
        Console.WriteLine("ConcreteClassA: Step1");

    protected override void Step2() =>
        Console.WriteLine("ConcreteClassA: Step2");

    protected override void Step3() =>
        Console.WriteLine("ConcreteClassA: Step3");
}

public class ConcreteClassB : AbstractClass
{
    protected override void Step1() =>
        Console.WriteLine("ConcreteClassB: Step1");

    protected override void Step2() =>
        Console.WriteLine("ConcreteClassB: Step2");

    protected override void Hook() =>
        Console.WriteLine("ConcreteClassB: Hook (overridden)");

    protected override void Step3() =>
        Console.WriteLine("ConcreteClassB: Step3");
}

Örnek Senaryo

Birden fazla kaynak için rapor üretilmesi gerektiği düşünülsün: bir SalesReportGenerator satış verilerini, bir InventoryReportGenerator ise stok verilerini raporlasın. Her iki rapor da şu sabit adımları izler:

  1. Veriyi hazırla (FetchData)
  2. Veriyi işle (ProcessData)
  3. Çıktıyı biçimlendir (FormatOutput)
  4. Raporu kaydet (SaveReport)

Ancak her adımın içeriği rapora göre değişir. Template Method Pattern bu senaryoyu aşağıdaki gibi çözer.

public abstract class ReportGenerator
{
    // Template method — rapor üretiminin sabit akışını tanımlar
    public void GenerateReport()
    {
        Console.WriteLine($"--- {ReportName()} Raporu Oluşturuluyor ---");
        FetchData();
        ProcessData();
        FormatOutput();
        SaveReport();
        Console.WriteLine($"--- {ReportName()} Raporu Tamamlandı ---\n");
    }

    protected abstract string ReportName();
    protected abstract void FetchData();
    protected abstract void ProcessData();

    // Hook — varsayılan olarak düz metin biçimlendirmesi yapar
    protected virtual void FormatOutput()
    {
        Console.WriteLine("Çıktı düz metin olarak biçimlendirildi.");
    }

    protected abstract void SaveReport();
}

SalesReportGenerator ve InventoryReportGenerator sınıfları bu base class'ı inherit alarak yalnızca kendi adımlarını implemente eder.

public class SalesReportGenerator : ReportGenerator
{
    protected override string ReportName() => "Satış";

    protected override void FetchData() =>
        Console.WriteLine("Satış veritabanından veriler çekildi.");

    protected override void ProcessData() =>
        Console.WriteLine("Satış verileri aylık bazda gruplandı ve toplamlar hesaplandı.");

    protected override void FormatOutput() =>
        Console.WriteLine("Çıktı PDF formatında biçimlendirildi.");

    protected override void SaveReport() =>
        Console.WriteLine("Satış raporu /reports/sales/ dizinine kaydedildi.");
}

public class InventoryReportGenerator : ReportGenerator
{
    protected override string ReportName() => "Stok";

    protected override void FetchData() =>
        Console.WriteLine("Depo yönetim sisteminden stok verileri çekildi.");

    protected override void ProcessData() =>
        Console.WriteLine("Kritik stok seviyeleri tespit edildi ve sıralandı.");

    // FormatOutput override edilmedi — varsayılan (düz metin) davranış kullanılıyor

    protected override void SaveReport() =>
        Console.WriteLine("Stok raporu /reports/inventory/ dizinine kaydedildi.");
}

program.cs

internal class Program
{
    static void Main(string[] args)
    {
        ReportGenerator salesReport = new SalesReportGenerator();
        salesReport.GenerateReport();

        ReportGenerator inventoryReport = new InventoryReportGenerator();
        inventoryReport.GenerateReport();
    }
}

çıktı

--- Satış Raporu Oluşturuluyor ---
Satış veritabanından veriler çekildi.
Satış verileri aylık bazda gruplandı ve toplamlar hesaplandı.
Çıktı PDF formatında biçimlendirildi.
Satış raporu /reports/sales/ dizinine kaydedildi.
--- Satış Raporu Tamamlandı ---

--- Stok Raporu Oluşturuluyor ---
Depo yönetim sisteminden stok verileri çekildi.
Kritik stok seviyeleri tespit edildi ve sıralandı.
Çıktı düz metin olarak biçimlendirildi.
Stok raporu /reports/inventory/ dizinine kaydedildi.
--- Stok Raporu Tamamlandı ---

Sonuç

Template Method Pattern, bir algoritmanın genel akışını tek bir yerde (base class) sabitlerken değişkenlik gösteren adımları alt sınıflara devreder. Bu sayede algoritmanın iskelet kodu tekrar edilmez (DRY), herhangi bir adımın nasıl uygulandığı değiştirilmek istendiğinde yalnızca ilgili alt sınıfa dokunmak yeterlidir. örnekler: base class hangi adımların ne zaman çalışacağını kontrol eder, alt sınıflar ise yalnızca kendilerine devredilen adımları doldurur.

Hook metotları ise isteğe bağlı genişleme noktaları sağlar; alt sınıf bir hook'u override etmeyebilir ve base class'ın varsayılan davranışı devreye girer. Bu esneklik, Template Method Pattern'i rapor üretimi, veri aktarım pipeline'ları (ETL), oyun döngüleri ve form doğrulama gibi "sabit iskelet, değişken adım" yapısına sahip her senaryoda kullanışlı kılar.

Kaynakça