Factory Pattern Beispiele
Factory Pattern: Objekte elegant erstellen
Das Factory Pattern kapselt die Objekterzeugung und macht Code flexibler. Lernen Sie Simple Factory, Factory Method und Abstract Factory.
Warum Factory Pattern?
| Problem | Lösung durch Factory |
|---|---|
| Komplexe Objekterzeugung | Zentralisierte Erstellungslogik |
| Viele Konstruktor-Parameter | Sprechende Factory-Methoden |
| Bedingte Objekterzeugung | Switch-Logik in Factory |
| Testbarkeit | Factory kann gemockt werden |
1. Simple Factory
// ❌ Ohne Factory - Überall verstreute Logik
class PaymentController {
public function process(string $type): void {
if ($type === 'creditcard') {
$processor = new CreditCardProcessor(
new PaymentGateway(),
new FraudDetection(),
new Logger()
);
} elseif ($type === 'paypal') {
$processor = new PayPalProcessor(
new PayPalAPI(),
new Logger()
);
} elseif ($type === 'bitcoin') {
$processor = new BitcoinProcessor(
new BlockchainAPI(),
new WalletService(),
new Logger()
);
}
// ...
}
}
// ✅ Mit Simple Factory
class PaymentProcessorFactory {
public function __construct(
private PaymentGateway $gateway,
private PayPalAPI $paypalApi,
private BlockchainAPI $blockchainApi,
private Logger $logger
) {}
public function create(string $type): PaymentProcessorInterface {
return match($type) {
'creditcard' => new CreditCardProcessor(
$this->gateway,
new FraudDetection(),
$this->logger
),
'paypal' => new PayPalProcessor(
$this->paypalApi,
$this->logger
),
'bitcoin' => new BitcoinProcessor(
$this->blockchainApi,
new WalletService(),
$this->logger
),
default => throw new InvalidArgumentException("Unknown type: {$type}")
};
}
}
// Verwendung
class PaymentController {
public function __construct(
private PaymentProcessorFactory $factory
) {}
public function process(string $type): void {
$processor = $this->factory->create($type);
$processor->process($payment);
}
}
2. Static Factory Method
class User {
private function __construct(
private string $name,
private string $email,
private string $role,
private bool $isVerified
) {}
// Factory Methods
public static function createAdmin(string $name, string $email): self {
return new self($name, $email, 'admin', true);
}
public static function createMember(string $name, string $email): self {
return new self($name, $email, 'member', false);
}
public static function createGuest(): self {
return new self('Guest', '', 'guest', false);
}
// Named Constructor für komplexe Erstellung
public static function fromArray(array $data): self {
return new self(
$data['name'] ?? throw new InvalidArgumentException('Name required'),
$data['email'] ?? throw new InvalidArgumentException('Email required'),
$data['role'] ?? 'member',
$data['verified'] ?? false
);
}
public static function fromJson(string $json): self {
$data = json_decode($json, true);
if (!$data) {
throw new InvalidArgumentException('Invalid JSON');
}
return self::fromArray($data);
}
}
// Verwendung - Klar und lesbar
$admin = User::createAdmin('Max', 'max@admin.de');
$member = User::createMember('Anna', 'anna@user.de');
$guest = User::createGuest();
$imported = User::fromJson('{"name":"Tom","email":"tom@test.de"}');
3. Factory Method Pattern
// Abstract Creator
abstract class NotificationSender {
// Factory Method - Subklassen entscheiden welche Notification
abstract protected function createNotification(string $message): Notification;
// Template Method nutzt die Factory
public function send(string $message, User $user): void {
$notification = $this->createNotification($message);
$notification->setRecipient($user);
$notification->send();
$this->logNotification($notification);
}
private function logNotification(Notification $notification): void {
// Logging...
}
}
// Concrete Creators
class EmailNotificationSender extends NotificationSender {
public function __construct(private Mailer $mailer) {}
protected function createNotification(string $message): Notification {
return new EmailNotification($message, $this->mailer);
}
}
class SMSNotificationSender extends NotificationSender {
public function __construct(private SMSGateway $gateway) {}
protected function createNotification(string $message): Notification {
return new SMSNotification($message, $this->gateway);
}
}
class PushNotificationSender extends NotificationSender {
public function __construct(private PushService $pushService) {}
protected function createNotification(string $message): Notification {
return new PushNotification($message, $this->pushService);
}
}
// Verwendung
class NotificationService {
public function notify(User $user, string $message, string $channel): void {
$sender = match($channel) {
'email' => new EmailNotificationSender($this->mailer),
'sms' => new SMSNotificationSender($this->smsGateway),
'push' => new PushNotificationSender($this->pushService),
default => throw new InvalidArgumentException()
};
$sender->send($message, $user);
}
}
4. Abstract Factory Pattern
// Abstract Factory - Erstellt Familien zusammengehöriger Objekte
interface UIFactory {
public function createButton(): Button;
public function createCheckbox(): Checkbox;
public function createTextField(): TextField;
}
// Concrete Factory für Light Theme
class LightThemeFactory implements UIFactory {
public function createButton(): Button {
return new LightButton();
}
public function createCheckbox(): Checkbox {
return new LightCheckbox();
}
public function createTextField(): TextField {
return new LightTextField();
}
}
// Concrete Factory für Dark Theme
class DarkThemeFactory implements UIFactory {
public function createButton(): Button {
return new DarkButton();
}
public function createCheckbox(): Checkbox {
return new DarkCheckbox();
}
public function createTextField(): TextField {
return new DarkTextField();
}
}
// Client nutzt nur die Abstraktion
class FormBuilder {
public function __construct(private UIFactory $factory) {}
public function buildLoginForm(): Form {
$form = new Form();
$form->add($this->factory->createTextField()
->setLabel('Username'));
$form->add($this->factory->createTextField()
->setLabel('Password')
->setType('password'));
$form->add($this->factory->createCheckbox()
->setLabel('Remember me'));
$form->add($this->factory->createButton()
->setText('Login'));
return $form;
}
}
// Verwendung - Theme einfach austauschbar
$factory = $user->prefersDarkMode()
? new DarkThemeFactory()
: new LightThemeFactory();
$builder = new FormBuilder($factory);
$loginForm = $builder->buildLoginForm();
JavaScript Factory Beispiele
// Simple Factory in JavaScript
class UserFactory {
static createAdmin(name, email) {
return {
name,
email,
role: 'admin',
permissions: ['read', 'write', 'delete', 'admin'],
createdAt: new Date()
};
}
static createMember(name, email) {
return {
name,
email,
role: 'member',
permissions: ['read'],
createdAt: new Date()
};
}
static createFromDTO(dto) {
const base = {
name: dto.name,
email: dto.email,
createdAt: new Date(dto.created_at || Date.now())
};
return dto.is_admin
? { ...base, role: 'admin', permissions: ['read', 'write', 'delete', 'admin'] }
: { ...base, role: 'member', permissions: ['read'] };
}
}
// Verwendung
const admin = UserFactory.createAdmin('Max', 'max@admin.de');
const member = UserFactory.createMember('Anna', 'anna@user.de');
const imported = UserFactory.createFromDTO(apiResponse);
// Factory mit Registrierung in JavaScript
class HandlerFactory {
static handlers = new Map();
static register(type, handler) {
this.handlers.set(type, handler);
}
static create(type, ...args) {
const Handler = this.handlers.get(type);
if (!Handler) {
throw new Error(`Unknown handler type: ${type}`);
}
return new Handler(...args);
}
}
// Handler registrieren
HandlerFactory.register('json', JSONHandler);
HandlerFactory.register('xml', XMLHandler);
HandlerFactory.register('csv', CSVHandler);
// Verwendung
const handler = HandlerFactory.create('json', options);
handler.parse(data);
// Erweiterbar ohne Factory-Code zu ändern
HandlerFactory.register('yaml', YAMLHandler);
Best Practices
// ✅ Factory für komplexe Objekterzeugung
class OrderFactory {
public function createFromCart(Cart $cart, User $user): Order {
$order = new Order();
$order->setUser($user);
$order->setItems($cart->getItems());
$order->setShippingAddress($user->getDefaultAddress());
$order->setTotalPrice($this->calculateTotal($cart));
$order->setStatus('pending');
$order->setCreatedAt(new DateTime());
return $order;
}
}
// ✅ Factory für Testdaten
class TestDataFactory {
public static function createUser(array $overrides = []): User {
return new User(
name: $overrides['name'] ?? 'Test User',
email: $overrides['email'] ?? 'test@example.com',
role: $overrides['role'] ?? 'member'
);
}
}
// In Tests
$user = TestDataFactory::createUser(['role' => 'admin']);
💡 Wann welche Factory?
Simple Factory: Wenn Sie verschiedene Objekte basierend auf einem Parameter erstellen müssen.
Factory Method: Wenn Subklassen entscheiden sollen, welche Objekte erstellt werden.
Abstract Factory: Wenn Sie Familien zusammengehöriger Objekte erstellen müssen.
Factory Method: Wenn Subklassen entscheiden sollen, welche Objekte erstellt werden.
Abstract Factory: Wenn Sie Familien zusammengehöriger Objekte erstellen müssen.