 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Ivo Bauer Guest
|
Posted: Mon Jan 08, 2007 4:25 pm Post subject: How to extend observer pattern? |
|
|
Hi!
I've got an object that exposes a couple of events. As with many other
objects, not necessarily all events have the same prototypes. I'd like
to be able to make multiple objects observe the events of that object.
With the observer pattern, I can make multiple objects observe just
one event of the object which is being observed. Of course I could
create one separate observer for each event but this doesn't look good
to me. Is there a way how to customize/extend the observer pattern to
be able to register/unregister for observing multiple object events at
once?
Thanks in advance.
--
Ivo Bauer [OZM Research] |
|
| Back to top |
|
 |
Joanna Carter [TeamB] Guest
|
Posted: Mon Jan 08, 2007 5:04 pm Post subject: Re: How to extend observer pattern? |
|
|
"Ivo Bauer" <abuer (AT) zom (DOT) zc> a écrit dans le message de news:
45a21bfa (AT) newsgroups (DOT) borland.com...
| I've got an object that exposes a couple of events. As with many other
| objects, not necessarily all events have the same prototypes. I'd like
| to be able to make multiple objects observe the events of that object.
| With the observer pattern, I can make multiple objects observe just
| one event of the object which is being observed. Of course I could
| create one separate observer for each event but this doesn't look good
| to me. Is there a way how to customize/extend the observer pattern to
| be able to register/unregister for observing multiple object events at
| once?
What I do is to use a concept which has been called Aspects, but it is also
known as a Parameter Object. In .NET, all events are multicast (a type of
the observer pattern) and the method prototypes for the event all derive
from the EventHandler delegate (method pointer) type. The base argument
object class and the delegate are declared like this :
public class EventArgs
{
public static readonly EventArgs Empty;
static MyArgs()
{
Empty = new EventArgs();
}
public EventArgs() : base() { }
}
public delegate void EventHandler(object sender, EventArgs args);
This the allows you to derive all your event argument objects from EventArgs
and pass them to the same pattern of event handler. So you get a Parameter
Object like this :
public enum SelectionType
{
Empty,
Single,
Multiple
}
public class SelectionChangedEventArgs : EventArgs
{
private SelectionType selectionType;
public SelectionChangedArgs(SelectionType selectionType) : base()
{
this.selectionType = selectionType;
}
public SelectionType SelectionType
{
get { return selectionType; }
}
}
Consequently, the Observer pattern needs to change slightly :
public interface ISubject
{
void Attach(IObserver observer);
void Detach(IObserver observer);
void Notify();
}
public interface IObserver
{
void Update(object sender, EventArgs args);
}
Then, if you want to, you can derive from IObserver to give you:
public interface ISelectionObserver : IObserver
{
void Update(object sender, SelectionChangedEventArgs args);
}
And your observing class class can implement multiple I...Observer
interfaces, or each of several observing classes can implement one
interface.
Finally, your subject class can choose which observers to broadcast to by
checking for the derived IObserver interface type and only sending messages
to that/those observer(s).
I'm sorry it's in C#, I find it easier to think in that language at the
moment, but does that help ?
Joanna
--
Joanna Carter [TeamB]
Consultant Software Engineer |
|
| Back to top |
|
 |
Joanna Carter [TeamB] Guest
|
Posted: Mon Jan 08, 2007 5:18 pm Post subject: Re: How to extend observer pattern? |
|
|
"Joanna Carter [TeamB]" <joanna (AT) not (DOT) for.spam> a écrit dans le message de
news: 45a22552$1 (AT) newsgroups (DOT) borland.com...
Oops ! Errata...
| public class EventArgs
| {
| public static readonly EventArgs Empty;
|
***** static EventArgs() static constructor
| {
| Empty = new EventArgs();
| }
|
| public EventArgs() : base() { } // default instance constructor
| }
Joanna
--
Joanna Carter [TeamB]
Consultant Software Engineer |
|
| Back to top |
|
 |
Joao Morais Guest
|
Posted: Mon Jan 08, 2007 5:33 pm Post subject: Re: How to extend observer pattern? |
|
|
Ivo Bauer wrote:
| Quote: | I've got an object that exposes a couple of events. As with many other
objects, not necessarily all events have the same prototypes. I'd like
to be able to make multiple objects observe the events of that object.
With the observer pattern, I can make multiple objects observe just one
event of the object which is being observed. Of course I could create
one separate observer for each event but this doesn't look good to me.
Is there a way how to customize/extend the observer pattern to be able
to register/unregister for observing multiple object events at once?
|
I follow a pattern that sounds like the publish-subscriber:
== Unit 1 - event declaration
TSomeEvent1 = class(TEvent)
end;
TSomeEvent2 = class(TEvent)
end;
== Unit 2 - the observer
TObserverObject = class(TObject)
private
FNotifier1: TNotifier;
FNotifier2: TNotifier;
procedure Notify1(AEvent: TEvent);
procedure Notify2(AEvent: TEvent);
end;
....
FNotifier1 := TNotifier.Create(Notify1);
FNotifier2 := TNotifier.Create(Notify2);
// One event from one observed object
FNotifier1.AddNotificationItem(ObservedObjInstance, [TSomeEvent1]);
// Two events from any object
FNotifier2.AddNotificationItem(nil, [TSomeEvent1, TSomeEvent2]);
....
procedure TObserverObject.Notify1(AEvent: TEvent);
begin
if AEvent is TSomeEvent1 then
...
else
...
end;
....
procedure TObserverObject.Notify2(AEvent: TEvent);
begin
if AEvent is TSomeEvent1 then
if AEvent.Owner = SomeObj then
...
else
...
else
...
end;
== Unit 3 - the observed
....
TSomeEvent1.Create(Self).Notify;
....
TSomeEvent2.Create(Self).QueueNotification;
HTH.
--
Joao Morais |
|
| Back to top |
|
 |
Ivo Bauer Guest
|
Posted: Tue Jan 09, 2007 3:11 pm Post subject: Re: How to extend observer pattern? |
|
|
Hi, Joanna!
Joanna Carter [TeamB] napsal(a):
| Quote: | What I do is to use a concept which has been called Aspects, but it is also
known as a Parameter Object. In .NET, all events are multicast (a type of
the observer pattern) and the method prototypes for the event all derive
from the EventHandler delegate (method pointer) type.
|
[snipped a lot of good suggestions/insights]
| Quote: | I'm sorry it's in C#, I find it easier to think in that language at the
moment, but does that help ?
|
I appreciate your assistance. It certainly helped me a lot despite the
fact I work in Delphi/Win32 and use interfaces only when it's
unavoidable. Thanks a lot!
I'll show you some example how I customized the observer pattern while
taking your suggestions into account and would be happy to read your
(and anybody else's) comments.
Following is the declaration of simple TTransceiver class that exposes
3 events that notify class user that a message (whatever it is) has
been transmitted, received or discarded:
{==============================================================================}
type
{ TTransceiver }
TTransceiver = class;
TTransceiverMessageNotifyEvent = procedure (
aSender: TTransceiver;
const aMessage: string);
TTransceiver = class
private
fOnMessageDiscard,
fOnMessageReceive,
fOnMessageTransmit: TTransceiverMessageNotifyEvent;
protected
procedure DoMessageDiscard(const aMessage: string);
procedure DoMessageReceive(const aMessage: string);
procedure DoMessageTransmit(const aMessage: string);
public
property OnMessageDiscard: TTransceiverMessageNotifyEvent
read fOnMessageDiscard write fOnMessageDiscard;
property OnMessageReceive: TTransceiverMessageNotifyEvent
read fOnMessageReceive write fOnMessageReceive;
property OnMessageTransmit: TTransceiverMessageNotifyEvent
read fOnMessageTransmit write fOnMessageTransmit;
end;
Since the above 3 events are closely related and share the same
prototype, I'd like to be able to observe them all with single
observer. Now I'm going to show the basics of the "extended" observer
pattern:
{ TSubject }
type
TEventArgs = class
{ empty }
end;
TBaseObserver = class
public
procedure Update(aController: TObject; aEventArgs: TEventArgs);
virtual; abstract;
end;
TSubject = class
private
fController: TObject;
fObservers: TpdxObjectList;
protected
function IsSuitableObserver(aEventArgs: TEventArgs; aObserver:
TBaseObserver): Boolean; virtual;
public
constructor Create(aController: TObject);
destructor Destroy; override;
procedure NotifyObservers(aEventArgs: TEventArgs);
procedure RegisterObserver(aObserver: TBaseObserver);
procedure UnregisterObserver(aObserver: TBaseObserver);
end;
There's nothing special in the TSubject implementation, just that it
uses IsSuitableObserver method to determine whether or not an observer
of a particular kind should be sent notification:
procedure TSubject.NotifyObservers(aEventArgs: TEventArgs);
var
I: Integer;
vObserver: TBaseObserver;
begin
if Assigned(fObservers) then
for I := 0 to Pred(fObservers.Count) do
begin
vObserver := TBaseObserver(fObservers[I]);
if IsSuitableObserver(aEventArgs, vObserver) then
vObserver.Update(fController, aEventArgs);
end;
end;
TSubject.IsSuitableObserver is a virtual method that returns True by
default. Concrete TSubject descendants are advised to override this
method to decide about validity of a particular observer.
Now I'm going to define a revised TTransceiverEx class that
incorporates the concept of observer pattern (this is the class being
observed):
{ TTransceiverEx }
type
TMessageOperation = (
moTransmit,
moReceive,
moDiscard
);
TMessageOperationArgs = class(TEventArgs)
private
fMessage: string;
fOperation: TMessageOperation;
public
constructor Create(const aMessage: string; aOperation:
TMessageOperation);
property Message: string read fMessage;
property Operation: TMessageOperation read fOperation;
end;
TMessageOperationArgs basically encapsulates the original 3 events
defined by TTransceiver class. The new TTransceiver class will
construct the instance of this class when needed while passing
corresponding aMessage and aOperation parameters into the constructor:
constructor TMessageOperationArgs.Create(const aMessage: string;
aOperation: TMessageOperation);
begin
fMessage := aMessage;
fOperation := aOperation;
end;
TBaseMessageOperationObserver is just an abstract observer that is
used for observing message operations performed by TTransceiverEx class:
type
TBaseMessageOperationObserver = class(TBaseObserver)
public
procedure Update(aController: TObject; aEventArgs: TEventArgs);
overload; override;
procedure Update(aController: TObject; aEventArgs:
TMessageOperationArgs); reintroduce; overload; virtual; abstract;
end;
This class introduces overloaded Update method that declares
aEventArgs as a parameter of TMessageOperationArgs type. Internally,
it does just a dynamic cast of this param and calls the original
Update method:
procedure TBaseMessageOperationObserver.Update(aController: TObject;
aEventArgs: TEventArgs);
begin
Update(aController, aEventArgs as TMessageOperationArgs);
end;
TTransceiverExSubject is a TSubject descendant that just overrides
IsSuitableObserver method to perform the broadcasts only to those
observers that are descendants of TBaseMessageOperationObserver:
type
TTransceiverExSubject = class(TSubject)
protected
function IsSuitableObserver(aEventArgs: TEventArgs; aObserver:
TBaseObserver): Boolean; override;
end;
function TTransceiverExSubject.IsSuitableObserver(aEventArgs:
TEventArgs; aObserver: TBaseObserver): Boolean;
begin
if aEventArgs is TMessageOperationArgs then
Result := aObserver is TBaseMessageOperationObserver
else
Result := inherited IsSuitableObserver(aEventArgs, aObserver);
end;
Finally, there comes the declaration of TTransceiverEx class:
type
TTransceiverEx = class
private
fSubject: TTransceiverExSubject;
protected
procedure DoMessageDiscard(const aMessage: string);
procedure DoMessageReceive(const aMessage: string);
procedure DoMessageTransmit(const aMessage: string);
public
constructor Create;
destructor Destroy; override;
property AsSubject: TTransceiverExSubject read fSubject;
end;
constructor TTransceiverEx.Create;
begin
fSubject := TTransceiverExSubject.Create(Self);
end;
destructor TTransceiverEx.Destroy;
begin
fSubject.Free;
inherited;
end;
procedure TTransceiverEx.DoMessageDiscard(const aMessage: string);
var
vEventArgs: TMessageOperationArgs;
begin
vEventArgs := TMessageOperationArgs.Create(aMessage, moDiscard);
try
fSubject.NotifyObservers(vEventArgs);
finally
vEventArgs.Free;
end;
end;
Implementations of DoMessageReceive and DoMessageTransmit methods are
similar. As these methods determine the lifetime of argument object,
this should be accessed only during notification. IOW, consumer class
(the one that will be observing) should not store the reference to
this argument object because this reference will become invalid
outside the context of notification.
The last thing I'm gonna show here is the declaration/implementation
of sample consumer (observatory) class and the corresponding concrete
message operation observer:
type
TSampleConsumer = class;
TConcreteMessageOperationObserver =
class(TBaseMessageOperationObserver)
private
fConsumer: TSampleConsumer;
public
constructor Create(aConsumer: TSampleConsumer);
procedure Update(aController: TObject; aEventArgs:
TMessageOperationArgs); override;
end;
TSampleConsumer = class
private
fObserver: TConcreteMessageOperationObserver;
fTransceiver: TTransceiverEx;
public
constructor Create(aTransceiver: TTransceiverEx);
destructor Destroy; override;
procedure TransceiverMessageNotification(
aTransceiver: TTransceiverEx;
aEventArgs: TMessageOperationArgs);
end;
constructor TConcreteMessageOperationObserver.Create(aConsumer:
TSampleConsumer);
begin
fConsumer := aConsumer;
end;
procedure TConcreteMessageOperationObserver.Update(aController:
TObject; aEventArgs: TMessageOperationArgs);
begin
if aController is TTransceiverEx then
fConsumer.TransceiverMessageNotification(TTransceiverEx(aController),
aEventArgs);
end;
constructor TSampleConsumer.Create(aTransceiver: TTransceiverEx);
begin
fObserver := TConcreteMessageOperationObserver.Create(Self);
fTransceiver := aTransceiver;
fTransceiver.AsSubject.RegisterObserver(fObserver);
end;
destructor TSampleConsumer.Destroy;
begin
fTransceiver.AsSubject.UnregisterObserver(fObserver);
fObserver.Free;
end;
procedure TSampleConsumer.TransceiverMessageNotification(
aTransceiver: TTransceiverEx;
aEventArgs: TMessageOperationArgs);
begin
{ Do something specific here }
end;
{==============================================================================}
--
Ivo Bauer [OZM Research] |
|
| Back to top |
|
 |
Ivo Bauer Guest
|
Posted: Tue Jan 09, 2007 3:19 pm Post subject: Re: How to extend observer pattern? |
|
|
Joao Morais napsal(a):
| Quote: | I follow a pattern that sounds like the publish-subscriber:
|
Thanks for your suggestions. Could you please provide more detailed
information? I'd be especially interested to see the declaration of
TEvent and TNotifier classes.
--
Ivo Bauer [OZM Research] |
|
| Back to top |
|
 |
Joao Morais Guest
|
|
| Back to top |
|
 |
Peter Morris [Droopy eyes Guest
|
Posted: Wed Jan 10, 2007 2:32 pm Post subject: Re: How to extend observer pattern? |
|
|
Bold does it something like this
Subject.Subscribe(SomeEnumValue);
Observer.Receive(SomeEnumValue);
That way the subject can have a kind of hash table holding a list of
observers that are interested in each enum. When something subscribable
happens you retrieve the observer list based on the correct enum and then
notify each one.
--
--
Pete
Blessed are the geek, for they shall public class GeekEarth : Earth {}
====
Audio compression components, DIB graphics controls, ECO extensions,
FastStrings
http://www.droopyeyes.com
http://mrpmorris.blogspot.com
==== |
|
| Back to top |
|
 |
Ivo Bauer Guest
|
Posted: Wed Jan 10, 2007 4:34 pm Post subject: Re: How to extend observer pattern? |
|
|
Peter Morris [Droopy eyes software] napsal(a):
| Quote: | Subject.Subscribe(SomeEnumValue);
Observer.Receive(SomeEnumValue);
That way the subject can have a kind of hash table holding a list of
observers that are interested in each enum. When something subscribable
happens you retrieve the observer list based on the correct enum and then
notify each one.
|
Thanks for your suggestion. If I understand it correctly, you mean the
subject is holding multiple lists of observers and each of those lists
holds those observers that are interested in a specific enum? But in
this case the subject's Subscribe method (RegisterObserver ?) would
look like this:
TSubject.Subscribe(SomeEnumValue, SomeObserverInstance);
This concept would allow the observing class to determine which
observer is interested in what enum as opposed to what Joanna was
proposing - that subject is the one responsible for determining which
observers to broadcast - by checking if they descend from the right
base observer class. I guess I will need to think a bit more about it.
--
Ivo Bauer [OZM Research] |
|
| Back to top |
|
 |
Ivo Bauer Guest
|
Posted: Wed Jan 10, 2007 4:35 pm Post subject: Re: How to extend observer pattern? |
|
|
Joao Morais napsal(a):
Thanks, Joao. I'll take a look at it ASAP.
--
Ivo Bauer [OZM Research] |
|
| Back to top |
|
 |
Peter Morris [Droopy eyes Guest
|
Posted: Wed Jan 10, 2007 7:37 pm Post subject: Re: How to extend observer pattern? |
|
|
Hi
| Quote: | Thanks for your suggestion. If I understand it correctly, you mean the
subject is holding multiple lists of observers and each of those lists
holds those observers that are interested in a specific enum?
|
Yes. Something like this
(Constructor)
for Index := etFirst to etLast do
ObserverLists.Add(TInterfaceList.Create);
function TMyClass.GetObserverList(EventType : TEventType);
begin
Result := ObserverLists[Ord(EventType)];
end;
procedure TMyClass.RegisterObserver(EventType: TEventType; const Observer:
IObserver);
begin
GetObserverList(EventType).Add(Observer);
end;
procedure TMyClass.Signal(EventType: TEventType);
var
Index: Integer;
Observers: TInterfaceList;
begin
Observers := GetObserverList(EventType);
for Index := 0 to Observers.Count - 1 do
(Observers[Index] as IObserver).Notify(Self, EventType);
end;
| Quote: | that subject is the one responsible for determining which observers to
broadcast - by checking if they descend from the right base observer
class.
|
I think this would be less code than writing a class per event type.
--
--
Pete
Blessed are the geek, for they shall public class GeekEarth : Earth {}
====
Audio compression components, DIB graphics controls, ECO extensions,
FastStrings
http://www.droopyeyes.com
http://mrpmorris.blogspot.com
==== |
|
| Back to top |
|
 |
Ivo Bauer Guest
|
Posted: Thu Jan 11, 2007 4:59 am Post subject: Re: How to extend observer pattern? |
|
|
Hi, Peter!
Thanks for your insights. Please find my comments inlined below:
Peter Morris [Droopy eyes software] napsal(a):
| Quote: | subject is holding multiple lists of observers and each of those lists
holds those observers that are interested in a specific enum?
Yes. Something like this
|
[sample code snipped]
| Quote: | that subject is the one responsible for determining which observers to
broadcast - by checking if they descend from the right base observer
class.
I think this would be less code than writing a class per event type.
|
Putting aside the issue of writing more/less code, you've made me
think more about the responsibility - who is in charge of makind
decision which observer should be notified of what event. Subject
which is being observer or the observer itself? As you said there is
no need to write a dedicated observer class for each event (or group
of events that share the same signature) - one could have a single
observer class that knows how to deal with multiple notification
types. OTOH, one could also have multiple observers while each of them
is dedicated to single event type, but the important thing is that
it's not subject's responsibility to force how the observers are
actually defined. Taking this into account I've changed my mind and
redesigned my sample code so that observer itself decides which types
of events it will accept.
I've also thought about your proposal about the subject holding
multiple lists of observers, one list per event type (enum). This
concept looks fine to me at first sight but when you take a closer
look it seems to me that it forces me to reimplement a significant
portion of the observer pattern in every class that is intended to be
observed (i.e. subject). To be honest, I also like Joanna's argument
object approach due to its ability to define additional arguments for
every notification type. I'm going to show you the second version of
my sample code and will gladly appreciate your comments.
type
TBaseObserver = class
public
function AcceptsNotification(aEventArgs: TEventArgs): Boolean;
virtual;
procedure Update(aController: TObject; aEventArgs: TEventArgs);
virtual; abstract;
end;
function TBaseObserver.AcceptsNotification(aEventArgs: TEventArgs):
Boolean;
begin
Result := True;
end;
Base observer now has AcceptsNotification method that determines
whether the observer accepts (knows how to handle) various argument
objects. This method is virtual but not abstract because I don't want
to force every possible observer to override it so by default it
accepts every kind of argument object. Descendant classes may override
it to accept only specific types of notifications.
Another difference to previous design is that there will always be
only one subject because I shifted the above mentioned responsibility
to observer instead. Now the TTransceiver class declaration looks like
this:
type
TTransceiverEx = class
private
fSubject: TSubject;
protected
procedure DoMessageDiscard(const aMessage: string);
procedure DoMessageReceive(const aMessage: string);
procedure DoMessageTransmit(const aMessage: string);
public
constructor Create;
destructor Destroy; override;
property AsSubject: TSubject read fSubject;
end;
Now I'm going to declare only one observer of the TransceiverEx class
- it can handle only one type of notification, but it can also handle
multiple notification types as well. The point here is that I have the
freedom to decide myself. :-)
type
TSampleObserver = class(TBaseObserver)
private
fConsumer: TSampleConsumer;
public
constructor Create(aConsumer: TSampleConsumer);
function AcceptsNotification(aEventArgs: TEventArgs): Boolean;
override;
procedure Update(aController: TObject; aEventArgs: TEventArgs);
override;
end;
function TSampleObserver.AcceptsNotification(aEventArgs: TEventArgs):
Boolean;
begin
Result := aEventArgs is TTransceiverMessageOperationArgs;
end;
As you can see, the observer handles only one type of notification.
But should there be interest to handle more types, they can be easily
added to the above method. Or I can express interest in all
notification types that TTransceiver class exposes and don't override
this method at all (thereby keeping the default implementation).
procedure TSampleObserver.Update(aController: TObject; aEventArgs:
TEventArgs);
var
vTransceiver: TTransceiverEx;
vMsgOpArgs: TTransceiverMessageOperationArgs;
begin
if (aController is TTransceiverEx) and
(aEventArgs is TTransceiverMessageOperationArgs) then
begin
vTransceiver := TTransceiverEx(aController);
vMsgOpArgs := TTransceiverMessageOperationArgs(aEventArgs);
fConsumer.TransceiverMessageNotification(vTransceiver, vMsgOpArgs);
end;
end;
What I said above also applies to Update method. It can be easily
extended to handle multiple notification types.
Finally the sample consumer (observatory) class declaration looks like
this:
type
TSampleConsumer = class
private
fObserver: TSampleObserver;
fTransceiver: TTransceiverEx;
public
constructor Create(aTransceiver: TTransceiverEx);
destructor Destroy; override;
procedure TransceiverMessageNotification(
aTransceiver: TTransceiverEx;
aEventArgs: TTransceiverMessageOperationArgs);
end;
--
Ivo Bauer [OZM Research] |
|
| Back to top |
|
 |
Graeme Geldenhuys Guest
|
Posted: Thu Jan 11, 2007 6:13 pm Post subject: Re: How to extend observer pattern? |
|
|
I'm using the tiOPF framework and was having the same problem and found
a solution in this past week. I'm trying out an idea based on the Java
Event Listeners design and so far it is working very nicely. This
allows me to register for specific events and not just a
one-size-fits-all as tiOPF currently has (and most Observer
implementations). It can be so fine grained to the point where I can get
notified when a property changes (even if I don't use attribute types).
And all that using a single internal observer list - no need for
multiple observer lists.
I implemented the Observer pattern based on Interfaces. Note that tiOPF
doesn't use Interfaces in it's design and I am very new to Interfaces,
but this solution seemed to lend itself to Interfaces very well. So far
so good. The Observed Object doesn't need to know anything more
about the observing objects, other than that they implement a specific
interface.
I created a TCustomBusinessObject and TCustomBusinessObjectList that
descend from the standard tiOPF base classes and has the following extra
methods. I had to introduce IInterface into my object hierarchy myself
due to tiOPF not using Interfaces - as I mentioned before.
TCustomBusinessObject
....
procedure AddListener(Listener: ITIObjectListener); virtual;
procedure RemoveListener(Listener: ITIObjectListener); virtual;
end;
TCustomBusinessObjectList
....
procedure AddListener(Listener: ITIObjectListListener); virtual;
procedure RemoveListener(Listener: ITIObjectListListener); virtual;
end;
Both classes contain a single TEventListenerList object defined as follows:
{ all new event listeners will descend from this interface }
IEventListener = interface(IInterface)
['{7461C599-974C-4592-8173-A3550BB267D9}']
end;
TEventListenerList = class(TInterfacedObject, IEventListenerList)
private
FListeners: IInterfaceList;
public
{ IEventListenerList }
function Add(Item: IEventListener): Integer;
procedure Clear;
procedure Delete(Index: Integer);
procedure EnumListeners(EnumListenerProc: TEnumListenerProc);
function GetCount: Integer;
function GetItems(Index: Integer): IEventListener;
procedure Remove(Item: IEventListener);
constructor Create;
end;
Now lets use a simple example by looking at the TtiObjectList and how I
extended it. Currently I can observe the ObjectList and get notified if
something changes and the changed ObjectList gets passed to the
Observers in the Update method. This is the problem. What actually
changed? Was something added or deleted for example? The current
implementation didn't give us any such hints!
To solve this I then created the following Interface which can be used
to get notified when items get added or deleted from the ObjectList.
ITIObjectListListener = interface(IEventListener)
['{3002F5D7-0504-4AB9-B993-FD1E272BF64A}']
procedure ItemAdded(AObject: TTiObject);
procedure ItemDeleted(AObject: TTiObject);
end;
I then override the relevant methods like Add, Delete, etc to fire off
the notifications. Here is an example for handling the adding of a object.
procedure TCustomBusinessObjectList.Add(const AObject: TtiObject);
begin
inherited Add(AObject);
FLastItemAdded := AObject;
FListeners.EnumListeners(@EnumDoAdd);
FLastItemAdded := nil;
end;
procedure TCustomBusinessObjectList.EnumDoAdd(Listener: IEventListener);
var
lTIObjectListListener: ITIObjectListListener;
begin
if Supports(Listener, IID_TIObjectListListener,
lTIObjectListListener) then
lTIObjectListListener.ItemAdded(FLastItemAdded);
end;
The benefits of doing things this way is that your connections between
objects are explicit and you only implement what you need in observer
objects. The downside is that it doesn't lend itself to a generic, "tell
me when property X of object Y changes" - though I believe that over
time I would see common patterns occurring that could be refactored
either into helper classes or into the base classes themselves.
Regards,
- Graeme -
Ivo Bauer wrote:
| Quote: | Hi!
I've got an object that exposes a couple of events. As with many other
objects, not necessarily all events have the same prototypes. I'd like
to be able to make multiple objects observe the events of that object.
With the observer pattern, I can make multiple objects observe just one
event of the object which is being observed. Of course I could create
one separate observer for each event but this doesn't look good to me.
Is there a way how to customize/extend the observer pattern to be able
to register/unregister for observing multiple object events at once?
Thanks in advance.
|
|
|
| Back to top |
|
 |
Peter Morris [Droopy eyes Guest
|
Posted: Fri Jan 12, 2007 9:12 am Post subject: Re: How to extend observer pattern? |
|
|
Well I didn't read it all so I might be wrong, but this is my impression
from your text (not source).
If an observer asks to be notified about something then it should only be
notified when that specific thing happens. I get the impression that some
code in your observer will be executed for *anything* that happens, ather
than only what it is interested in. I think this is more code, and when you
have lots of subscribers it will really slow down your app if you have to
send out lots of notifications.
--
--
Pete
Blessed are the geek, for they shall public class GeekEarth : Earth {}
====
Audio compression components, DIB graphics controls, ECO extensions,
FastStrings
http://www.droopyeyes.com
http://mrpmorris.blogspot.com
==== |
|
| Back to top |
|
 |
Ivo Bauer Guest
|
Posted: Mon Jan 22, 2007 9:11 am Post subject: Re: How to extend observer pattern? |
|
|
Ivo Bauer napsal(a):
| Quote: | 3rd version of my sample code is underway.
|
And also done now. I can publish the complete source somewhere in case
anyone would be interested.
Thanks again for your suggestions, Peter.
--
Ivo Bauer [OZM Research] |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|