Tasarım Kalıplarına Giriş
Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 85 / 107.
Tasarım desenleri, yazılım tasarımında ortaya çıkan yaygın sorunlara yönelik yeniden kullanılabilir çözümlerdir. Bunlar doğrudan kopyaladığınız kodlar değil, aksine belirli zorlukları çözmek için türlerinizi ve bunların etkileşimlerini nasıl yapılandıracağınız konusunda rehberlik eden şablonlardır.
Tasarım desenleri genellikle üç kategoriye ayrılır:
| Kategori | Amaç | Örnekler |
|---|---|---|
| Creational (Yaratımsal) | Nesne oluşturma mekanizmaları | Singleton, Factory, Builder |
| Structural (Yapısal) | Türleri daha büyük yapılar halinde birleştirme | Adapter, Decorator, Composite |
| Behavioral (Davranışsal) | Nesneler arası iletişim | Observer, Strategy, Command |
Go'da tasarım desenleri, geleneksel OOP dillerindekinden farklı görünür.
Sınıflar veya kalıtım olmadan Go; arayüzler, struct gömme ve kompozisyon kullanarak desenleri uygular. Örneğin, Java'nın soyut bir sınıf kullanabileceği yerlerde Go bir arayüz kullanır. C++'ın kalıtım kullanabileceği yerlerde Go gömme kullanır.
Tasarım kalıplarını öğrenmek, yaygın sorunları tanımanıza ve çözümleri diğer geliştiricilerle etkili bir şekilde paylaşmanıza yardımcı olur. Birisi "burada Strategy kalıbını kullan" dediğinde, tüm ekip uzun açıklamalara gerek kalmadan yaklaşımı anlar.
Gelecek derslerde, Singleton ve Factory gibi oluşturucu (creational) desenlerle başlayıp ardından Observer ve Strategy gibi davranışsal (behavioral) desenlere geçerek Go'da birkaç temel deseni uygulayacaksınız.
Görev
KolayTasarım desenlerinin kodu düzenlemeye nasıl yardımcı olduğunu gösteren basit bir bildirim sistemi kuralım! Gelecekteki derslerde Observer veya Strategy gibi desenlerle kolayca genişletilebilecek bir yapı oluşturacaksınız; ancak şimdilik, iyi OOP prensiplerini takip eden temiz arayüzler ve türler kurmaya odaklanın.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
notifier.go: Bildirim sisteminiz için temel arayüzü ve türleri tanımlayın.Bir onay mesajı döndüren tek bir
Send(message string) stringmetoduna sahip birNotifierarayüzü oluşturun.İki somut bildirim türü uygulayın:
Addressalanına sahipEmailNotifier—SendmetoduEmail to [address]: [message]döndürürPhoneNumberalanına sahipSMSNotifier—SendmetoduSMS to [phone]: [message]döndürür
dispatcher.go: Arayüz aracılığıyla herhangi bir bildirimci ile çalışan bir dağıtıcı (dispatcher) oluşturun.Notifierarayüzlerinden oluşan bir dilim (slice) tutan birNotificationDispatcheryapısı oluşturun. Şu metodları ekleyin:- Bir bildirimciyi kaydetmek için
AddNotifier(n Notifier) - Mesajı tüm kayıtlı bildirimciler aracılığıyla gönderen ve tüm onay dizilerinin bir dilimini döndüren
Broadcast(message string) []string
Ayrıca, başlatılmış bir dağıtıcı döndüren bir
NewNotificationDispatcher()yapıcı (constructor) fonksiyonu oluşturun.- Bir bildirimciyi kaydetmek için
main.go: Bildirimcileri ayarlayın ve mesajları yayınlayın.Oluşturulacak bildirimci sayısını okuyun. Her bildirimci için türünü (
emailveyasms) ve hedefini (adres veya telefon numarası) okuyun. Her birini bir dağıtıcıya ekleyin.Ardından yayınlanacak bir mesaj okuyun. Bu mesajı tüm bildirimciler aracılığıyla gönderin ve her bir onayı kendi satırında yazdırın.
Aşağıdaki girdiler sağlanacaktır:
- Satır 1: Bildirimci sayısı
- Her bildirimci için: tür (
emailveyasms), ardından hedef - Son satır: Yayınlanacak mesaj
Örneğin, şu girdi verildiğinde:
3
email
alice@example.com
sms
555-1234
email
bob@example.com
Server maintenance at midnightÇıktınız şu olmalıdır:
Email to alice@example.com: Server maintenance at midnight
SMS to 555-1234: Server maintenance at midnight
Email to bob@example.com: Server maintenance at midnightVe şu girdi verildiğinde:
2
sms
555-9999
sms
555-0000
Meeting canceledÇıktınız şu olmalıdır:
SMS to 555-9999: Meeting canceled
SMS to 555-0000: Meeting canceledBu yapı, arayüzlerin nasıl esnek ve genişletilebilir tasarımlar sağladığını gösterir. Dağıtıcı, belirli bildirimci türlerini bilmez; sadece onların Send yapabildiğini bilir. Bu, Observer ve Strategy gibi desenlerin üzerine inşa edildiği temeldir!
Kopya kağıdı
Tasarım desenleri, yazılım tasarımındaki yaygın sorunlara yönelik yeniden kullanılabilir çözümlerdir. Türlerin nasıl yapılandırılacağı ve etkileşimleri konusunda rehberlik eden şablonlardır.
Tasarım desenleri üç kategoriye ayrılır:
| Kategori | Amaç | Örnekler |
|---|---|---|
| Creational (Oluşturucu) | Nesne oluşturma mekanizmaları | Singleton, Factory, Builder |
| Structural (Yapısal) | Türleri daha büyük yapılar halinde birleştirme | Adapter, Decorator, Composite |
| Behavioral (Davranışsal) | Nesneler arası iletişim | Observer, Strategy, Command |
Go'da tasarım desenleri, sınıflar ve kalıtım yerine arayüzler (interfaces), yapı gömme (struct embedding) ve bileşim (composition) kullanılarak uygulanır. Geleneksel OOP dillerinin soyut sınıflar (abstract classes) kullandığı yerlerde Go arayüzleri kullanır. Kalıtım kullandıkları yerlerde ise Go gömmeyi kullanır.
Kendin dene
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Bildirimci sayısını oku
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
// Yeni bir dağıtıcı (dispatcher) oluştur
dispatcher := NewNotificationDispatcher()
// Her bir bildirimciyi oku ve dağıtıcıya ekle
for i := 0; i < count; i++ {
notifierType, _ := reader.ReadString('\n')
notifierType = strings.TrimSpace(notifierType)
destination, _ := reader.ReadString('\n')
destination = strings.TrimSpace(destination)
// TODO: Türe göre uygun bildirimciyi oluştur
// ve onu dağıtıcıya ekle
// İpucu: notifierType'ın "email" mi yoksa "sms" mi olduğunu kontrol et
}
// Yayınlanacak mesajı oku
message, _ := reader.ReadString('\n')
message = strings.TrimSpace(message)
// TODO: Mesajı yayınla ve her bir onayı yazdır
}
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ı3İş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