Observer Pattern und Event-Systeme | Enjyn Gruppe
Hallo Welt
→
→
Hallo Welt
Original Lingva Deutsch
Übersetzung wird vorbereitet...
Dieser Vorgang kann bis zu 60 Sekunden dauern.
Diese Seite wird erstmalig übersetzt und dann für alle Besucher gespeichert.
0%
DE Zurück zu Deutsch
Übersetzung durch Lingva Translate

330 Dokumentationen verfügbar

Wissensdatenbank

Observer Pattern Events

Zuletzt aktualisiert: 14.07.2026 um 12:29 Uhr

Observer Pattern und Event-Systeme

Das Observer Pattern ermöglicht lose Kopplung durch ein Publish-Subscribe-Modell. Lernen Sie die Implementierung und moderne Event-Systeme.

Das Pattern verstehen

┌─────────────────────────────────────────────────────────────┐
│                     OBSERVER PATTERN                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌─────────────┐         notify()        ┌─────────────┐  │
│   │   Subject   │ ───────────────────────►│  Observer A │  │
│   │  (Publisher)│                         └─────────────┘  │
│   │             │         notify()        ┌─────────────┐  │
│   │  - observers│ ───────────────────────►│  Observer B │  │
│   │  - state    │                         └─────────────┘  │
│   │             │         notify()        ┌─────────────┐  │
│   │  attach()   │ ───────────────────────►│  Observer C │  │
│   │  detach()   │                         └─────────────┘  │
│   │  notify()   │                                          │
│   └─────────────┘                                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Klassische Implementierung (PHP)

// Observer Interface
interface Observer {
    public function update(Subject $subject): void;
}

// Subject Interface
interface Subject {
    public function attach(Observer $observer): void;
    public function detach(Observer $observer): void;
    public function notify(): void;
}

// Konkretes Subject
class User implements Subject {
    private array $observers = [];
    private string $email;
    private string $status = 'pending';

    public function attach(Observer $observer): void {
        $this->observers[] = $observer;
    }

    public function detach(Observer $observer): void {
        $this->observers = array_filter(
            $this->observers,
            fn($o) => $o !== $observer
        );
    }

    public function notify(): void {
        foreach ($this->observers as $observer) {
            $observer->update($this);
        }
    }

    public function setStatus(string $status): void {
        $this->status = $status;
        $this->notify();  // Alle Observer benachrichtigen
    }

    public function getStatus(): string {
        return $this->status;
    }

    public function getEmail(): string {
        return $this->email;
    }
}

// Konkrete Observer
class EmailNotificationObserver implements Observer {
    public function update(Subject $subject): void {
        if ($subject instanceof User && $subject->getStatus() === 'active') {
            $this->sendWelcomeEmail($subject->getEmail());
        }
    }

    private function sendWelcomeEmail(string $email): void {
        echo "Sending welcome email to {$email}\n";
    }
}

class LoggingObserver implements Observer {
    public function update(Subject $subject): void {
        if ($subject instanceof User) {
            echo "User status changed to: {$subject->getStatus()}\n";
        }
    }
}

class AnalyticsObserver implements Observer {
    public function update(Subject $subject): void {
        if ($subject instanceof User) {
            echo "Tracking user status change in analytics\n";
        }
    }
}

// Verwendung
$user = new User();
$user->attach(new EmailNotificationObserver());
$user->attach(new LoggingObserver());
$user->attach(new AnalyticsObserver());

$user->setStatus('active');  // Alle Observer werden benachrichtigt

Modernes Event-System (PHP)

// Event Klasse
class Event {
    private bool $propagationStopped = false;

    public function stopPropagation(): void {
        $this->propagationStopped = true;
    }

    public function isPropagationStopped(): bool {
        return $this->propagationStopped;
    }
}

// Konkrete Events
class UserRegisteredEvent extends Event {
    public function __construct(
        public readonly User $user,
        public readonly DateTime $registeredAt
    ) {}
}

class OrderPlacedEvent extends Event {
    public function __construct(
        public readonly Order $order,
        public readonly User $user
    ) {}
}

// Event Dispatcher
class EventDispatcher {
    private array $listeners = [];

    public function addListener(string $eventClass, callable $listener, int $priority = 0): void {
        $this->listeners[$eventClass][$priority][] = $listener;
    }

    public function dispatch(Event $event): Event {
        $eventClass = get_class($event);

        if (!isset($this->listeners[$eventClass])) {
            return $event;
        }

        // Nach Priorität sortieren (höher = früher)
        krsort($this->listeners[$eventClass]);

        foreach ($this->listeners[$eventClass] as $priority => $listeners) {
            foreach ($listeners as $listener) {
                if ($event->isPropagationStopped()) {
                    return $event;
                }
                $listener($event);
            }
        }

        return $event;
    }
}

// Event Subscriber (gruppiert mehrere Listener)
interface EventSubscriberInterface {
    public static function getSubscribedEvents(): array;
}

class UserEventSubscriber implements EventSubscriberInterface {
    public static function getSubscribedEvents(): array {
        return [
            UserRegisteredEvent::class => [
                ['onUserRegistered', 10],
                ['sendWelcomeEmail', 0],
            ],
        ];
    }

    public function onUserRegistered(UserRegisteredEvent $event): void {
        echo "User {$event->user->getName()} registered\n";
    }

    public function sendWelcomeEmail(UserRegisteredEvent $event): void {
        echo "Sending welcome email to {$event->user->getEmail()}\n";
    }
}

// Verwendung
$dispatcher = new EventDispatcher();

// Einzelne Listener
$dispatcher->addListener(
    UserRegisteredEvent::class,
    fn($e) => echo "Analytics: New user\n",
    5
);

// Subscriber registrieren
$subscriber = new UserEventSubscriber();
foreach ($subscriber::getSubscribedEvents() as $eventClass => $listeners) {
    foreach ($listeners as [$method, $priority]) {
        $dispatcher->addListener($eventClass, [$subscriber, $method], $priority);
    }
}

// Event auslösen
$event = new UserRegisteredEvent($user, new DateTime());
$dispatcher->dispatch($event);

JavaScript Event System

// Einfacher Event Emitter
class EventEmitter {
    constructor() {
        this.listeners = new Map();
    }

    on(event, callback) {
        if (!this.listeners.has(event)) {
            this.listeners.set(event, []);
        }
        this.listeners.get(event).push(callback);
        return this; // Für Chaining
    }

    off(event, callback) {
        if (!this.listeners.has(event)) return this;

        const callbacks = this.listeners.get(event);
        const index = callbacks.indexOf(callback);
        if (index > -1) {
            callbacks.splice(index, 1);
        }
        return this;
    }

    once(event, callback) {
        const wrapper = (...args) => {
            callback(...args);
            this.off(event, wrapper);
        };
        return this.on(event, wrapper);
    }

    emit(event, ...args) {
        if (!this.listeners.has(event)) return false;

        this.listeners.get(event).forEach(callback => {
            callback(...args);
        });
        return true;
    }
}

// Verwendung
const emitter = new EventEmitter();

emitter.on('user:created', (user) => {
    console.log(`User created: ${user.name}`);
});

emitter.on('user:created', (user) => {
    sendWelcomeEmail(user.email);
});

emitter.once('user:created', (user) => {
    console.log('This only runs once');
});

// Event auslösen
emitter.emit('user:created', { name: 'Max', email: 'max@test.de' });
// Typisierter Event Emitter (TypeScript-Style mit JSDoc)
/**
 * @template {Record} Events
 */
class TypedEventEmitter {
    /** @type {Map} */
    #listeners = new Map();

    /**
     * @template {keyof Events} K
     * @param {K} event
     * @param {(...args: Events[K]) => void} callback
     */
    on(event, callback) {
        if (!this.#listeners.has(event)) {
            this.#listeners.set(event, []);
        }
        this.#listeners.get(event).push(callback);
        return this;
    }

    /**
     * @template {keyof Events} K
     * @param {K} event
     * @param {Events[K]} args
     */
    emit(event, ...args) {
        this.#listeners.get(event)?.forEach(cb => cb(...args));
    }
}

// Verwendung mit definiertem Event-Schema
/** @typedef {{ 'user:login': [User], 'user:logout': [string], 'error': [Error] }} AppEvents */
/** @type {TypedEventEmitter} */
const events = new TypedEventEmitter();

events.on('user:login', (user) => {
    // user ist typisiert
});

events.emit('user:login', currentUser);

DOM Events (Browser)

// Custom Events im Browser
class UserComponent extends HTMLElement {
    connectedCallback() {
        this.innerHTML = ``;
        this.querySelector('button').addEventListener('click', () => {
            this.registerUser();
        });
    }

    registerUser() {
        // ... registration logic ...

        // Custom Event dispatchen
        const event = new CustomEvent('user:registered', {
            bubbles: true,    // Event steigt im DOM auf
            composed: true,   // Durchdringt Shadow DOM
            detail: {
                user: { name: 'Max', email: 'max@test.de' }
            }
        });

        this.dispatchEvent(event);
    }
}

customElements.define('user-component', UserComponent);

// Event global abfangen
document.addEventListener('user:registered', (e) => {
    console.log('User registered:', e.detail.user);
});

Praktische Anwendungsfälle

// 1. Entkopplung von Modulen
class OrderService {
    constructor(private eventDispatcher: EventDispatcher) {}

    placeOrder(order: Order): void {
        // Kernlogik
        order.status = 'placed';
        this.orderRepository.save(order);

        // Andere Module werden via Event benachrichtigt
        this.eventDispatcher.dispatch(new OrderPlacedEvent(order));
        // Inventory, Shipping, Email, Analytics reagieren unabhängig
    }
}

// 2. Audit Logging
dispatcher.addListener('*', (event) => {
    auditLogger.log({
        event: event.constructor.name,
        timestamp: new Date(),
        data: event
    });
});

// 3. Caching invalidieren
dispatcher.addListener(ProductUpdatedEvent::class, (event) => {
    cache.invalidate(`product:${event.product.id}`);
    cache.invalidate('product:list');
});

// 4. Real-time Updates (WebSockets)
dispatcher.addListener(MessageSentEvent::class, (event) => {
    websocket.broadcast('chat:' + event.roomId, {
        type: 'new_message',
        message: event.message
    });
});
💡 Best Practices: 1. Events sollten immutable sein (readonly properties)
2. Vermeiden Sie zu viele Events (Performance)
3. Dokumentieren Sie alle Events
4. Nutzen Sie typisierte Events wo möglich
5. Events für asynchrone/optionale Aktionen, direkte Aufrufe für Pflichtaktionen

Weitere Informationen

Texterstellung mit KI-Unterstützung

Enjix Beta

Enjyn AI Agent

Hallo 👋 Ich bin Enjix — wie kann ich dir helfen?
LLMs können Fehler machen. Ihre Nachrichten werden an Google Gemini weitergeleitet.
120