Ş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-dataTemplate 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.
Görev
KolayTemplate 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
ReportGeneratorarayüzü oluşturun:GatherData() string— rapor için ham verileri alırFormatData(data string) string— verileri raporun formatına dönüştürürOutputReport(formatted string) string— nihai çıktı mesajını üretir
Bir
ReportGeneratoruygulamasını barındıran birReportProcessoryapı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 birGenerate() stringyöntemi ekleyin.generators.go: Her adımı özelleştiren somut rapor oluşturucuları uygulayın.İki rapor türü oluşturun:
Regionalanına (string) sahipSalesReportGatherData()ş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]
Warehousealanına (string) sahipInventoryReportGatherData()ş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ü (
salesveyainventory) ve yapılandırma değerini (satış için bölge, envanter için depo) okuyun. Uygun oluşturucuyu oluşturun, onu birReportProcessoriç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ü (
salesveyainventory) - 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-NorthVe ş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-WestReportProcessor'ı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-dataTemplate 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")
}
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
1Go OOP Temelleri
Harici DosyalarGo Çalışma Alanı ve ModüllerPaketler ve İçe AktarmalarDışa Aktarılan ve Aktarılmayan İsimlerGo'da OOP'ye GirişSınıf Olarak Struct'larStruct'larda Metot TanımlamaPointer ve Değer AlıcılarStruct BaşlatmaYapıcı FonksiyonlarÖzet - Basit Hesap Makinesi4Arayüzler
Arayüzlere GirişÖrtük UygulamaSözleşme Olarak ArayüzBoş Arayüz (any)Tür OnaylamaTür SeçimiArayüz BileşimiStringer ve Error ArayüzleriÖzet - Şekil Hesaplayıcı7Kapsülleme
Dışa Aktarılan ve Aktarılmayan AlanlarPaket Seviyesinde KapsüllemeGetter ve Setter MetotlarıGo'da Bilgi GizlemeÖzet - Öğrenci Kayıtları10Generics (Go 1.18+)
Generics'e GirişTür ParametreleriTür KısıtlamalarıGeneric Struct'larGeneric Metotlar İçin Geçici ÇözümÖzet - Generic Koleksiyon13Tasarım Kalıpları Bölüm 1
Tasarım Kalıplarına GirişSingleton KalıbıFactory KalıbıAbstract Factory KalıbıObserver KalıbıStrategy Kalıbı2Türler ve Struct'lar: Derinlemesine İnceleme
Temel ve Bileşik TürlerÖzel Tür TanımlamalarıStruct EtiketleriAnonim Struct'larİç İçe Geçmiş Struct'larSıfır Değerler ve VarsayılanlarÖzet - Rehber5Kalıtım Yerine Kompozisyon
Go'da Neden Kalıtım YokStruct Gömme TemelleriMetot YükseltmeBirden Fazla Struct GömmeGömme vs AgregasyonGömülü Metotları GölgelemeÖzet - Çalışan Hiyerarşisi8Hata Yönetimi ve OOP
error ArayüzüÖzel Hata TürleriHata Sarmalama (fmt.Errorf)Sentinel Hatalarerrors.Is() ve errors.As()Panic, Defer ve RecoverÖzet - Dosya Ayrıştırıcı11Standart Kütüphane ve OOP
io.Reader ve io.Writersort.Interface Arayüzüfmt.Stringer ArayüzüStruct'lar ile encoding/jsonhttp.Handler ArayüzüÖzet - REST API Modelleri14Tasarım Kalıpları Bölüm 2
Komut KalıbıAdaptör KalıbıDekoratör KalıbıŞablon Metot KalıbıDurum KalıbıKompozit KalıbıDekoratör olarak Middleware3İşaretçiler ve Bellek
Go'da İşaretçi TemelleriStruct İşaretçileriDeğer ile Geçme ve Referans ile Geçmenew() FonksiyonuGo'da Garbage CollectionÖzet - Linked List Oluşturucu6Go'da Polimorfizm
Interface'ler ile PolimorfizmGo'da Duck TypingInterface Karşılama KurallarıPolimorfik KoleksiyonlarDependency InjectionÖzet - Payment Processor9Eşzamanlılık ve OOP
Goroutine TemelleriKanallar ve İletişimTamponlu ve Tamponsuz KanallarSelect İfadesisync.Mutex ve sync.RWMutexsync.WaitGroupThread-Safe Struct TasarımıÖzet - Worker Pool