Observer Design Pattern in Delphi

A common side-effect of partitioning a system into a collection of co-operating classes, is the need to maintain consistency between related objects. You don't want to achieve consistency by making the classes tightly coupled, because that reduces their reusability

Delphi's events (which are actually method pointers) let you deal with this problem in a structured manner. Events let you decouple classes that need to co-operate. For example: The TButton.OnClick event is dispatched ‘to whom it may concern', the button does not store a (typed) reference to the class handling the event. In fact the event might not even be handled at all. In terms of the observer pattern the object dispatching an event is called subject, the object handling the event is called observer.

So Delphi's events take care of decoupling classes, but what if you want to handle an event in more than one place?

An observer pattern describes how to establish one-to-many notifications. A subject may have any number of observers. All observers are notified whenever the subject undergoes a change in state (such as a button being clicked). In response each observer may query the subject to synchronise its state with the subject's state.
This kind of interaction is also known as publish-subscribe, the subject is the publisher of notifications. It sends out these notifications without having to know who it's observers are. Any number of observers can subscribe to receive notifications.

Implementation

The implementation of the observer pattern is taking advantage of Delphi's events to deal with decoupling classes. The one-to-many aspect is implemented by registering and un-registering dedicated observers. The one-to-many mechanism is actually implemented by iterating over the list of observers.

Let's assume you've got a class TSubject which defines useful behaviour. The following code demonstrates the implementation of the observer pattern.

type 
  TSubject = class (TObject) 
  private 
    FObservers: TList; 
  public 
    procedure RegisterObserver(Observer: TSubjectObserver); 
    procedure UnregisterObserver(Observer: TSubjectObserver); 
  end; 

  TSubjectObserver = class (TComponent) 
  private 
    FEnabled: Boolean; 
  published 
    property Enabled: Boolean read FEnabled write FEnabled; default True; 
  end; 

In this interface: A registration mechanism has been added to the class TSubject, consisting of: FObservers: TList; which stores all registered observers. RegisterObserver, which registers an observer by adding it to FObservers. UnregisterObserver, which unregisters an observer by removing it from FObservers.

A new class Observer pattern has been created: TSubjectObserver. This class is a TComponent descendant. It has an Enabled property which allows you to switch the observer on and off rather than having to register/unregister it each time. How this property actually cooperates in the one-to-many event dispatch mechanism will be explained shortly.

The actual implementation of this pattern is:

procedure TSubject.RegisterObserver(Observer: TSubjectObserver); 
begin 
  if FObservers.IndexOf(Observer) = -1 then 
    FObservers.Add(Observer); 
end; 

procedure TSubject.UnregisterObserver(Observer: TSubjectObserver); 
begin 
  FObservers.Remove(Observer); 
end; 

As you see in the implementation: this deals only with the registration part of the observer pattern. Now you may ask: ‘where is my one-to-many notification'? Well, it's not possible to implement this as part of the pattern. The actual one-to-many notifications you have to implement yourself. Assume that TSubject has a method Change which notifies all it's registered observers of a change. The observers would have an OnChange event property which is actually dispatched. You could implement this like:

type 
  TSubject = class (TObject) 
  private 
    FObservers: TList; 
  protected 
    procedure Change;   //Call this method to dispatch change
  public 
    procedure RegisterObserver(Observer: TSubjectObserver); 
    procedure UnregisterObserver(Observer: TSubjectObserver); 
  end; 

  TSubjectObserver = class (TComponent) 
  private 
    FEnabled: Boolean; 
    FOnChange: TNotifyEvent; 
  protected 
    procedure Change; 
  published 
    property Enabled: Boolean read FEnabled write FEnabled; 
    property OnChange: TNotifyEvent read FOnChange write FOnChange; 
  end; 

implementation 

procedure TSubject.Change; 
var 
  Obs: TSubjectObserver; 
  I: Integer; 
begin 
  for I := 0 to FObservers.Count - 1 do 
  begin 
    Obs := FObservers[I]; 
    if Obs.Enabled then Obs.Change; 
  end; 
end; 

procedure TSubject.RegisterObserver(Observer: TSubjectObserver); 
begin 
  if FObservers.IndexOf(Observer) = -1 then 
    FObservers.Add(Observer); 
end; 

procedure TSubject.UnregisterObserver(Observer: TSubjectObserver); 
begin 
  FObservers.Remove(Observer); 
end; 

procedure TSubjectObserver.Change; 
begin 
  if Assigned(FOnChange) then FOnChange(Self); 
end;

In this example notice: the method TSubject.Change which iterates the registered observers, calling each observer's Change method. This is the actual one-to-many notification. the observer's Enabled property which is checked to determine whether the observer should be notified; the event OnChange in the class TSubjectObserver which can be wired using the object inspector.

Code examples