Design Patterns Uebersicht
Design Patterns: Bewährte Lösungsmuster
Design Patterns sind wiederverwendbare Lösungen für häufige Probleme. Lernen Sie die wichtigsten Muster und ihre Anwendung.
Kategorien
Design Patterns
├── Creational (Erzeugung)
│ ├── Singleton
│ ├── Factory
│ └── Builder
│
├── Structural (Struktur)
│ ├── Adapter
│ ├── Decorator
│ └── Facade
│
└── Behavioral (Verhalten)
├── Observer
├── Strategy
└── Command
Singleton
// Nur eine Instanz existiert
class Database {
private static instance: Database;
private connection: Connection;
private constructor() {
this.connection = createConnection();
}
static getInstance(): Database {
if (!Database.instance) {
Database.instance = new Database();
}
return Database.instance;
}
query(sql: string) {
return this.connection.execute(sql);
}
}
// Verwendung
const db1 = Database.getInstance();
const db2 = Database.getInstance();
console.log(db1 === db2); // true
// Besser in modernem Code: Dependency Injection!
Factory
// Objekt-Erzeugung abstrahieren
interface Notification {
send(message: string): void;
}
class EmailNotification implements Notification {
send(message: string) {
console.log(`Email: ${message}`);
}
}
class SMSNotification implements Notification {
send(message: string) {
console.log(`SMS: ${message}`);
}
}
class PushNotification implements Notification {
send(message: string) {
console.log(`Push: ${message}`);
}
}
// Factory
class NotificationFactory {
static create(type: 'email' | 'sms' | 'push'): Notification {
switch (type) {
case 'email': return new EmailNotification();
case 'sms': return new SMSNotification();
case 'push': return new PushNotification();
default: throw new Error(`Unknown type: ${type}`);
}
}
}
// Verwendung
const notification = NotificationFactory.create('email');
notification.send('Hallo!');
Builder
// Komplexe Objekte schrittweise aufbauen
class QueryBuilder {
private table: string;
private columns: string[] = ['*'];
private conditions: string[] = [];
private orderBy: string | null = null;
private limitValue: number | null = null;
from(table: string): this {
this.table = table;
return this;
}
select(...columns: string[]): this {
this.columns = columns;
return this;
}
where(condition: string): this {
this.conditions.push(condition);
return this;
}
order(column: string, direction: 'ASC' | 'DESC' = 'ASC'): this {
this.orderBy = `${column} ${direction}`;
return this;
}
limit(n: number): this {
this.limitValue = n;
return this;
}
build(): string {
let query = `SELECT ${this.columns.join(', ')} FROM ${this.table}`;
if (this.conditions.length) {
query += ` WHERE ${this.conditions.join(' AND ')}`;
}
if (this.orderBy) query += ` ORDER BY ${this.orderBy}`;
if (this.limitValue) query += ` LIMIT ${this.limitValue}`;
return query;
}
}
// Verwendung
const query = new QueryBuilder()
.from('users')
.select('id', 'name', 'email')
.where('active = true')
.where('age > 18')
.order('name')
.limit(10)
.build();
Observer
// Benachrichtigung bei Zustandsänderungen
interface Observer {
update(event: string, data: any): void;
}
class EventEmitter {
private observers: Map<string, Observer[]> = new Map();
subscribe(event: string, observer: Observer): void {
if (!this.observers.has(event)) {
this.observers.set(event, []);
}
this.observers.get(event)!.push(observer);
}
unsubscribe(event: string, observer: Observer): void {
const observers = this.observers.get(event);
if (observers) {
const index = observers.indexOf(observer);
if (index > -1) observers.splice(index, 1);
}
}
emit(event: string, data: any): void {
const observers = this.observers.get(event);
if (observers) {
observers.forEach(obs => obs.update(event, data));
}
}
}
// Verwendung
const emitter = new EventEmitter();
const logger: Observer = {
update(event, data) {
console.log(`[${event}]`, data);
}
};
emitter.subscribe('user.created', logger);
emitter.emit('user.created', { id: 1, name: 'Max' });
Strategy
// Algorithmen austauschbar machen
interface PaymentStrategy {
pay(amount: number): void;
}
class CreditCardPayment implements PaymentStrategy {
constructor(private cardNumber: string) {}
pay(amount: number) {
console.log(`Paid ${amount}€ with Credit Card ${this.cardNumber}`);
}
}
class PayPalPayment implements PaymentStrategy {
constructor(private email: string) {}
pay(amount: number) {
console.log(`Paid ${amount}€ via PayPal (${this.email})`);
}
}
class ShoppingCart {
private items: { price: number }[] = [];
private paymentStrategy: PaymentStrategy;
setPaymentStrategy(strategy: PaymentStrategy) {
this.paymentStrategy = strategy;
}
addItem(item: { price: number }) {
this.items.push(item);
}
checkout() {
const total = this.items.reduce((sum, item) => sum + item.price, 0);
this.paymentStrategy.pay(total);
}
}
// Verwendung
const cart = new ShoppingCart();
cart.addItem({ price: 100 });
cart.addItem({ price: 50 });
cart.setPaymentStrategy(new CreditCardPayment('4111-1111-1111-1111'));
cart.checkout(); // Paid 150€ with Credit Card
Decorator
// Funktionalität dynamisch erweitern
interface Coffee {
cost(): number;
description(): string;
}
class SimpleCoffee implements Coffee {
cost() { return 2; }
description() { return 'Kaffee'; }
}
abstract class CoffeeDecorator implements Coffee {
constructor(protected coffee: Coffee) {}
abstract cost(): number;
abstract description(): string;
}
class MilkDecorator extends CoffeeDecorator {
cost() { return this.coffee.cost() + 0.5; }
description() { return this.coffee.description() + ' + Milch'; }
}
class SugarDecorator extends CoffeeDecorator {
cost() { return this.coffee.cost() + 0.2; }
description() { return this.coffee.description() + ' + Zucker'; }
}
// Verwendung
let coffee: Coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
console.log(coffee.description()); // Kaffee + Milch + Zucker
console.log(coffee.cost()); // 2.7
Facade
// Komplexes System hinter einfacher Schnittstelle
class CPU {
freeze() { console.log('CPU freeze'); }
execute() { console.log('CPU execute'); }
}
class Memory {
load(address: number) { console.log(`Memory load at ${address}`); }
}
class HardDrive {
read(sector: number) { console.log(`HardDrive read sector ${sector}`); }
}
// Facade
class Computer {
private cpu = new CPU();
private memory = new Memory();
private hardDrive = new HardDrive();
start() {
this.cpu.freeze();
this.memory.load(0);
this.hardDrive.read(0);
this.cpu.execute();
console.log('Computer started!');
}
}
// Verwendung - einfach!
const computer = new Computer();
computer.start();
💡 Tipp:
Wenden Sie Patterns nicht blind an. Verstehen Sie das Problem zuerst, dann wählen Sie das passende Pattern - oder keins!