[fpc-devel] Interface delegation fix: backport to FPC 2.4.2 ...?
Dimitri Smits
smitco at telenet.be
Thu May 20 17:40:48 CEST 2010
Graeme Geldenhuys wrote:
>> Patterns are faddy - you are not going to please everyone.
>Please explain and give examples where Observer will not be useful. Also, I
>do not know what "faddy" means.
one of the first rules when using GoF book/patterns is:
use them wisely/appropriately.
that said, I've encountered many design pattern noobs who made it a fetish to use as much patterns as possible, whereever possible, when first introduced to the notion. At times, this discussion seems like one of those.
don't get me wrong, the observer/observable (or publish/subscriber) is a nice pattern and at times useful at other times sorely needed, but yet at other times unneeded.
If I understand correctly, you want to implement something like a multicast eventsystem? (ISO single event handlers)
Why it has to be on TPersistent is beyond me.
Can an implementation not be made using a "global" registry where you can link your instance with the other instances? Every time the subscribers need to be made aware of the occurrance of some "event", you can "multicast" using "self" and maybe the event-identifier?
Either way, you still need to trigger a notification to your subscribers that your objects' state has changed and a way to tell it WHAT it triggered.
And either way, you definately need a clean separation of concerns.
>> I'd rather see a mechanism for injecting first class extensions to
>> existing classes.
>* Interfaces don't work because you need to subclass them to get the extra
>functionality. This is totally useless for things like TCombobox.Items or
>TMemo.Lines (like Michael explained) because such components uses TStrings
>directly.
the way others tried to make it clear is the notion of "java interfaces". By which they mean the AWT eventlisteners, I presume?
it goes (from memory of times gone by) a bit like so:
we have an interface that has 2 methods.
interface WindowMessageHandler {
public Open(...);
public Close(...);
}
you have your Window class that has 2 public methods & 1 arbitrarely scoped method per fireEvent:
Window {
...
public void attachWindowMessageHandler(WindowMessageHandler wmh);
public void removeWindowMessageHandler(WindowMessageHandler wmh);
protected void fireOpen() {
// loop over the internal list and call Close(...)
}
protected void fireClose() {
// loop over the internal list and call Close(...)
}
}
When your event occurs, you still need to call fireOpen() or fireClose() to notify all your listeners, and just as with events, you need to parametrise them, so custom developed every time you have something new and not nicely generalised/abstracted.
Something similar is easily done with pascal interfaces. Even outside of every class!
With interfaces is maybe not so easily done if you don't understand them or their use, but it gives you at least independence on implementation level and does not restrict you to use a specific type.
Either way, you still need to (somehow) link back. Does your observer know it gets attached to that specific instance of observable? (ie: did it link to it by itself or by its creator?) How does the observed object know when the observer gets destroyed or are you sure it is out of the observer-list of the observed object? Interfaces with automatic destroy on refcount=0 are not an option there. (btw, you DO know that IUnknown.Release CAN be overridden to NOT Free on refcount=0?)
One might get far with a TComponent that implements attach+remove for an 2 object-instances and that implements a "visitor" pattern
TObservableEvent=class(TObject)
private
FEmitter:
public
constructor Create(elmitter);
// the object that is generating the event
property Emitter : TObject read=GetEmitter;
procedure Execute(observer: TObject);
end;
TObserverObservableRegistry=class(TComponent)
...
public
// store a "connection" between the 2
procedure attach(observable, observer: TObject);
// lookup the "connection" and remove it
procedure remove(observable, observer: TObject);
// "loop/visit" through all the observers' oberservables and remove the "connection" where observer=parameter
procedure removeAllObservables( observer: TObject);
// "loop/visit" through all the observers' oberservables and remove the "connection" where observable=parameter
procedure removeAllObservers( observable: TObject);
// "loop/visit" over all "connections" where "observable"=event.emitter and call event.execute(connection.observer)?
procedure fireEvent(event: TObservableEvent);
// loop over all "connections" and remove those where connection.observer = parameter or connection.observable.parameter i.e. call removeAllObservables and removeAllObservers with that parameter
procedure FreeNotification(...);override;
end;
In fact, TObservableEvent can be made into a referencecounted interface very easily, and I would even prefer it.
>* Class Helpers like in Delphi. Also useless because you can only extend
>them with one helper class at a time. So you want ComboBox.Items.IsEmpty so
>you create a help class do do that. Now that helper class blocks any other
>functionality from being added. Class Helpers were introduced in Delphi 8 -
>the worse release of Delphi *ever*, and with such features we can clearly
>see why.
while I 100% agree with you on the crappyness of Delphi Ocho, I must say that it was a .Net only release! Syntax for .Net features support that later made its way into Win32 Delphi. Delphi 8 was bundled with D7 for the w32 apps.
---
that all said, I still am not opposed to design patterns, just not in something that is supposed to be somewhat delphi compatible framework.
sorry for the rambling/hard-to-follow(/read)-train-of-though, but the A/C seems broken here today in the "factory" and hot+humidity is not a way to think clearly. :-)
sorry also for the conversation disruption, but I read digests on the list and can only reply accordingly.
kind regards,
Dimitri Smits
More information about the fpc-devel
mailing list