# Entwurfsmuster Diese Dokumentation beschreibt die wichtigsten Entwurfsmuster, die im Framework verwendet werden. Das Verständnis dieser Muster hilft bei der effektiven Nutzung des Frameworks und bei der Erweiterung seiner Funktionalität. ## Strukturelle Muster ### Adapter Das Adapter-Muster wird verwendet, um Schnittstellen von inkompatiblen Klassen anzupassen, sodass sie zusammenarbeiten können. **Verwendung im Framework:** - Datenbank-Adapter für verschiedene Datenbanktypen - Cache-Adapter für verschiedene Cache-Backends - Logging-Adapter für verschiedene Logging-Systeme **Beispiel:** ```php // Interface für die Zielschnittstelle interface CacheInterface { public function get(string $key, mixed $default = null): mixed; public function set(string $key, mixed $value, int $ttl = null): bool; public function delete(string $key): bool; } // Adapter für Redis class RedisCacheAdapter implements CacheInterface { private $redis; public function __construct(Redis $redis) { $this->redis = $redis; } public function get(string $key, mixed $default = null): mixed { $value = $this->redis->get($key); return $value !== false ? unserialize($value) : $default; } public function set(string $key, mixed $value, int $ttl = null): bool { return $this->redis->set($key, serialize($value), $ttl); } public function delete(string $key): bool { return $this->redis->del($key) > 0; } } ``` ### Composite Das Composite-Muster wird verwendet, um Objekte in Baumstrukturen zu organisieren und Einzelobjekte und Kompositionen einheitlich zu behandeln. **Verwendung im Framework:** - Menüsystem mit verschachtelten Menüpunkten - Validierungsregeln mit komplexen Bedingungen - Routengruppen mit verschachtelten Routen **Beispiel:** ```php // Basiskomponente interface MenuItemInterface { public function render(): string; } // Blattkomponente class MenuItem implements MenuItemInterface { private string $label; private string $url; public function __construct(string $label, string $url) { $this->label = $label; $this->url = $url; } public function render(): string { return '
  • ' . $this->label . '
  • '; } } // Kompositkomponente class MenuGroup implements MenuItemInterface { private string $label; private array $items = []; public function __construct(string $label) { $this->label = $label; } public function addItem(MenuItemInterface $item): void { $this->items[] = $item; } public function render(): string { $output = '
  • ' . $this->label . '
  • '; return $output; } } ``` ### Decorator Das Decorator-Muster wird verwendet, um Objekten dynamisch zusätzliche Verantwortlichkeiten hinzuzufügen, ohne ihre Struktur zu ändern. **Verwendung im Framework:** - HTTP-Middleware-Stack - Caching-Dekoratoren für Repository-Klassen - Logging-Dekoratoren für Service-Klassen **Beispiel:** ```php // Basiskomponente interface RepositoryInterface { public function find(int $id): ?Model; public function findAll(): array; } // Konkrete Komponente class UserRepository implements RepositoryInterface { public function find(int $id): ?Model { // Benutzer aus der Datenbank abrufen } public function findAll(): array { // Alle Benutzer aus der Datenbank abrufen } } // Basisdekorator abstract class RepositoryDecorator implements RepositoryInterface { protected RepositoryInterface $repository; public function __construct(RepositoryInterface $repository) { $this->repository = $repository; } public function find(int $id): ?Model { return $this->repository->find($id); } public function findAll(): array { return $this->repository->findAll(); } } // Konkreter Dekorator class CachingRepositoryDecorator extends RepositoryDecorator { private CacheInterface $cache; public function __construct(RepositoryInterface $repository, CacheInterface $cache) { parent::__construct($repository); $this->cache = $cache; } public function find(int $id): ?Model { $cacheKey = 'user:' . $id; if ($this->cache->has($cacheKey)) { return $this->cache->get($cacheKey); } $user = $this->repository->find($id); if ($user) { $this->cache->set($cacheKey, $user, 3600); } return $user; } } ``` ### Facade Das Facade-Muster bietet eine vereinfachte Schnittstelle zu einem komplexen Subsystem. **Verwendung im Framework:** - Datenbank-Facade für einfachen Zugriff auf Datenbankoperationen - Cache-Facade für einfachen Zugriff auf Cache-Operationen - Auth-Facade für einfachen Zugriff auf Authentifizierungsfunktionen **Beispiel:** ```php // Komplexes Subsystem class Authentication { public function checkCredentials(string $username, string $password): bool { // Überprüfen der Anmeldeinformationen } } class Session { public function set(string $key, mixed $value): void { // Wert in der Sitzung speichern } public function get(string $key): mixed { // Wert aus der Sitzung abrufen } } class User { public function findByUsername(string $username): ?User { // Benutzer nach Benutzernamen suchen } } // Facade class Auth { private Authentication $authentication; private Session $session; private User $user; public function __construct(Authentication $authentication, Session $session, User $user) { $this->authentication = $authentication; $this->session = $session; $this->user = $user; } public function login(string $username, string $password): bool { if ($this->authentication->checkCredentials($username, $password)) { $user = $this->user->findByUsername($username); $this->session->set('user_id', $user->id); return true; } return false; } public function logout(): void { $this->session->set('user_id', null); } public function check(): bool { return $this->session->get('user_id') !== null; } } ``` ### Proxy Das Proxy-Muster stellt einen Platzhalter für ein anderes Objekt bereit, um den Zugriff darauf zu kontrollieren. **Verwendung im Framework:** - Lazy-Loading-Proxies für ressourcenintensive Objekte - Remote-Proxies für API-Aufrufe - Zugriffssteuerung-Proxies für Autorisierung **Beispiel:** ```php // Subjektschnittstelle interface ImageInterface { public function display(): void; } // Reales Subjekt class Image implements ImageInterface { private string $filename; public function __construct(string $filename) { $this->filename = $filename; $this->loadImageFromDisk(); // Teure Operation } private function loadImageFromDisk(): void { echo "Loading image {$this->filename} from disk...\n"; // Bild laden } public function display(): void { echo "Displaying image {$this->filename}\n"; } } // Proxy class LazyImage implements ImageInterface { private ?Image $realImage = null; private string $filename; public function __construct(string $filename) { $this->filename = $filename; } public function display(): void { if ($this->realImage === null) { $this->realImage = new Image($this->filename); } $this->realImage->display(); } } ``` ## Verhaltensmuster ### Chain of Responsibility Das Chain of Responsibility-Muster ermöglicht es, eine Anfrage entlang einer Kette von Handlern zu senden, wobei jeder Handler entscheidet, ob er die Anfrage verarbeitet oder an den nächsten Handler weiterleitet. **Verwendung im Framework:** - Middleware-Pipeline für HTTP-Anfragen - Ereignislistener-Kette - Validierungskette **Beispiel:** ```php // Handler-Schnittstelle interface RequestHandlerInterface { public function handle(Request $request): Response; } // Konkreter Handler class AuthenticationMiddleware implements RequestHandlerInterface { private RequestHandlerInterface $next; public function setNext(RequestHandlerInterface $next): void { $this->next = $next; } public function handle(Request $request): Response { if (!$this->isAuthenticated($request)) { return new Response('Unauthorized', 401); } return $this->next->handle($request); } private function isAuthenticated(Request $request): bool { // Überprüfen, ob der Benutzer authentifiziert ist } } // Konkreter Handler class AuthorizationMiddleware implements RequestHandlerInterface { private RequestHandlerInterface $next; public function setNext(RequestHandlerInterface $next): void { $this->next = $next; } public function handle(Request $request): Response { if (!$this->isAuthorized($request)) { return new Response('Forbidden', 403); } return $this->next->handle($request); } private function isAuthorized(Request $request): bool { // Überprüfen, ob der Benutzer autorisiert ist } } ``` ### Command Das Command-Muster kapselt eine Anfrage als Objekt, wodurch es möglich wird, Anfragen zu parametrisieren, in eine Warteschlange zu stellen, zu protokollieren und rückgängig zu machen. **Verwendung im Framework:** - Konsolenbefehle - Aufgaben in der Warteschlange - Transaktionen **Beispiel:** ```php // Befehlsschnittstelle interface CommandInterface { public function execute(): void; } // Konkreter Befehl class CreateUserCommand implements CommandInterface { private UserRepository $userRepository; private string $name; private string $email; public function __construct(UserRepository $userRepository, string $name, string $email) { $this->userRepository = $userRepository; $this->name = $name; $this->email = $email; } public function execute(): void { $user = new User(); $user->name = $this->name; $user->email = $this->email; $this->userRepository->save($user); } } // Aufrufer class CommandBus { public function dispatch(CommandInterface $command): void { $command->execute(); } } ``` ### Observer Das Observer-Muster definiert eine Eins-zu-viele-Abhängigkeit zwischen Objekten, sodass alle abhängigen Objekte automatisch benachrichtigt und aktualisiert werden, wenn ein Objekt seinen Zustand ändert. **Verwendung im Framework:** - Ereignissystem - Modell-Beobachter - Konfigurationsänderungen **Beispiel:** ```php // Subjektschnittstelle interface SubjectInterface { public function attach(ObserverInterface $observer): void; public function detach(ObserverInterface $observer): void; public function notify(): void; } // Beobachterschnittstelle interface ObserverInterface { public function update(SubjectInterface $subject): void; } // Konkretes Subjekt class User implements SubjectInterface { private array $observers = []; private string $name; private string $email; public function attach(ObserverInterface $observer): void { $this->observers[] = $observer; } public function detach(ObserverInterface $observer): void { $index = array_search($observer, $this->observers, true); if ($index !== false) { unset($this->observers[$index]); } } public function notify(): void { foreach ($this->observers as $observer) { $observer->update($this); } } public function setName(string $name): void { $this->name = $name; $this->notify(); } public function setEmail(string $email): void { $this->email = $email; $this->notify(); } public function getName(): string { return $this->name; } public function getEmail(): string { return $this->email; } } // Konkreter Beobachter class UserLogger implements ObserverInterface { public function update(SubjectInterface $subject): void { if ($subject instanceof User) { echo "User updated: {$subject->getName()} ({$subject->getEmail()})\n"; } } } ``` ### Strategy Das Strategy-Muster definiert eine Familie von Algorithmen, kapselt jeden einzelnen und macht sie austauschbar. Das Muster ermöglicht es, den Algorithmus unabhängig von den Clients zu variieren, die ihn verwenden. **Verwendung im Framework:** - Validierungsstrategien - Caching-Strategien - Authentifizierungsstrategien **Beispiel:** ```php // Strategieschnittstelle interface PaymentStrategyInterface { public function pay(float $amount): bool; } // Konkrete Strategien class CreditCardPaymentStrategy implements PaymentStrategyInterface { private string $cardNumber; private string $expiryDate; private string $cvv; public function __construct(string $cardNumber, string $expiryDate, string $cvv) { $this->cardNumber = $cardNumber; $this->expiryDate = $expiryDate; $this->cvv = $cvv; } public function pay(float $amount): bool { // Kreditkartenzahlung verarbeiten return true; } } class PayPalPaymentStrategy implements PaymentStrategyInterface { private string $email; private string $password; public function __construct(string $email, string $password) { $this->email = $email; $this->password = $password; } public function pay(float $amount): bool { // PayPal-Zahlung verarbeiten return true; } } // Kontext class PaymentProcessor { private PaymentStrategyInterface $paymentStrategy; public function setPaymentStrategy(PaymentStrategyInterface $paymentStrategy): void { $this->paymentStrategy = $paymentStrategy; } public function processPayment(float $amount): bool { return $this->paymentStrategy->pay($amount); } } ``` ### Template Method Das Template Method-Muster definiert das Skelett eines Algorithmus in einer Methode und verschiebt einige Schritte in Unterklassen. Das Muster ermöglicht es Unterklassen, bestimmte Schritte eines Algorithmus zu überschreiben, ohne die Struktur des Algorithmus zu ändern. **Verwendung im Framework:** - Controller-Basisklasse mit Hooks - Migrationsprozess - Testfälle **Beispiel:** ```php // Abstrakte Klasse mit Template-Methode abstract class Report { // Template-Methode public function generate(): string { $data = $this->fetchData(); $processedData = $this->processData($data); $report = $this->formatReport($processedData); $this->saveReport($report); return $report; } // Abstrakte Methoden, die von Unterklassen implementiert werden müssen abstract protected function fetchData(): array; abstract protected function formatReport(array $data): string; // Konkrete Methoden, die von Unterklassen überschrieben werden können protected function processData(array $data): array { // Standardverarbeitung return $data; } protected function saveReport(string $report): void { // Standardspeicherung file_put_contents('report.txt', $report); } } // Konkrete Unterklasse class SalesReport extends Report { protected function fetchData(): array { // Verkaufsdaten abrufen return [ ['product' => 'Product A', 'sales' => 100], ['product' => 'Product B', 'sales' => 200], ]; } protected function processData(array $data): array { // Spezielle Verarbeitung für Verkaufsdaten $processedData = []; $totalSales = 0; foreach ($data as $item) { $totalSales += $item['sales']; $processedData[] = $item; } $processedData[] = ['product' => 'Total', 'sales' => $totalSales]; return $processedData; } protected function formatReport(array $data): string { // Formatierung als CSV $report = "Product,Sales\n"; foreach ($data as $item) { $report .= "{$item['product']},{$item['sales']}\n"; } return $report; } } ``` ## Erzeugungsmuster ### Factory Method Das Factory Method-Muster definiert eine Schnittstelle zum Erstellen eines Objekts, lässt aber Unterklassen entscheiden, welche Klasse instanziiert werden soll. Das Muster ermöglicht es einer Klasse, die Instanziierung an Unterklassen zu delegieren. **Verwendung im Framework:** - Controller-Factory - Repository-Factory - Service-Factory **Beispiel:** ```php // Produktschnittstelle interface LoggerInterface { public function log(string $message): void; } // Konkrete Produkte class FileLogger implements LoggerInterface { private string $filePath; public function __construct(string $filePath) { $this->filePath = $filePath; } public function log(string $message): void { file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND); } } class DatabaseLogger implements LoggerInterface { private PDO $pdo; public function __construct(PDO $pdo) { $this->pdo = $pdo; } public function log(string $message): void { $stmt = $this->pdo->prepare('INSERT INTO logs (message) VALUES (:message)'); $stmt->execute(['message' => $message]); } } // Creator-Schnittstelle mit Factory-Methode interface LoggerFactoryInterface { public function createLogger(): LoggerInterface; } // Konkrete Creators class FileLoggerFactory implements LoggerFactoryInterface { private string $filePath; public function __construct(string $filePath) { $this->filePath = $filePath; } public function createLogger(): LoggerInterface { return new FileLogger($this->filePath); } } class DatabaseLoggerFactory implements LoggerFactoryInterface { private PDO $pdo; public function __construct(PDO $pdo) { $this->pdo = $pdo; } public function createLogger(): LoggerInterface { return new DatabaseLogger($this->pdo); } } ``` ### Abstract Factory Das Abstract Factory-Muster bietet eine Schnittstelle zum Erstellen von Familien verwandter oder abhängiger Objekte, ohne ihre konkreten Klassen zu spezifizieren. **Verwendung im Framework:** - UI-Komponenten-Factory - Datenbankabstraktions-Factory - Service-Factory für verschiedene Umgebungen **Beispiel:** ```php // Abstrakte Produktschnittstellen interface ButtonInterface { public function render(): string; } interface InputInterface { public function render(): string; } // Konkrete Produkte für Bootstrap class BootstrapButton implements ButtonInterface { private string $label; public function __construct(string $label) { $this->label = $label; } public function render(): string { return ''; } } class BootstrapInput implements InputInterface { private string $name; private string $placeholder; public function __construct(string $name, string $placeholder) { $this->name = $name; $this->placeholder = $placeholder; } public function render(): string { return ''; } } // Konkrete Produkte für Material Design class MaterialButton implements ButtonInterface { private string $label; public function __construct(string $label) { $this->label = $label; } public function render(): string { return ''; } } class MaterialInput implements InputInterface { private string $name; private string $placeholder; public function __construct(string $name, string $placeholder) { $this->name = $name; $this->placeholder = $placeholder; } public function render(): string { return '
    '; } } // Abstrakte Factory-Schnittstelle interface UIFactoryInterface { public function createButton(string $label): ButtonInterface; public function createInput(string $name, string $placeholder): InputInterface; } // Konkrete Factories class BootstrapUIFactory implements UIFactoryInterface { public function createButton(string $label): ButtonInterface { return new BootstrapButton($label); } public function createInput(string $name, string $placeholder): InputInterface { return new BootstrapInput($name, $placeholder); } } class MaterialUIFactory implements UIFactoryInterface { public function createButton(string $label): ButtonInterface { return new MaterialButton($label); } public function createInput(string $name, string $placeholder): InputInterface { return new MaterialInput($name, $placeholder); } } ``` ### Singleton Das Singleton-Muster stellt sicher, dass eine Klasse nur eine Instanz hat und bietet einen globalen Zugriffspunkt auf diese Instanz. **Verwendung im Framework:** - Konfigurationsmanager - Datenbankverbindung - Logger **Beispiel:** ```php class DatabaseConnection { private static ?DatabaseConnection $instance = null; private PDO $pdo; private function __construct(string $dsn, string $username, string $password) { $this->pdo = new PDO($dsn, $username, $password); } public static function getInstance(string $dsn, string $username, string $password): DatabaseConnection { if (self::$instance === null) { self::$instance = new self($dsn, $username, $password); } return self::$instance; } public function getConnection(): PDO { return $this->pdo; } // Verhindern des Klonens private function __clone() {} // Verhindern der Deserialisierung public function __wakeup() { throw new \Exception("Cannot unserialize a singleton."); } } ``` ### Builder Das Builder-Muster trennt die Konstruktion eines komplexen Objekts von seiner Repräsentation, sodass derselbe Konstruktionsprozess verschiedene Repräsentationen erstellen kann. **Verwendung im Framework:** - Query Builder - Form Builder - Response Builder **Beispiel:** ```php // Produkt class Query { private string $select = '*'; private string $from = ''; private array $where = []; private array $orderBy = []; private ?int $limit = null; private ?int $offset = null; public function setSelect(string $select): void { $this->select = $select; } public function setFrom(string $from): void { $this->from = $from; } public function addWhere(string $condition): void { $this->where[] = $condition; } public function addOrderBy(string $column, string $direction = 'ASC'): void { $this->orderBy[] = "$column $direction"; } public function setLimit(?int $limit): void { $this->limit = $limit; } public function setOffset(?int $offset): void { $this->offset = $offset; } public function getSql(): string { $sql = "SELECT {$this->select} FROM {$this->from}"; if (!empty($this->where)) { $sql .= " WHERE " . implode(' AND ', $this->where); } if (!empty($this->orderBy)) { $sql .= " ORDER BY " . implode(', ', $this->orderBy); } if ($this->limit !== null) { $sql .= " LIMIT {$this->limit}"; } if ($this->offset !== null) { $sql .= " OFFSET {$this->offset}"; } return $sql; } } // Builder-Schnittstelle interface QueryBuilderInterface { public function select(string $columns): QueryBuilderInterface; public function from(string $table): QueryBuilderInterface; public function where(string $condition): QueryBuilderInterface; public function orderBy(string $column, string $direction = 'ASC'): QueryBuilderInterface; public function limit(int $limit): QueryBuilderInterface; public function offset(int $offset): QueryBuilderInterface; public function getQuery(): Query; } // Konkreter Builder class QueryBuilder implements QueryBuilderInterface { private Query $query; public function __construct() { $this->query = new Query(); } public function select(string $columns): QueryBuilderInterface { $this->query->setSelect($columns); return $this; } public function from(string $table): QueryBuilderInterface { $this->query->setFrom($table); return $this; } public function where(string $condition): QueryBuilderInterface { $this->query->addWhere($condition); return $this; } public function orderBy(string $column, string $direction = 'ASC'): QueryBuilderInterface { $this->query->addOrderBy($column, $direction); return $this; } public function limit(int $limit): QueryBuilderInterface { $this->query->setLimit($limit); return $this; } public function offset(int $offset): QueryBuilderInterface { $this->query->setOffset($offset); return $this; } public function getQuery(): Query { return $this->query; } } // Director class QueryDirector { private QueryBuilderInterface $builder; public function __construct(QueryBuilderInterface $builder) { $this->builder = $builder; } public function buildUserQuery(): Query { return $this->builder ->select('id, name, email') ->from('users') ->where('active = 1') ->orderBy('name') ->limit(10) ->getQuery(); } public function buildPostQuery(int $userId): Query { return $this->builder ->select('id, title, content') ->from('posts') ->where("user_id = $userId") ->where('published = 1') ->orderBy('created_at', 'DESC') ->getQuery(); } } ``` ## Weitere Informationen - [Architekturübersicht](overview.md): Überblick über die Architektur des Frameworks. - [Komponenten](components.md): Detaillierte Informationen zu den Hauptkomponenten des Frameworks. - [Komponentendokumentation](../components/README.md): Dokumentation der einzelnen Komponenten.