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

241 Dokumentationen verfügbar

Wissensdatenbank

Observer Pattern Events

Zuletzt aktualisiert: 20.01.2026 um 11:26 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

Enjix Beta

Enjyn AI Agent

Hallo 👋 Ich bin Enjix — wie kann ich dir helfen?
120