Go Tasarim Desenleri: Go Gelistiricileri icin Temel Desenler ve Mulakat Sorulari
Go tasarim desenlerinin kapsamli rehberi: Functional Options, Strategy, Factory, Observer ve Middleware desenleri. Pratik kod ornekleri ve Go mulakat sorulari.

Go programlama dili, basitliği ve performansı ile modern yazılım geliştirme dünyasında önemli bir yer edinmiştir. Ancak bu basitlik, tasarım desenlerinin gereksiz olduğu anlamına gelmemektedir. Aksine, Go'nun kendine özgü yapısı ve felsefesi, klasik nesne yönelimli tasarım desenlerinin dile uygun şekilde yeniden yorumlanmasını gerektirmektedir. Teknik mülakatlarda Go tasarım desenleri hakkında sorulan sorular, adayların dili ne kadar derinlemesine anladığını ortaya koymaktadır.
Go, kalıtım yerine kompozisyon, sınıflar yerine arayüzler ve karmaşık hiyerarşiler yerine basit yapılar tercih eden bir dildir. Bu nedenle klasik Gang of Four tasarım desenleri Go'da farklı biçimlerde uygulanmaktadır. Mülakatlarda bu farklılıkları anlamak ve açıklayabilmek büyük avantaj sağlamaktadır.
Functional Options Deseni
Functional Options deseni, Go ekosisteminde en yaygın kullanılan yapılandırma desenlerinden biridir. Bu desen, bir yapının oluşturulması sırasında opsiyonel parametrelerin esnek bir şekilde geçirilmesini sağlamaktadır. Özellikle çok sayıda yapılandırma seçeneği bulunan kütüphanelerde ve servislerde tercih edilmektedir.
Geleneksel yapılandırıcı (constructor) yaklaşımlarında, parametre sayısı arttıkça fonksiyon imzası karmaşıklaşmakta ve okunabilirlik azalmaktadır. Functional Options deseni bu sorunu, her bir seçenek için ayrı bir fonksiyon tanımlayarak çözmektedir. Bu fonksiyonlar, ana yapıyı değiştiren closure'lar döndürmektedir.
package server
import (
"time"
"log/slog"
)
// Server holds the HTTP server configuration.
type Server struct {
host string
port int
timeout time.Duration
maxConns int
logger *slog.Logger
}
// Option defines a functional option for Server.
type Option func(*Server)
// WithPort sets the server port.
func WithPort(port int) Option {
return func(s *Server) {
s.port = port
}
}
// WithTimeout sets the request timeout.
func WithTimeout(d time.Duration) Option {
return func(s *Server) {
s.timeout = d
}
}
// WithMaxConns sets the maximum concurrent connections.
func WithMaxConns(n int) Option {
return func(s *Server) {
s.maxConns = n
}
}
// New creates a Server with sensible defaults and applies options.
func New(host string, opts ...Option) *Server {
srv := &Server{
host: host,
port: 8080, // default port
timeout: 30 * time.Second, // default timeout
maxConns: 100, // default max connections
logger: slog.Default(),
}
for _, opt := range opts {
opt(srv)
}
return srv
}Bu desenin en önemli avantajlarından biri geriye dönük uyumluluktur. Yeni seçenekler eklendiğinde mevcut kod değişiklik gerektirmemektedir. Ayrıca varsayılan değerler merkezi bir noktada tanımlandığından, yapılandırma tutarlılığı sağlanmaktadır. Mülakatlarda bu desenin Rob Pike tarafından popülerleştirildiğini ve gRPC, Uber Zap gibi büyük projelerde kullanıldığını belirtmek faydalı olmaktadır.
Strategy Deseni
Strategy deseni, bir algoritma ailesini tanımlayarak bunları birbirinin yerine kullanılabilir hale getirmektedir. Go'da bu desen, arayüzler (interfaces) aracılığıyla doğal bir şekilde uygulanmaktadır. Ödeme sistemleri, sıralama algoritmaları ve şifreleme yöntemleri gibi farklı davranışların değiştirilebilir olması gereken durumlarda tercih edilmektedir.
Go'nun arayüz sistemi, Strategy desenini son derece zarif bir şekilde desteklemektedir. Bir arayüz tanımlandığında, bu arayüzü implemente eden herhangi bir tip strateji olarak kullanılabilmektedir. Bu yaklaşım, bağımlılık enjeksiyonu ve test edilebilirlik açısından da önemli avantajlar sağlamaktadır.
package payment
import "fmt"
// Processor defines the strategy interface.
type Processor interface {
Pay(amount float64) (string, error)
}
// CreditCard implements Processor for card payments.
type CreditCard struct {
CardNumber string
Expiry string
}
func (c *CreditCard) Pay(amount float64) (string, error) {
// Charge the card via payment gateway
return fmt.Sprintf("charged %.2f to card ending %s", amount, c.CardNumber[len(c.CardNumber)-4:]), nil
}
// BankTransfer implements Processor for wire transfers.
type BankTransfer struct {
IBAN string
}
func (b *BankTransfer) Pay(amount float64) (string, error) {
return fmt.Sprintf("initiated transfer of %.2f to %s", amount, b.IBAN), nil
}
// Checkout processes a payment using the given strategy.
func Checkout(p Processor, amount float64) error {
receipt, err := p.Pay(amount)
if err != nil {
return fmt.Errorf("payment failed: %w", err)
}
fmt.Println(receipt)
return nil
}Strategy deseninin Go'daki uygulamasında dikkat edilmesi gereken nokta, arayüzlerin küçük tutulmasıdır. Go topluluğunda yaygın olan "küçük arayüzler" prensibi, Strategy deseninde de geçerlidir. Tek metodlu arayüzler tercih edilmekte ve bu sayede esneklik maksimize edilmektedir.
Factory Fonksiyonları
Factory deseni, nesne oluşturma mantığını kapsüllemek için kullanılmaktadır. Go'da sınıf yapıcıları bulunmadığından, factory fonksiyonları bu görevi üstlenmektedir. Bu fonksiyonlar genellikle New veya NewXxx şeklinde adlandırılmakta ve yapının geçerli bir durumda oluşturulmasını garanti etmektedir.
Factory fonksiyonlarının en kritik özelliklerinden biri doğrulama (validation) yapabilmeleridir. Geçersiz parametreler ile bir yapı oluşturulmaya çalışıldığında, fonksiyon hata döndürerek programın tutarsız bir duruma girmesini engellemektedir. Bu yaklaşım, Go'nun hata işleme felsefesi ile tam uyumludur.
package pool
import (
"errors"
"sync"
)
// ConnPool manages a pool of reusable connections.
type ConnPool struct {
mu sync.Mutex
conns []Conn
maxSize int
}
// Conn represents a database connection.
type Conn struct {
ID int
Active bool
}
// NewConnPool validates parameters and returns an initialized pool.
func NewConnPool(maxSize int) (*ConnPool, error) {
if maxSize <= 0 {
return nil, errors.New("pool: maxSize must be positive")
}
return &ConnPool{
conns: make([]Conn, 0, maxSize),
maxSize: maxSize,
}, nil
}
// Acquire returns a connection from the pool.
func (p *ConnPool) Acquire() (*Conn, error) {
p.mu.Lock()
defer p.mu.Unlock()
for i := range p.conns {
if !p.conns[i].Active {
p.conns[i].Active = true
return &p.conns[i], nil
}
}
if len(p.conns) >= p.maxSize {
return nil, errors.New("pool: no available connections")
}
c := Conn{ID: len(p.conns) + 1, Active: true}
p.conns = append(p.conns, c)
return &p.conns[len(p.conns)-1], nil
}Mülakatlarda factory fonksiyonları ile Functional Options deseninin birlikte kullanımı hakkında sorular sıklıkla karşılaşılmaktadır. Bu iki desenin kombinasyonu, hem doğrulama hem de esnek yapılandırma ihtiyaçlarını karşılayan güçlü bir çözüm sunmaktadır.
Go mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Observer Deseni
Observer deseni, bir nesnenin durumundaki değişikliklerin diğer nesnelere otomatik olarak bildirilmesini sağlamaktadır. Go'da bu desen genellikle channel'lar ve goroutine'ler kullanılarak uygulanmaktadır. Event-driven mimarilerde, pub/sub sistemlerinde ve reaktif programlama senaryolarında yaygın olarak kullanılmaktadır.
Go'nun eşzamanlılık primitifleri, Observer deseninin uygulanmasını oldukça kolaylaştırmaktadır. Channel'lar, subscriber'lar ile publisher arasındaki iletişimi type-safe bir şekilde sağlamaktadır. Buffered channel kullanımı, yavaş subscriber'ların publisher'ı bloke etmesini engellemektedir.
package events
import "sync"
// Event carries a topic and payload.
type Event struct {
Topic string
Payload any
}
// Bus manages subscriptions and event dispatch.
type Bus struct {
mu sync.RWMutex
subscribers map[string][]chan Event
}
// NewBus creates an event bus.
func NewBus() *Bus {
return &Bus{
subscribers: make(map[string][]chan Event),
}
}
// Subscribe returns a channel that receives events for a topic.
func (b *Bus) Subscribe(topic string) <-chan Event {
ch := make(chan Event, 16) // buffered to avoid blocking publisher
b.mu.Lock()
b.subscribers[topic] = append(b.subscribers[topic], ch)
b.mu.Unlock()
return ch
}
// Publish sends an event to all subscribers of the topic.
func (b *Bus) Publish(topic string, payload any) {
b.mu.RLock()
defer b.mu.RUnlock()
for _, ch := range b.subscribers[topic] {
select {
case ch <- Event{Topic: topic, Payload: payload}:
default:
// subscriber too slow, drop event
}
}
}Bu implementasyonda sync.RWMutex kullanımı dikkat çekicidir. Okuma işlemleri (Publish) sırasında RLock, yazma işlemleri (Subscribe) sırasında Lock kullanılarak eşzamanlı okuma performansı optimize edilmektedir. Mülakatlarda bu detayın açıklanması, concurrency konusundaki bilgi derinliğini göstermektedir.
Middleware Deseni
Middleware deseni, özellikle HTTP sunucularında yaygın olarak kullanılmaktadır. Bu desen, cross-cutting concern'lerin (logging, authentication, rate limiting) ana iş mantığından ayrılmasını sağlamaktadır. Go'nun http.Handler arayüzü, middleware zincirlerinin oluşturulması için ideal bir temel sunmaktadır.
Middleware fonksiyonları, bir http.Handler alıp yeni bir http.Handler döndüren fonksiyonlardır. Bu fonksiyonel kompozisyon yaklaşımı, middleware'lerin sıralı olarak uygulanmasını ve her birinin bağımsız test edilmesini mümkün kılmaktadır.
package middleware
import (
"log/slog"
"net/http"
"time"
)
// Logging records request duration and status.
func Logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
wrapped := &statusWriter{ResponseWriter: w, status: 200}
next.ServeHTTP(wrapped, r)
slog.Info("request",
"method", r.Method,
"path", r.URL.Path,
"status", wrapped.status,
"duration", time.Since(start),
)
})
}
// statusWriter captures the HTTP status code.
type statusWriter struct {
http.ResponseWriter
status int
}
func (w *statusWriter) WriteHeader(code int) {
w.status = code
w.ResponseWriter.WriteHeader(code)
}
// Chain applies middleware in order: first listed = outermost.
func Chain(h http.Handler, mw ...func(http.Handler) http.Handler) http.Handler {
for i := len(mw) - 1; i >= 0; i-- {
h = mw[i](h)
}
return h
}Middleware deseninde statusWriter yapısının kullanımı önemli bir tekniktir. Standart http.ResponseWriter status code'u yakalamak için bir yöntem sunmamaktadır. Bu wrapper yapı sayesinde response status code'u logging için erişilebilir hale gelmektedir. Bu teknik, mülakatlarda Go'nun arayüz sarmalama (interface wrapping) yeteneğini göstermek için güzel bir örnektir.
Struct Embedding ile Kompozisyon
Go, kalıtım yerine kompozisyon tercih eden bir dildir. Struct embedding, bu kompozisyon yaklaşımının temel mekanizmasıdır. Bir struct başka bir struct'ı gömüldüğünde (embed), gömülen struct'ın alanları ve metodları doğrudan erişilebilir hale gelmektedir.
Bu yaklaşım, kod tekrarını önlemek ve ortak davranışları paylaşmak için etkili bir yöntemdir. Özellikle veritabanı modelleri, audit alanları ve ortak yapılandırma seçenekleri için sıklıkla kullanılmaktadır.
package models
import "time"
// Timestamps provides common audit fields.
type Timestamps struct {
CreatedAt time.Time
UpdatedAt time.Time
}
// User embeds Timestamps to gain CreatedAt/UpdatedAt.
type User struct {
Timestamps
ID int
Email string
}
// Order also embeds Timestamps.
type Order struct {
Timestamps
ID int
UserID int
Total float64
}Struct embedding'in kalıtımdan önemli bir farkı bulunmaktadır: Go'da "is-a" ilişkisi yerine "has-a" ilişkisi söz konusudur. Bir User bir Timestamps değildir, bir Timestamps içermektedir. Bu ayrım, mülakatlarda Go'nun tasarım felsefesini anlamak açısından kritik öneme sahiptir.
Mülakat Soruları ve Cevapları
Teknik mülakatlarda Go tasarım desenleri hakkında çeşitli sorular sorulmaktadır. Bu bölümde en sık karşılaşılan sorular ve beklenen cevaplar incelenmektedir.
Soru: Go'da Singleton deseni nasıl uygulanır ve ne zaman kullanılmalıdır?
Go'da Singleton deseni genellikle sync.Once kullanılarak uygulanmaktadır. Bu yapı, bir fonksiyonun tüm program yaşam döngüsü boyunca yalnızca bir kez çalışmasını garanti etmektedir. Veritabanı bağlantı havuzları, yapılandırma yöneticileri ve logger'lar için uygundur. Ancak global state oluşturduğundan test edilebilirliği azaltmakta ve bağımlılık enjeksiyonu tercih edilmelidir.
Soru: Functional Options deseni ile Builder deseni arasındaki farklar nelerdir?
Her iki desen de karmaşık nesnelerin oluşturulmasını kolaylaştırmaktadır. Ancak Builder deseni genellikle ayrı bir builder nesnesi gerektirirken, Functional Options deseni closure'lar kullanmaktadır. Go'da Functional Options daha idiomatik kabul edilmektedir çünkü method chaining gerektirmemekte ve zero-value uyumluluğu sağlamaktadır.
Go'da arayüzler tüketen tarafta (consumer) tanımlanmalıdır, sağlayan tarafta (provider) değil. Bu yaklaşım, bağımlılıkların tersine çevrilmesi (Dependency Inversion) prensibini doğal olarak uygulamakta ve mock oluşturmayı kolaylaştırmaktadır.
Soru: Go'da Decorator deseni nasıl uygulanır?
Decorator deseni, Go'da fonksiyon sarmalama (function wrapping) ile uygulanmaktadır. Middleware deseni bunun en yaygın örneğidir. Bir fonksiyon başka bir fonksiyonu parametre olarak alıp, ek davranış ekleyerek yeni bir fonksiyon döndürmektedir. Bu yaklaşım, io.Reader ve io.Writer arayüzleri etrafında da yaygın olarak kullanılmaktadır.
Go'da klasik nesne yönelimli tasarım desenlerini birebir uygulamaya çalışmak yaygın bir hatadır. Go'nun arayüz sistemi, channel'ları ve goroutine'leri bu desenlerin daha basit ve idiomatik alternatiflerini sunmaktadır. Mülakatlarda bu farklılığı vurgulamak önemlidir.
Soru: Context paketi tasarım desenleri ile nasıl kullanılır?
Context paketi, özellikle timeout, cancellation ve request-scoped değerlerin yönetimi için kullanılmaktadır. Middleware deseninde context, request boyunca taşınması gereken değerler için kullanılmaktadır. Strategy deseninde ise context, uzun süren işlemlerin iptal edilebilmesini sağlamaktadır. Her arayüz metodunun ilk parametresi olarak context.Context alması, Go'da yaygın bir pratiktir.
Go mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Sonuç
Go tasarım desenleri, dilin kendine özgü özelliklerini yansıtan ve klasik OOP desenlerinden farklılaşan yapılardır. Mülakatlarda başarılı olmak için aşağıdaki noktaların anlaşılması gerekmektedir:
- Functional Options deseni, esnek yapılandırma ve geriye dönük uyumluluk için tercih edilmelidir
- Strategy deseni, Go'nun arayüz sistemi ile doğal bir şekilde uygulanmakta ve test edilebilirliği artırmaktadır
- Factory fonksiyonları, yapıların geçerli durumda oluşturulmasını garanti etmekte ve doğrulama mantığını kapsüllemektedir
- Observer deseni, channel'lar ve goroutine'ler ile etkili bir şekilde uygulanabilmektedir
- Middleware deseni, cross-cutting concern'lerin ayrılması için standart yöntemdir
- Struct embedding, kalıtım yerine kompozisyon prensibini uygulamaktadır
- Arayüzler küçük tutulmalı ve tüketen tarafta tanımlanmalıdır
- Context kullanımı, modern Go uygulamalarında zorunlu bir pratiktir
Bu desenlerin anlaşılması ve doğru bağlamda uygulanabilmesi, Go geliştiricileri için temel bir yetkinliktir. Mülakatlarda teorik bilginin yanı sıra, bu desenlerin gerçek dünya senaryolarında nasıl kullanıldığının açıklanabilmesi beklenmektedir.
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Etiketler
Paylaş
İlgili makaleler

Go 1.26 Mulakat Rehberi: Green Tea GC, go fix Araci ve Stack Optimizasyonlari
Go 1.26 surumunde yer alan Green Tea cep toplayici, yenilenmis go fix araci ve stack bellek optimizasyonlari uzerine kapsamli mulakat rehberi. Kod ornekleri ve teknik aciklamalar ile desteklenmistir.

En çok sorulan 25 Go mülakat sorusu: tam geliştirici rehberi
Go mülakatlarına hâkim olmak için en çok sorulan 25 soru. Goroutine, channel, arayüz ve eşzamanlılık desenleri kod örnekleriyle.

Go Teknik Mulakat: Goroutine'lar, Channel'lar ve Concurrency
Goroutine'lar, channel'lar ve eszamanlilik kaliplari uzerine Go mulakat sorulari. Kod ornekleri, yaygin tuzaklar ve 2026 yilinda Go teknik mulakatina hazirlanmak icin uzman duzeyinde cevaplar.