Mega Code Archive

 
Categories / Delphi / Examples
 

Simple Observer pattern

Title: Simple Observer pattern Question: How to make objects aware of changes in the state of another object, without coupling the changing object with the observing object. Answer: To make, for example a form, avare of changes in another object we can add a notify event to that object and implement some method to it. But in that case only one observer can exist for that class. If more than one class has to get notified of changes in the class, we have to implement some kind of list with events. This is best implemented with an Observer-pattern. And here we have our little version of the pattern :). The unit Observer_un consists of two classes TObserverItem wich is the container for one "observation" event TObserverMgr Maintains a list of TObserverItem and has a method for running registered observers For clarity we call the classes thats going to use these for observation TObserver : The class that want to get notified of changes TChanger : The class that changes To use this you start with the class you want others to be able to observe (TChanger), and implement a private field for TObserverMgr. Then to avoid need for coupling between observer and TObserverMgr you implement two methods, AddObserver and RemoveObserver in the changer which just forwards the methods to TObserverMgr. procedure TChanger.AddObserver(aNotifyEvent: TNotifyEvent; Observer: TObject); begin // Forwarding method call, Observer should not need to know about fObserverMgr fObserverMgr.AddObserver(aNotifyEvent, Observer); end; Now if you want to observe the field FMyString in changer you implement a property MyString with a set method Could look like this procedure TChanger.SetMyStr( value : String ) begin FMyString := Value ; // And now we want to call all the observers FObserverMgr.RunObservers ; end; Ok its esier for you to understand the source so look below or in Observer.zip which has an example also. /Christer & Fredrik unit Observers_un; interface uses contnrs, classes; type {{ Item for holding an observer and one of its TNotifyEvents. } TObserverItem = class private FObserved: TObject; FObserver: TObject; FOnChange: TNotifyEvent; protected constructor Create(theObserved: TObject); public procedure Change; property Observer: TObject read FObserver write FObserver; property OnChange: TNotifyEvent read FOnChange write FOnChange; end; {{ Class for managing ObserverItems. Holds methods for inserting deleting and running ObserverItems. } TObserverMgr = class (TObject) private FObserved: TObject; FObserverList: TObjectList; function FindMatch(aNotifyEvent: TNotifyEvent; Observer: TObject): Integer; public constructor Create(Owner : TObject); procedure AddObserver(aNotifyEvent : TNotifyEvent ; Observer : TObject); procedure RemoveObserver(aNotifyEvent : TNotifyEvent ; Observer : TObject); procedure RunObservers; end; implementation { ****************************************** TObserverItem ******************************************* } constructor TObserverItem.Create(theObserved: TObject); begin inherited Create ; FObserved := theObserved ; end; procedure TObserverItem.Change; begin if Assigned(FOnChange) then FOnChange( FObserved ) ; end; { ******************************************* TObserverMgr ******************************************* } constructor TObserverMgr.Create(Owner : TObject); begin inherited Create ; FObserved := Owner ; FObserverList := TObjectList.Create; FObserverList.OwnsObjects := true; end; procedure TObserverMgr.AddObserver(aNotifyEvent : TNotifyEvent ; Observer : TObject); var anItem: TObserverItem; begin if (0 FindMatch(aNotifyEvent, Observer)) then begin anItem := TObserverItem.Create(FObserved) ; anItem.OnChange := aNotifyEvent ; anItem.Observer := Observer ; FObserverList.Add(anItem) ; end ; end; function TObserverMgr.FindMatch(aNotifyEvent: TNotifyEvent; Observer: TObject): Integer; var I: Integer; anItem: TObserverItem; // Check if this Observer and notify event exists in FObserverList // Result is -1 if not present, else it's the index in FObserverList // // If aNotifyEvent is Nil then the first item with the same observer will // return it's index // begin result := -1 ; For I := 0 to FObserverList.count-1 do begin anItem := TObserverItem( FObserverList.Items[i] ) ; if anItem.Observer = Observer then begin if @aNotifyEvent = nil then begin result := I ; exit ; end else if (@anItem.OnChange = @aNotifyEvent) then begin result := I ; exit ; end ; end ; end ; end; procedure TObserverMgr.RemoveObserver(aNotifyEvent : TNotifyEvent ; Observer : TObject); var I: Integer; begin // If aNotifyEvent is Nil then we loop through the list // and removes all items with this observer repeat I := FindMatch(aNotifyEvent, Observer) ; if (I = 0) then FObserverList.Delete(I) ; until (I end; procedure TObserverMgr.RunObservers; var I: Integer; anObserver: TObserverItem; begin For I := 0 to FObserverList.Count - 1 do begin anObserver := TObserverItem( FObserverList.items[i] ); anObserver.Change ; end ; end; end.