Menu
Coddy logo textTech

Şablon Metot Kalıbı

Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 94 / 107.

Template Method deseni, bir algoritmanın iskeletini bir temel türde tanımlar ve alt türlerin genel yapıyı değiştirmeden belirli adımları geçersiz kılmasına (override etmesine) olanak tanır. Decorator nesneleri sarmalayarak davranış eklerken, Template Method algoritmanın akışını kontrol eder ve münferit adımların özelleştirilmesine izin verir.

Go'da kalıtım (inheritance) olmadığı için, bu deseni arayüzlerle (interfaces) birleştirilmiş yapı gömme (struct embedding) kullanarak uygularız. "Template" yapısı algoritma yapısını tanımlar ve özelleştirilebilen metotları çağırır:

type DataProcessor interface {
    ReadData() string
    ProcessData(data string) string
    SaveData(result string) string
}

type BaseProcessor struct {
    Impl DataProcessor
}

func (b *BaseProcessor) Execute() string {
    data := b.Impl.ReadData()
    result := b.Impl.ProcessData(data)
    return b.Impl.SaveData(result)
}

Somut uygulamalar, algoritma akışı değişmeden kalırken her adımın kendi versiyonlarını sunar:

type CSVProcessor struct{}

func (c CSVProcessor) ReadData() string       { return "csv-data" }
func (c CSVProcessor) ProcessData(d string) string { return "processed-" + d }
func (c CSVProcessor) SaveData(r string) string    { return "Saved: " + r }

type JSONProcessor struct{}

func (j JSONProcessor) ReadData() string       { return "json-data" }
func (j JSONProcessor) ProcessData(d string) string { return "parsed-" + d }
func (j JSONProcessor) SaveData(r string) string    { return "Stored: " + r }

Şablon yöntemi Execute, adımları sabit bir sırayla yönetir:

csvProc := &BaseProcessor{Impl: CSVProcessor{}}
fmt.Println(csvProc.Execute())  // Kaydedildi: processed-csv-data

jsonProc := &BaseProcessor{Impl: JSONProcessor{}}
fmt.Println(jsonProc.Execute()) // Depolandı: parsed-json-data

Template Method, veri içe/dışa aktarma boru hatları, rapor oluşturucular veya test çerçeveleri gibi, aynı yapıya sahip olan ancak belirli adımlarda farklılık gösteren algoritmalarınız olduğunda idealdir.

challenge icon

Görev

Kolay

Template Method desenini kullanarak bir rapor oluşturma sistemi inşa edelim! Farklı rapor türlerinin aynı oluşturma sürecini (veri toplama, biçimlendirme ve sonucu çıktı verme) takip ettiği, ancak her rapor türünün bu adımları farklı şekilde özelleştirdiği bir iskelet oluşturacaksınız.

Kodunuzu üç dosya halinde düzenleyeceksiniz:

  • report.go: Arayüzünüzü (interface) ve rapor oluşturma algoritmasını yöneten temel işlemciyi (processor) tanımlayın.

    Rapor oluşturma adımlarını temsil eden üç yönteme sahip bir ReportGenerator arayüzü oluşturun:

    • GatherData() string — rapor için ham verileri alır
    • FormatData(data string) string — verileri raporun formatına dönüştürür
    • OutputReport(formatted string) string — nihai çıktı mesajını üretir

    Bir ReportGenerator uygulamasını barındıran bir ReportProcessor yapısı (struct) oluşturun. Üç adımı sırasıyla (topla, biçimlendir, ardından çıktı ver) yürüten ve nihai sonucu döndüren bir Generate() string yöntemi ekleyin.

  • generators.go: Her adımı özelleştiren somut rapor oluşturucuları uygulayın.

    İki rapor türü oluşturun:

    • Region alanına (string) sahip SalesReport
      • GatherData() şunu döndürür: sales-data-[region]
      • FormatData(data) şunu döndürür: SALES REPORT: [data]
      • OutputReport(formatted) şunu döndürür: Printed: [formatted]
    • Warehouse alanına (string) sahip InventoryReport
      • GatherData() şunu döndürür: inventory-[warehouse]
      • FormatData(data) şunu döndürür: *** [data] ***
      • OutputReport(formatted) şunu döndürür: Exported: [formatted]
  • main.go: Aynı algoritma yapısının uygulamaya bağlı olarak nasıl farklı sonuçlar ürettiğini gösterin.

    Rapor türünü (sales veya inventory) ve yapılandırma değerini (satış için bölge, envanter için depo) okuyun. Uygun oluşturucuyu oluşturun, onu bir ReportProcessor içine sarın, Generate() yöntemini çağırın ve sonucu yazdırın.

Aşağıdaki girdiler sağlanacaktır:

  • Satır 1: Rapor türü (sales veya inventory)
  • Satır 2: Yapılandırma değeri (bölge adı veya depo adı)

Örneğin, şu girdi verildiğinde:

sales
North

Çıktınız şu olmalıdır:

Printed: SALES REPORT: sales-data-North

Ve şu girdi verildiğinde:

inventory
MainHub

Çıktınız şu olmalıdır:

Exported: *** inventory-MainHub ***

Ve şu girdi verildiğinde:

sales
West

Çıktınız şu olmalıdır:

Printed: SALES REPORT: sales-data-West

ReportProcessor'ın her zaman aynı üç adımı aynı sırayla nasıl çağırdığına, ancak her rapor türünün bu adımların kendi uygulamasını nasıl sunduğuna dikkat edin. Algoritmanın iskeleti sabit kalırken detaylar değişir; işte bu, Template Method deseninin iş başındaki halidir!

Kopya kağıdı

Template Method deseni, bir algoritmanın iskeletini bir temel türde tanımlar ve alt türlerin genel yapıyı değiştirmeden belirli adımları geçersiz kılmasına (override) olanak tanır.

Go'da bu deseni, arayüzlerle (interfaces) birleştirilmiş yapı gömme (struct embedding) kullanarak uygulayın. "Şablon" yapısı algoritma yapısını tanımlar ve özelleştirilebilen yöntemleri çağırır:

type DataProcessor interface {
    ReadData() string
    ProcessData(data string) string
    SaveData(result string) string
}

type BaseProcessor struct {
    Impl DataProcessor
}

func (b *BaseProcessor) Execute() string {
    data := b.Impl.ReadData()
    result := b.Impl.ProcessData(data)
    return b.Impl.SaveData(result)
}

Somut uygulamalar, algoritma akışı değişmeden kalırken her adımın kendi sürümlerini sağlar:

type CSVProcessor struct{}

func (c CSVProcessor) ReadData() string       { return "csv-data" }
func (c CSVProcessor) ProcessData(d string) string { return "processed-" + d }
func (c CSVProcessor) SaveData(r string) string    { return "Saved: " + r }

type JSONProcessor struct{}

func (j JSONProcessor) ReadData() string       { return "json-data" }
func (j JSONProcessor) ProcessData(d string) string { return "parsed-" + d }
func (j JSONProcessor) SaveData(r string) string    { return "Stored: " + r }

Şablon yöntemi, adımları sabit bir sırayla yönetir:

csvProc := &BaseProcessor{Impl: CSVProcessor{}}
fmt.Println(csvProc.Execute())  // Saved: processed-csv-data

jsonProc := &BaseProcessor{Impl: JSONProcessor{}}
fmt.Println(jsonProc.Execute()) // Stored: parsed-json-data

Template Method, veri içe/dışa aktarma boru hatları (pipelines), rapor oluşturucular veya test çerçeveleri gibi algoritmaların aynı yapıyı paylaştığı ancak belirli adımlarda farklılık gösterdiği durumlar için idealdir.

Kendin dene

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Rapor türünü oku
	scanner.Scan()
	reportType := scanner.Text()
	
	// Yapılandırma değerini oku (bölge veya depo)
	scanner.Scan()
	configValue := scanner.Text()

	// TODO: reportType değerine göre uygun oluşturucuyu (generator) oluşturun
	// - Eğer reportType "sales" ise, Region alanı configValue olarak ayarlanmış bir SalesReport oluşturun
	// - Eğer reportType "inventory" ise, Warehouse alanı configValue olarak ayarlanmış bir InventoryReport oluşturun
	
	// TODO: Oluşturucu ile bir ReportProcessor oluşturun
	
	// TODO: Generate() fonksiyonunu çağırın ve sonucu yazdırın
	_ = reportType
	_ = configValue
	fmt.Println("TODO: Generate and print the report")
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Nesne Yönelimli Programlama bölümündeki tüm dersler