🎨 Tasarım Kalıpları — Öğrenme & Mülakat Odaklı
GoF kalıplarını gerçek dünyadan örnekler, kısa UML fikirleri ve Java kodlarıyla kavra. Ne zaman hangi kalıbı kullanacağını, tipik hataları ve mülakatta nasıl anlatacağını netleştir.
🔎 Genel Bakış
- Creational: Nesne yaratımını kontrol eder (Singleton, Factory Method, Abstract Factory, Builder, Prototype).
- Structural: Nesneler arası kompozisyonu düzenler (Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy).
- Behavioral: Sorumluluk ve işbirliğini tanımlar (Strategy, Template Method, Observer, Command, State, Chain of Responsibility, Mediator, Visitor, Memento, Iterator, Interpreter).
Altın kural: Kalıbı probleme uydur, problemi kalıba zorlamaya çalışma.
🧰 UML Mini Rehber
- Genelleme (Inheritance): boş üçgen ok.
- Birleşim/Kompozisyon: dolu/boş baklava.
- Arayüz gerçekleştirimi: kesikli çizgi + boş üçgen.
🏗️ Creational Patterns
1) Singleton — “Tek Örnek”
Ne zaman? Uygulama genelinde tek bir paylaşılan kaynak (config, cache, thread pool). Dikkat: Global state → test zorlaşır.
public class Singleton {
private static volatile Singleton INSTANCE;
private Singleton() {}
public static Singleton getInstance() {
if (INSTANCE == null) {
synchronized (Singleton.class) {
if (INSTANCE == null) INSTANCE = new Singleton();
}
}
return INSTANCE;
}
}
- Java 5+
volatile+ double-checked locking. - Spring konteyneri zaten singleton scope yönetir → manuel Singleton çoğu zaman gereksiz.
2) Factory Method — “Alt sınıf seçsin”
Üretim kararını alt sınıflara bırakır. Kazanım: istemci yaratım detayını bilmez.
interface Button { void render(); }
class WindowsButton implements Button { public void render(){ System.out.println("Win"); } }
class MacButton implements Button { public void render(){ System.out.println("Mac"); } }
abstract class Dialog {
abstract Button createButton(); // Factory Method
public void show(){ createButton().render(); }
}
class WindowsDialog extends Dialog { Button createButton(){ return new WindowsButton(); } }
class MacDialog extends Dialog { Button createButton(){ return new MacButton(); } }
3) Abstract Factory — “Aile üretimi”
Uyumlu ürün aileleri üretir (örn. Win UI ailesi: Button, Checkbox).
interface UIFactory { Button button(); Checkbox checkbox(); }
class WinFactory implements UIFactory { public Button button(){...} public Checkbox checkbox(){...} }
class MacFactory implements UIFactory { public Button button(){...} public Checkbox checkbox(){...} }
Ne zaman? Ürün aileleri arasında tutarlılık ve değiştirme kolaylığı.
4) Builder — “Adım adım inşa”
Çok parametreli karmaşık nesneleri okunur ve hatasız kurar.
public class User {
private final String username;
private final String email;
private final boolean newsletter;
private User(Builder b){ this.username=b.username; this.email=b.email; this.newsletter=b.newsletter; }
public static class Builder{
private String username, email; private boolean newsletter;
public Builder username(String v){ this.username=v; return this; }
public Builder email(String v){ this.email=v; return this; }
public Builder newsletter(boolean v){ this.newsletter=v; return this; }
public User build(){ return new User(this); }
}
}
Java’da record + builder kütüphaneleri (Lombok @Builder) pratik.
5) Prototype — “Kopyalayarak üret”
Oluşturması pahalı nesneleri kopyalayarak çoğalt.
class Doc implements Cloneable {
String text; List<String> tags = new ArrayList<>();
public Doc clone() throws CloneNotSupportedException {
Doc d = (Doc) super.clone();
d.tags = new ArrayList<>(this.tags); // deep copy
return d;
}
}
Dikkat: Derin/kısmi kopya stratejini netleştir.
🏛️ Structural Patterns
1) Adapter — “Uymayanı uydur”
Uyumsuz arayüzleri birlikte çalıştırır.
interface JsonLogger { void log(String json); }
class LegacyLogger { void write(String level, String msg){ /* ... */ } }
class LegacyToJsonAdapter implements JsonLogger {
private final LegacyLogger legacy;
LegacyToJsonAdapter(LegacyLogger l){ this.legacy=l; }
public void log(String json){ legacy.write("INFO", json); }
}
2) Bridge — “Soyutlama & implementasyon ayrımı”
Bağımsız değişebilen iki hiyerarşi: Remote (soyut) ve Device (implementasyon).
interface Device { void setVolume(int v); }
class Tv implements Device { public void setVolume(int v){ /* ... */ } }
abstract class Remote { protected Device device; Remote(Device d){ this.device=d; } }
class AdvancedRemote extends Remote { void mute(){ device.setVolume(0); } }
3) Composite — “Ağaç yapılarla tekil gibi davran”
interface Node { int size(); }
class FileNode implements Node { int bytes; public int size(){ return bytes; } }
class DirNode implements Node {
List<Node> children = new ArrayList<>();
public int size(){ return children.stream().mapToInt(Node::size).sum(); }
}
4) Decorator — “Sarmala, davranış ekle”
interface Notifier { void send(String msg); }
class EmailNotifier implements Notifier { public void send(String msg){ /* email */ } }
class SlackDecorator implements Notifier {
private final Notifier inner;
SlackDecorator(Notifier n){ this.inner=n; }
public void send(String msg){ inner.send(msg); /* + slack */ }
}
Ne zaman? Kalıtım patladığında kompozisyonla esnek eklemeler.
5) Facade — “Basit yüz”
Karmaşık alt sistemlere tek ve basit API sunar (örn. PaymentFacade).
6) Flyweight — “Paylaşımlı hafif nesne”
Yüksek sayıda küçük nesnede ortak, değişmeyen durumu paylaş.
7) Proxy — “Vekil nesne”
Erişim kontrolü, lazy yükleme, cache, uzak çağrı vekili.
interface Image { void display(); }
class RealImage implements Image { public void display(){ /* pahalı */ } }
class ImageProxy implements Image {
private RealImage real;
public void display(){ if(real==null) real=new RealImage(); real.display(); }
}
🧠 Behavioral Patterns
1) Strategy — “Algoritmayı değiştirilebilir yap”
interface SortStrategy { <T extends Comparable<T>> void sort(List<T> list); }
class QuickSort implements SortStrategy { public <T extends Comparable<T>> void sort(List<T> l){ /* ... */ } }
class MergeSort implements SortStrategy { public <T extends Comparable<T>> void sort(List<T> l){ /* ... */ } }
class Sorter {
private SortStrategy strategy;
public Sorter(SortStrategy s){ this.strategy=s; }
public <T extends Comparable<T>> void sort(List<T> list){ strategy.sort(list); }
}
Spring eşleniği: @Primary/@Qualifier ile runtime strateji seçimi.
2) Template Method — “İskeleti sabitle, adımları özelleştir”
abstract class DataExporter {
public final void export(){ open(); writeBody(); close(); } // template
protected abstract void writeBody();
private void open(){ /* ... */ } private void close(){ /* ... */ }
}
3) Observer — “Yayınla/Abone ol”
interface Observer { void onEvent(String msg); }
class EventBus {
private final List<Observer> obs = new ArrayList<>();
void subscribe(Observer o){ obs.add(o); }
void publish(String msg){ obs.forEach(o -> o.onEvent(msg)); }
}
Uygulama: GUI event’leri, domain event, mesaj kuyrukları.
4) Command — “İsteği nesneleştir”
interface Command { void execute(); }
class TransferCommand implements Command { public void execute(){ /* para transferi */ } }
class Invoker {
void run(Command c){ c.execute(); } // kuyruklanabilir, loglanabilir
}
5) State — “Duruma göre davranış”
interface State { void handle(Context ctx); }
class Draft implements State { public void handle(Context c){ /* ... */ c.state = new Published(); } }
class Published implements State { public void handle(Context c){ /* ... */ } }
class Context { State state = new Draft(); void next(){ state.handle(this); } }
6) Chain of Responsibility — “Zincirle sorumluluk”
abstract class Handler {
protected Handler next;
public Handler link(Handler n){ this.next=n; return n; }
public void handle(Request r){ if(next!=null) next.handle(r); }
}
Spring Security filter chain benzeri akışlar.
7) Mediator — “Merkezi arabulucu”
Bileşenler birbirini bilmeden mediator üzerinden konuşur.
8) Visitor — “Yapıya dokunmadan yeni işlem”
AST işlemleri, raporlama; çift dağıtım tekniği.
9) Memento — “Geri al için anı yakala”
Undo/redo senaryoları.
10) Iterator — “Gezgin”
Koleksiyon içeriğini sırayla gezmek için standart arayüz.
11) Interpreter — “Dilin yorumlayıcısı”
Basit DSL’ler, kurallar.
🚫 Anti-Pattern & Kod Kokuları
⚠️ Dikkat Edilecekler
- God Object: Aşırı yetenekli tek sınıf → sorumlulukları parçala (SRP).
- Spaghetti Code: Dallanıp budaklanmış bağımlılıklar → katman/port-adapter mimari.
- Overengineering: “Her yere pattern” → önce basit çözüm.
- Copy-Paste Inheritance: Kalıtıma aşırı yüklenme → kompozisyon & strateji.
- Singleton Abuse: Global mutable state → test ve concurrency sorunları.
🧭 Pattern Seçim Rehberi
| Sorun | Öneri Kalıp(lar) | Not |
|---|---|---|
| Çok parametreli karmaşık nesne | Builder | Okunabilirlik & doğruluk |
| Ürün aileleri ve değiştirme | Abstract Factory | Uyum ve bağımlılık ters çevrimi |
| Algoritmayı koşullara göre değiştir | Strategy / Template Method | Test edilebilirlik |
| Uyumsuz API’ler | Adapter | Refactor etmeye alternatif |
| Ek özellikleri dinamik ekle | Decorator | Kalıtım patladığında |
| Karmaşık alt sistemi basitleştir | Facade | Yüzey API |
| Durum makineleri | State | Açık akışlar |
| İş akışını parçalara böl | Command / CoR | Log/kuyruk/undo |
🌱 Spring ile Kalıplar (Doğal Eşleşmeler)
- Singleton → Spring bean varsayılan scope.
- Factory →
@Beanmetotları,FactoryBeanarayüzü. - Proxy → AOP,
@Transactional&@Cacheableproxy’leri. - Strategy → Çoklu
@Component+@Qualifier. - Template Method →
JdbcTemplate,RestTemplate. - Observer →
ApplicationEventPublisher,@EventListener. - Facade →
*Facadeservis bileşenleri ile alt sistemleri basitleştirme.
// Strategy örneği (Spring)
interface PricePolicy { BigDecimal calc(BigDecimal base); }
@Component("standard") class StandardPolicy implements PricePolicy { public BigDecimal calc(BigDecimal b){ return b; } }
@Component("premium") class PremiumPolicy implements PricePolicy { public BigDecimal calc(BigDecimal b){ return b.multiply(BigDecimal.valueOf(0.9)); } }
@Service
class PriceService {
private final Map<String, PricePolicy> strategies;
public PriceService(Map<String, PricePolicy> strategies){ this.strategies = strategies; }
public BigDecimal price(String type, BigDecimal base){ return strategies.getOrDefault(type, strategies.get("standard")).calc(base); }
}
❓ Mülakat Soruları & Örnek Yanıtlar
1) Strategy vs Template Method farkı?
Strategy çalışma zamanında algoritma seçer (kompozisyon). Template iskeleti sabitler, alt sınıf adımını özelleştirir (kalıtım).
2) Ne zaman Decorator, ne zaman Proxy?
Decorator davranış zenginleştirir; Proxy erişim kontrolü/lazy load/caching gibi araya girer. Niyet farkına odaklan.
3) Singleton’ın riskleri?
Global mutable state, test izolasyonu, concurrency. Spring singleton bean tercih et; manuel statik örneklerden kaçın.
4) Abstract Factory ile Factory Method ilişkisi?
Abstract Factory aile üretir (çoklu ürün), içindeki her ürün için Factory Method kullanabilir.
5) State ile Strategy farkı?
İkisi de kompozisyon kullanır; State nesnenin iç durumuna göre zamanla davranışı değiştirir, Strategy dışarıdan seçilir.
6) CoR gerçek kullanım örneği?
HTTP filtre zinciri, onay akışı (müdür → direktör → CFO), validation pipeline.
7) Observer ile Event-Driven farkı?
Observer aynı süreçte eşzamanlı olabilir; event-driven genellikle asenkron kuyruk/mesaj aracısı içerir (Kafka, RabbitMQ).
8) Overengineering’i nasıl önlersin?
Önce en basit çözüm; gereksinim kanıtlanınca pattern ekle; YAGNI ve KISS ilkeleri.