Table of Contents

Observer Pattern API Reference

Complete API documentation for the Observer pattern in PatternKit.

Namespace

using PatternKit.Behavioral.Observer;

Observer<TEvent>

Thread-safe event hub for broadcasting typed events.

public sealed class Observer<TEvent>

Type Parameters

Parameter Description
TEvent The event type to broadcast

Delegates

Handler

public delegate void Handler(in TEvent @event);

Processes a published event.

Filter

public delegate bool Filter(in TEvent @event);

Determines if a handler should receive an event.

ErrorSink

public delegate void ErrorSink(Exception ex, in TEvent @event);

Handles errors from failed handlers.

Methods

Method Returns Description
Publish(in TEvent @event) void Broadcasts event to all matching subscribers
Subscribe(Handler handler) IDisposable Subscribes to all events
Subscribe(Filter filter, Handler handler) IDisposable Subscribes with predicate filter

Static Methods

Method Returns Description
Create() Builder Creates a new fluent builder

Example

var hub = Observer<OrderEvent>.Create()
    .OnError((ex, in e) => Log.Error(ex, "Handler failed for {OrderId}", e.OrderId))
    .ThrowAggregate()
    .Build();

// Subscribe to all events
var sub1 = hub.Subscribe((in OrderEvent e) => ProcessOrder(e));

// Subscribe with filter
var sub2 = hub.Subscribe(
    (in OrderEvent e) => e.Type == OrderEventType.Urgent,
    (in OrderEvent e) => HandleUrgent(e));

// Publish
hub.Publish(new OrderEvent { OrderId = "123", Type = OrderEventType.Urgent });

// Unsubscribe
sub1.Dispose();
sub2.Dispose();

Observer<TEvent>.Builder

Fluent builder for configuring the observer.

public sealed class Builder

Methods

Method Returns Description
OnError(ErrorSink sink) Builder Sets error handler (optional)
ThrowAggregate() Builder Collects all exceptions, throws AggregateException
ThrowFirstError() Builder Throws immediately on first exception
SwallowErrors() Builder Never throws, errors go to sink only
Build() Observer<TEvent> Builds the immutable observer

Error Policy Details

Policy Handler Continuation Exception Behavior
ThrowAggregate All run Single AggregateException at end
ThrowFirstError Stops on first Immediate throw
SwallowErrors All run No throw, sink receives errors

Example

// Aggregate errors (default)
var hub1 = Observer<Event>.Create()
    .OnError((ex, in e) => Log.Warn(ex.Message))
    .ThrowAggregate()
    .Build();

// Fail fast
var hub2 = Observer<Event>.Create()
    .ThrowFirstError()
    .Build();

// Never throw
var hub3 = Observer<Event>.Create()
    .OnError((ex, in e) => Log.Error(ex.Message))
    .SwallowErrors()
    .Build();

Subscription Management

Subscriptions return IDisposable. Disposing is idempotent:

var sub = hub.Subscribe((in Event e) => Handle(e));

// Unsubscribe
sub.Dispose();
sub.Dispose(); // Safe to call multiple times

Subscription Lifetime

  • Subscriptions are held by the hub until disposed
  • Disposing during Publish affects subsequent publishes only
  • Re-subscribing creates a new subscription

Thread Safety

Component Thread-Safe
Builder No - use from single thread
Observer<TEvent> Yes - immutable after build
Subscribe Yes - atomic
Publish Yes - reads snapshot
Dispose (subscription) Yes - atomic

Implementation Notes

  • Copy-on-write subscriptions: publishing is contention-free
  • Subscribe/unsubscribe perform atomic array swap
  • Handlers invoked in registration order

Complete Example

using PatternKit.Behavioral.Observer;

// Define event type
public record StockPriceChanged(string Symbol, decimal Price, DateTime Timestamp);

// Create hub
var hub = Observer<StockPriceChanged>.Create()
    .OnError((ex, in e) => Console.Error.WriteLine($"Error handling {e.Symbol}: {ex.Message}"))
    .ThrowAggregate()
    .Build();

// UI subscriber - all updates
var uiSub = hub.Subscribe((in StockPriceChanged e) =>
    Console.WriteLine($"[UI] {e.Symbol}: ${e.Price}"));

// Alert subscriber - large movements only
var alertSub = hub.Subscribe(
    (in StockPriceChanged e) => e.Price > 100,
    (in StockPriceChanged e) => Console.WriteLine($"[ALERT] {e.Symbol} exceeded $100!"));

// Logging subscriber
var logSub = hub.Subscribe((in StockPriceChanged e) =>
    File.AppendAllText("prices.log", $"{e.Timestamp}: {e.Symbol} = {e.Price}\n"));

// Publish events
hub.Publish(new StockPriceChanged("AAPL", 150.50m, DateTime.UtcNow));
hub.Publish(new StockPriceChanged("GOOG", 95.00m, DateTime.UtcNow));

// Cleanup
uiSub.Dispose();
alertSub.Dispose();
logSub.Dispose();

See Also