In the previous article, we saw how TComponent initializes the observer engine. However, simply having an engine doesn't mean a component knows how to use it. To manage this, Delphi uses a "Gatekeeper" pattern consisting of two key virtual methods.
By default, a component is a closed door. If you attempt to attach an observer to a generic TComponent, it will fail. This is because the default implementation of the check-method is strictly pessimistic:
TComponent
function TComponent.CanObserve(const ID: Integer): Boolean; begin Result := False; end; procedure TComponent.ObserverAdded(const ID: Integer; const Observer: IObserver); begin // Default: do nothing end;
This design ensures that the observer engine doesn't waste resources or cause unexpected side effects by attaching logic to components that aren't specifically programmed to handle it.
When a developer wants to make a component "observable," they must override CanObserve. This method acts as a capability negotiation. It uses specific IDs (defined in TObserverMapping) to tell the engine what interfaces it supports.
TObserverMapping
Common IDs include:
EditLinkID (1)
ControlValueID (4)
PositionLinkID (3)
If CanObserve returns True, the engine proceeds to add the observer and then calls ObserverAdded. This is the component's opportunity to perform a "handshake"—setting up specific event handlers or internal state changes that should only exist when an observer is active.
CanObserve
True
How does the component know which logic to apply? Delphi uses the TObserverMapping class to manage these IDs. It provides a standardized way to request specific "behaviors" from a component without needing to know the component's concrete class type.
// Typical usage in a component override function TMyCustomControl.CanObserve(const ID: Integer): Boolean; begin Result := (ID = TObserverMapping.EditLinkID) or (ID = TObserverMapping.ControlValueID); end;
By splitting the logic this way, Delphi keeps the core TObservers engine generic and fast, while allowing every individual component to define its own unique rules for interaction.
TObservers
In Part 4: We look at a concrete example of this in action within TCustomEdit, and how it uses the ObserverToggle event to save and restore UI state.
TCustomEdit
ObserverToggle
Continued discussion of undocumented Delphi 8 Property Access Specifiers, and other ways of adding and removing delegates / events handlers, including clearing the list of all the delegates / event handlers.
This article discusses the new Delphi 8 property access specifiers.
A method pointer is now the same as a global procedure, ie, procedure of object = procedure.