 |
BorlandTalk.com Borland discussion newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Roal Zanazzi Guest
|
Posted: Mon Nov 01, 2004 9:56 pm Post subject: How to notify changes from child object to parent object? |
|
|
Hi all,
I have a somewhat complex data in memory that can be represented as a tree:
GranParent
+--ParentsList
+--Parent
| Quote: | +--ChildrenList
+--Child
+--Child
+--Parent
+--ChildrenList
+--Child
+... |
I want that whenever any change is done at any level below GranParent,
GranParent object is notified of it (and it will set a boolean flag
"modified").
Changes could include:
- any property of Child or Parent objects is modified
- a Child is added or deleted to/from any Parent
- a Parent is added or deleted
I'm seeking for a good design to do that;
I've implemented the Observer pattern (single observer, via
TNotifyEvent) to notify changes up the hierarchy to GranParent, it seems
good because it decouples classes (I don't need them decoupled now, but
I don't want to change everything later if I need to).
Any suggestion is greatly appreciated.
Also, any hint on a good implementation of Observer pattern with Delphi
is also appreciated (I've not used interfaces right now).
TIA
--
Roal Zanazzi
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Mon Nov 01, 2004 10:34 pm Post subject: Re: How to notify changes from child object to parent object |
|
|
"Roal Zanazzi" <spam (AT) someone (DOT) else> a écrit dans le message de news:
[email]4186b0ec (AT) newsgroups (DOT) borland.com[/email]...
| Quote: | Also, any hint on a good implementation of Observer pattern with Delphi
is also appreciated (I've not used interfaces right now).
|
Here is an example of the Observer pattern in Delphi; the Subject is a Timer
that is observed by as many Digital Clocks as required.
//////////////////////////
type
TObserver = class
public
procedure Update(const Subject: TObject); virtual; abstract;
end;
TSubject = class
private
fController: TObject;
fObservers: TObjectList;
public
constructor Create(const Controller: TObject);
procedure Attach(const Observer: TObserver);
procedure Detach(const Observer: TObserver);
procedure Notify;
end;
TClockTimer = class
private
fTimer: TTimer;
fInternalTime: TDateTime;
fSubject: TSubject;
procedure Tick(Sender: TObject);
public
constructor Create;
destructor Destroy; override;
function GetTime: TDateTime;
property AsSubject: TSubject
read fSubject;
end;
TDigitalClock = class;
TClockObserver = class(TObserver)
private
fDisplay: TDigitalClock;
public
constructor Create(const Display: TDigitalClock);
procedure Update(const Subject: TObject); override;
end;
TDigitalClock = class(TPanel)
private
fObserver: TClockObserver;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property AsObserver: TClockObserver
read fObserver;
procedure ObserverUpdate(const Subject: TClockTimer);
end;
implementation
uses
SysUtils;
// TSubject
constructor TSubject.Create(const Controller: TObject);
begin
inherited Create;
fController := Controller;
end;
procedure TSubject.Attach(const Observer: TObserver);
begin
if fObservers = nil then
fObservers := TObjectList.Create;
if fObservers.IndexOf(Observer) < 0 then
fObservers.Add(Observer);
end;
procedure TSubject.Detach(const Observer: TObserver);
begin
if fObservers <> nil then
begin
fObservers.Remove(Observer);
if fObservers.Count = 0 then
begin
fObservers.Free;
fObservers := nil;
end;
end;
end;
procedure TSubject.Notify;
var
i: Integer;
begin
if fObservers <> nil then
for i := 0 to Pred(fObservers.Count) do
TObserver(fObservers[i]).Update(fController);
end;
// TClockTimer
constructor TClockTimer.Create;
begin
inherited Create;
fTimer := TTimer.Create(nil);
fTimer.Interval := 1000;
fTimer.OnTimer := Tick;
fTimer.Enabled := True;
fSubject := TSubject.Create(self);
end;
destructor TClockTimer.Destroy;
begin
fSubject.Free;
fTimer.Enabled := False;
fTimer.Free;
inherited Destroy;
end;
function TClockTimer.GetTime: TDateTime;
begin
Result := fInternalTime;
end;
procedure TClockTimer.Tick(Sender: TObject);
begin
fInternalTime := Now;
fSubject.Notify;
end;
// TClockObserver
constructor TClockObserver.Create(const Display: TDigitalClock);
begin
inherited Create;
fDisplay := Display;
end;
procedure TClockObserver.Update(const Subject: TObject);
begin
if (Subject is TClockTimer) then
fDisplay.ObserverUpdate(TClockTimer(Subject));
end;
// TDigitalClock
constructor TDigitalClock.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fObserver := TClockObserver.Create(self);
end;
destructor TDigitalClock.Destroy;
begin
fObserver.Free;
inherited Destroy;
end;
procedure TDigitalClock.ObserverUpdate(const Subject: TClockTimer);
begin
Text := FormatDateTime('tt', Subject.GetTime);
end;
//////////////////////
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Rainer Krug Guest
|
Posted: Tue Nov 02, 2004 7:29 am Post subject: Re: How to notify changes from child object to parent object |
|
|
Hi
following your post, I tried to come up with the obsever pattern with
interfaces (IObserver and ISubject). I modified your TSubject and came
up with the code below, implementing TSubject, ISubject and IObserver.
Will this work, or are there any flaws in the design?
Rainer
/////////////////////////////////////////////////////////////////////
unit UObserverPattern;
interface
uses
SysUtils, Windows, Messages, Classes, Graphics, Controls,
Forms, Dialogs;
type
TSubject = class (TAggregatedObject)
private
FObservers: IInterfaceList;
public
constructor Create(const Controller: IInterface);
procedure Attach(const Observer: IObserver);
procedure Detach(const Observer: IObserver);
procedure Notify;
end;
IObserver = interface (IInterface)
['{06EE5F6F-AF59-4549-AA70-7E488C09CDE5}']
procedure Update(const Subject: IInterface); virtual; abstract;
end;
ISubject = interface (IInterface)
['{7D6F54F5-E919-4784-86D7-F00224DE0595}']
procedure Attach(const Observer: TObserver);
procedure Detach(const Observer: TObserver);
procedure Notify;
end;
procedure Register;
implementation
procedure Register;
begin
end;
constructor TSubject.Create(const Controller: IInterface);
begin
inherited Create(Controller);
FObservers := TInterfaceList.Creste;
end
procedure TSubject.Attach(const Observer: IObserver);
begin
if FObservers.IndexOf(Observer) < 0 then
FObservers.Add(Observer);
end;
procedure TSubject.Detach(const Observer: IObserver);
begin
if FObservers <> nil then
begin
FObservers.Remove(Observer);
end;
end;
procedure TSubject.Notify;
var
i: Integer;
begin
if FObservers <> nil then
for i := 0 to Pred(FObservers.Count) do
IObserver(FObservers[i]).Update(Controller);
end;
end.
/////////////////////////////////////////////////////////////////////
Joanna Carter (TeamB) wrote:
| Quote: | "Roal Zanazzi" <spam (AT) someone (DOT) else> a icrit dans le message de news:
[email]4186b0ec (AT) newsgroups (DOT) borland.com[/email]...
Also, any hint on a good implementation of Observer pattern with
Delphi is also appreciated (I've not used interfaces right now).
Here is an example of the Observer pattern in Delphi; the Subject is
a Timer that is observed by as many Digital Clocks as required.
//////////////////////////
type
TObserver = class
public
procedure Update(const Subject: TObject); virtual; abstract;
end;
TSubject = class
private
fController: TObject;
fObservers: TObjectList;
public
constructor Create(const Controller: TObject);
procedure Attach(const Observer: TObserver);
procedure Detach(const Observer: TObserver);
procedure Notify;
end;
TClockTimer = class
private
fTimer: TTimer;
fInternalTime: TDateTime;
fSubject: TSubject;
procedure Tick(Sender: TObject);
public
constructor Create;
destructor Destroy; override;
function GetTime: TDateTime;
property AsSubject: TSubject
read fSubject;
end;
TDigitalClock = class;
TClockObserver = class(TObserver)
private
fDisplay: TDigitalClock;
public
constructor Create(const Display: TDigitalClock);
procedure Update(const Subject: TObject); override;
end;
TDigitalClock = class(TPanel)
private
fObserver: TClockObserver;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property AsObserver: TClockObserver
read fObserver;
procedure ObserverUpdate(const Subject: TClockTimer);
end;
implementation
uses
SysUtils;
// TSubject
constructor TSubject.Create(const Controller: TObject);
begin
inherited Create;
fController := Controller;
end;
procedure TSubject.Attach(const Observer: TObserver);
begin
if fObservers = nil then
fObservers := TObjectList.Create;
if fObservers.IndexOf(Observer) < 0 then
fObservers.Add(Observer);
end;
procedure TSubject.Detach(const Observer: TObserver);
begin
if fObservers <> nil then
begin
fObservers.Remove(Observer);
if fObservers.Count = 0 then
begin
fObservers.Free;
fObservers := nil;
end;
end;
end;
procedure TSubject.Notify;
var
i: Integer;
begin
if fObservers <> nil then
for i := 0 to Pred(fObservers.Count) do
TObserver(fObservers[i]).Update(fController);
end;
// TClockTimer
constructor TClockTimer.Create;
begin
inherited Create;
fTimer := TTimer.Create(nil);
fTimer.Interval := 1000;
fTimer.OnTimer := Tick;
fTimer.Enabled := True;
fSubject := TSubject.Create(self);
end;
destructor TClockTimer.Destroy;
begin
fSubject.Free;
fTimer.Enabled := False;
fTimer.Free;
inherited Destroy;
end;
function TClockTimer.GetTime: TDateTime;
begin
Result := fInternalTime;
end;
procedure TClockTimer.Tick(Sender: TObject);
begin
fInternalTime := Now;
fSubject.Notify;
end;
// TClockObserver
constructor TClockObserver.Create(const Display: TDigitalClock);
begin
inherited Create;
fDisplay := Display;
end;
procedure TClockObserver.Update(const Subject: TObject);
begin
if (Subject is TClockTimer) then
fDisplay.ObserverUpdate(TClockTimer(Subject));
end;
// TDigitalClock
constructor TDigitalClock.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fObserver := TClockObserver.Create(self);
end;
destructor TDigitalClock.Destroy;
begin
fObserver.Free;
inherited Destroy;
end;
procedure TDigitalClock.ObserverUpdate(const Subject: TClockTimer);
begin
Text := FormatDateTime('tt', Subject.GetTime);
end;
//////////////////////
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Tue Nov 02, 2004 10:10 am Post subject: Re: How to notify changes from child object to parent object |
|
|
"Rainer Krug" <RMK_ (AT) _krugs_ (DOT) _de> a écrit dans le message de news:
[email]41874565 (AT) newsgroups (DOT) borland.com[/email]...
| Quote: | following your post, I tried to come up with the obsever pattern with
interfaces (IObserver and ISubject). I modified your TSubject and came
up with the code below, implementing TSubject, ISubject and IObserver.
Will this work, or are there any flaws in the design?
|
Apart from some copy-paste typos :-)
// 1. remove the virtual abstract from Update
IObserver = interface (IInterface)
['{06EE5F6F-AF59-4549-AA70-7E488C09CDE5}']
procedure Update(const Subject: IInterface);
end;
// 2. parameters to Attach and Detach should be interfaces
ISubject = interface (IInterface)
['{7D6F54F5-E919-4784-86D7-F00224DE0595}']
procedure Attach(const Observer: IObserver);
procedure Detach(const Observer: IObserver);
procedure Notify;
end;
You have to be aware that TAggregatedObject does not work correctly if you
use an interface ref for the delegate, it must be an object ref.
Assuming you do the following :
///////////////////////////
IThing = interface
['{0AEDD3C1-43CA-49C4-8AE7-CBAD10F789F3}']
function GetName: string;
procedure SetName(const Value: string);
end;
TThing = class(TInterfacedObject, IThing, ISubject)
private
fName: string;
function GetName: string;
procedure SetName(const Value: string);
private
fSubject: TSubject;
property Subject: TSubject
read fSubject
implements ISubject;
public
constructor Create;
destructor Destroy; override;
end;
/////////////////////
You will be fine, just don't forget to free the delegate in the destructor.
But if you do the following :
///////////////////////////
TSubject = class(TAggregatedObject, ISubject)
private
fObservers: IInterfaceList;
protected
procedure Attach(const Observer: IObserver);
procedure Detach(const Observer: IObserver);
procedure Notify;
public
constructor Create(const Controller: IInterface);
destructor Destroy; override;
end;
TThing = class(TInterfacedObject, IThing, ISubject)
private
fName: string;
function GetName: string;
procedure SetName(const Value: string);
private
fSubject: ISubject;
property Subject: ISubject
read fSubject
implements ISubject;
public
constructor Create;
end;
/////////////////////
And rely on the fact that, because fSubject is an interface, it will be
released on destruction, then you will be in for a surprise; because neither
the aggregate nor the delegate will be freed.
Instead you need to use the weak reference trick of TAggregatedObject, but
forget about the _AddRef and _Release methods. You can also do
QueryInterface differently :
///////////////////////
TSubject = class(TInterfacedObject, ISubject)
private
fController: Pointer;
function GetController: IInterface;
private
fObservers: IInterfaceList;
procedure Attach(const Observer: IObserver);
procedure Detach(const Observer: IObserver);
procedure Notify;
protected
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
public
constructor Create(const Controller: IInterface);
destructor Destroy; override;
end;
constructor TSubject.Create(const Controller: IInterface);
begin
inherited Create;
fController := Pointer(Controller);
fObservers := TInterfaceList.Create;
end;
function TSubject.GetController: IInterface;
begin
Result := IInterface(fController);
end;
function TSubject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if not IsEqualGUID(IID, ISubject) then
GetController.QueryInterface(IID, Obj);
end;
//////////////////
You also need to be aware that all Observers need to be explicitly cast to
IInterface before adding them to the observers list; also when trying to
find them in the list. This is because the address of an IObserver interface
reference is not the same as the address of an IInterface reference.
//////////////////////
procedure TSubject.Attach(const Observer: IObserver);
begin
if fObservers.IndexOf(Observer as IInterface) < 0 then
fObservers.Add(Observer as IInterface);
end;
procedure TSubject.Detach(const Observer: IObserver);
begin
fObservers.Remove(Observer as IInterface);
end;
/////////////////////
Also don't forget that there may be occasions when you are expecting an
Observer to be released somewhere in code but, because it is referenced
inside the Subject, it cannot be released until you Detach it from the
Subject. This can cause problems in tree structures where a node aggregates
other nodes and is also an Observer of those child nodes.
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Rainer Krug Guest
|
Posted: Tue Nov 02, 2004 11:29 am Post subject: Re: How to notify changes from child object to parent object |
|
|
Hi Joanna
Thanks for the help - I finally begin to understand something about
Interfaces...
Bet there are still some points which I don't understand ore which are
unclear - see below:
| Quote: | Apart from some copy-paste typos
|
OK - these are clear. In addition:
TSubject = class (TAggregatedObject, ISubject)
| Quote: |
// 1. remove the virtual abstract from Update
|
OK
| Quote: | // 2. parameters to Attach and Detach should be interfaces
|
OK
| Quote: |
You have to be aware that TAggregatedObject does not work correctly
if you use an interface ref for the delegate, it must be an object
ref.
|
I don't understand this. (see below)
| Quote: | Assuming you do the following :
You will be fine, just don't forget to free the delegate in the
destructor.
|
OK - this is clear.
| Quote: |
But if you do the following :
And rely on the fact that, because fSubject is an interface, it will
be released on destruction, then you will be in for a surprise;
because neither the aggregate nor the delegate will be freed.
Instead you need to use the weak reference trick of
TAggregatedObject, but forget about the _AddRef and _Release methods.
|
Isn't this already implemented in TAgregatedObject?
| Quote: | You can also do QueryInterface differently :
///////////////////////
function TSubject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if not IsEqualGUID(IID, ISubject) then
GetController.QueryInterface(IID, Obj);
end;
//////////////////
|
OK - here I am lost. Also: isn't "GetController.QueryInterface(IID,
Obj);" essentially the implementation of QueryInterface from
TAggregatedObject?
| Quote: |
You also need to be aware that all Observers need to be explicitly
cast to IInterface before adding them to the observers list; also
when trying to find them in the list. This is because the address of
an IObserver interface reference is not the same as the address of an
|
Good to know. So if one object implements 3 different Interfaces, their
references are not the same? I definitely have to remember this.
so:
TSubject = class (TAggregatedObject, IOne, ITwo, IThree)
var
fSubject = IOne;
begin
fSubject := TSubject.Create;
if Pointer(FSubject as ITwo) <> Pointer(FSubject) then //<== so this
condition is always true?
DoSomething;
end;
if Pointer((FSubject as ITwo) as IOne) = Pointer(FSubject) then
//<== and this one? Is this condition also always true?
DoSomethingElse;
end;
end;
Correct?
| Quote: |
Also don't forget that there may be occasions when you are expecting
an Observer to be released somewhere in code but, because it is
referenced inside the Subject, it cannot be released until you Detach
it from the Subject. This can cause problems in tree structures where
a node aggregates other nodes and is also an Observer of those child
nodes.
|
So there are essentially two ways of dealing with this: using a
TPointerList instead of the TIntegerList and to use weak interfaces or,
in the destructor of the Observer to Detach itself from the Subject. I
guess, a combination of both would be the best solution.
I definitely have the feeling that I am learning and understanding
something about interfaces...
Thanks,
Rainer
| Quote: |
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Tue Nov 02, 2004 12:51 pm Post subject: Re: How to notify changes from child object to parent object |
|
|
"Rainer Krug" <RMK_ (AT) _krugs_ (DOT) _de> a écrit dans le message de news:
[email]41877d90 (AT) newsgroups (DOT) borland.com[/email]...
| Quote: | Instead you need to use the weak reference trick of
TAggregatedObject, but forget about the _AddRef and _Release methods.
Isn't this already implemented in TAgregatedObject?
|
If you look at the example of the aggregating class, you will see that you
can declare the delegate as an ISubject rather than a TSubject so that you
can have a TSubject class that already supports ISubject that will
automatically released when the aggregating gobject gets freed.
Look carefully at the differences between the two examples.
| Quote: | OK - here I am lost. Also: isn't "GetController.QueryInterface(IID,
Obj);" essentially the implementation of QueryInterface from
TAggregatedObject?
|
No, TAggregatedObject tells the query to go to the aggreagating object for
all types, which means that although the query came into the Subject to
start with it has to go out to the agg. just to find that it could have been
handled in the Subject.
This is an optimization rather than a problem :-)
| Quote: | Good to know. So if one object implements 3 different Interfaces, their
references are not the same? I definitely have to remember this.
|
Once you have had to debug this problem, you don't forget )
| Quote: | so:
TSubject = class (TAggregatedObject, IOne, ITwo, IThree)
var
fSubject = IOne;
begin
fSubject := TSubject.Create;
if Pointer(FSubject as ITwo) <> Pointer(FSubject) then //<== so this
condition is always true?
DoSomething;
end;
if Pointer((FSubject as ITwo) as IOne) = Pointer(FSubject) then
//<== and this one? Is this condition also always true?
DoSomethingElse;
end;
end;
Correct?
|
I think so :-)
| Quote: | So there are essentially two ways of dealing with this: using a
TPointerList instead of the TIntegerList and to use weak interfaces or,
in the destructor of the Observer to Detach itself from the Subject. I
guess, a combination of both would be the best solution.
|
TIntegerList <> TInterfaceList :-)
But yes, this will depend on the scenario.
| Quote: | I definitely have the feeling that I am learning and understanding
something about interfaces...
|
Excellent, just don't forget the golden rule: don't mix interface and object
references to the same object.
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Rainer Krug Guest
|
Posted: Wed Nov 03, 2004 6:44 am Post subject: Re: How to notify changes from child object to parent object |
|
|
| Quote: | Instead you need to use the weak reference trick of
TAggregatedObject, but forget about the _AddRef and _Release
methods.
Isn't this already implemented in TAgregatedObject?
If you look at the example of the aggregating class, you will see
that you can declare the delegate as an ISubject rather than a
TSubject so that you can have a TSubject class that already supports
ISubject that will automatically released when the aggregating
gobject gets freed.
|
I see the difference between using
fSubject : ISubject
and
fSubject : TSubject
if TSubject inherits fromTAggregatedObject.
I am aware that in the second case, fSubject needs to be freed in the
destructor of the aggregating object - no problem with that.
But I don't understand why, if fSubject is of type ISubject, it won't
be freed automatically. When I look in the implementation of
TAggregatedObject, the implementation of Controller already uses a weak
link. Therefore, no circular references from the aggregating object to
TSubject exists, and fSubject should be destroyed automatically when
the aggregating objects becomes destroyed - or am I missing something?
| Quote: |
Look carefully at the differences between the two examples.
OK - here I am lost. Also: isn't "GetController.QueryInterface(IID,
Obj);" essentially the implementation of QueryInterface from
TAggregatedObject?
No, TAggregatedObject tells the query to go to the aggreagating
object for all types, which means that although the query came into
the Subject to start with it has to go out to the agg. just to find
that it could have been handled in the Subject.
This is an optimization rather than a problem
|
OK - I see your point. I must say, I like that optimization...
| Quote: |
TIntegerList <> TInterfaceList
|
Oh I hate it when the fingers are thinking about different things then
the brain...
| Quote: | I definitely have the feeling that I am learning and understanding
something about interfaces...
Excellent, just don't forget the golden rule: don't mix interface and
object references to the same object.
|
Thanks for the last part - "references to the same object". That
clarifies a lot. In other words, I can use object references to one
object and interface references to another one in the same program?
Rainer
| Quote: |
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Wed Nov 03, 2004 9:52 am Post subject: Re: How to notify changes from child object to parent object |
|
|
"Rainer Krug" <RMK_ (AT) _krugs_ (DOT) _de> a écrit dans le message de news:
41888c4d$1 (AT) newsgroups (DOT) borland.com...
| Quote: | But I don't understand why, if fSubject is of type ISubject, it won't
be freed automatically. When I look in the implementation of
TAggregatedObject, the implementation of Controller already uses a weak
link. Therefore, no circular references from the aggregating object to
TSubject exists, and fSubject should be destroyed automatically when
the aggregating objects becomes destroyed - or am I missing something?
|
No, you're not missing anything. I am not sure why it doesn't work but,
unless you call _Release after calling inherited Create in the constructor
of your aggregating type, the aggregated object never gets freed. Such a
call should not be necessary.
This must have something to do with the delegation of QueryInterface to the
aggregating class, but I haven't yet been able to track it down.
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Rainer Krug Guest
|
Posted: Wed Nov 03, 2004 9:59 am Post subject: Re: How to notify changes from child object to parent object |
|
|
That makes things a little bit clearer. I will use, in this case,
TSubject instead of ISubject.
Thanks a lot, I learned a loit,
Rainer
Joanna Carter (TeamB) wrote:
| Quote: | "Rainer Krug" <RMK_ (AT) _krugs_ (DOT) _de> a icrit dans le message de news:
41888c4d$1 (AT) newsgroups (DOT) borland.com...
But I don't understand why, if fSubject is of type ISubject, it
won't be freed automatically. When I look in the implementation of
TAggregatedObject, the implementation of Controller already uses a
weak link. Therefore, no circular references from the aggregating
object to TSubject exists, and fSubject should be destroyed
automatically when the aggregating objects becomes destroyed - or
am I missing something?
No, you're not missing anything. I am not sure why it doesn't work
but, unless you call _Release after calling inherited Create in the
constructor of your aggregating type, the aggregated object never
gets freed. Such a call should not be necessary.
This must have something to do with the delegation of QueryInterface
to the aggregating class, but I haven't yet been able to track it
down.
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Wed Nov 03, 2004 11:19 am Post subject: Re: How to notify changes from child object to parent object |
|
|
"Rainer Krug" <RMK_ (AT) _krugs_ (DOT) _de> a écrit dans le message de news:
4188ba1b$1 (AT) newsgroups (DOT) borland.com...
| Quote: | That makes things a little bit clearer. I will use, in this case,
TSubject instead of ISubject.
|
Either that or write yourself a once only TSubject class that does not
derive from TAggregatedObject, as per my example.
If you always use it in an ISubject field, you can forget about freeing the
Subject delegate instead of forgetting to free the delegate :-)
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Rainer Krug Guest
|
Posted: Wed Nov 03, 2004 1:16 pm Post subject: Re: How to notify changes from child object to parent object |
|
|
| Quote: | That makes things a little bit clearer. I will use, in this case,
TSubject instead of ISubject.
Either that or write yourself a once only TSubject class that does not
derive from TAggregatedObject, as per my example.
If you always use it in an ISubject field, you can forget about
freeing the Subject delegate instead of forgetting to free the
delegate :-)
|
In this case, I have to include QueryInterface as mentioned by you in
the example. For _AddRef and _Release, I have to use the basic code for
reference counting:
function TSubject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if not IsEqualGUID(IID, ISubject) then
GetController.QueryInterface(IID, Obj);
end;
function TdmIOCAMySQL._Release: Integer;
begin
Dec(FRefCount);
Result := FRefCount;
if Result = 0 then
Destroy;
end;
function TdmIOCAMySQL._AddRef: Integer;
begin
Inc(FRefCount);
Result := FRefCount;
end;
and one field
FRefCount: integer;
and then I can use ISubject as the Subject delegate?
Sounds interesting
Rainer
|
|
| Back to top |
|
 |
Joanna Carter (TeamB) Guest
|
Posted: Wed Nov 03, 2004 2:29 pm Post subject: Re: How to notify changes from child object to parent object |
|
|
"Rainer Krug" <RMK_ (AT) _krugs_ (DOT) _de> a écrit dans le message de news:
[email]4188e843 (AT) newsgroups (DOT) borland.com[/email]...
| Quote: | In this case, I have to include QueryInterface as mentioned by you in
the example. For _AddRef and _Release, I have to use the basic code for
reference counting:
function TSubject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if not IsEqualGUID(IID, ISubject) then
GetController.QueryInterface(IID, Obj);
end;
|
Derive both TSubject and your aggregating class from TInterfacedObject and
just redeclare QueryInterface in TSubject only; then you don't need to do
anything with _AddRef or _Release.
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
| Back to top |
|
 |
Rainer Krug Guest
|
Posted: Thu Nov 04, 2004 6:31 am Post subject: Re: How to notify changes from child object to parent object |
|
|
Thanks a lot Joanna for your help
Rainer
Joanna Carter (TeamB) wrote:
| Quote: | "Rainer Krug" <RMK_ (AT) _krugs_ (DOT) _de> a icrit dans le message de news:
[email]4188e843 (AT) newsgroups (DOT) borland.com[/email]...
In this case, I have to include QueryInterface as mentioned by you
in the example. For _AddRef and _Release, I have to use the basic
code for reference counting:
function TSubject.QueryInterface(const IID: TGUID; out Obj):
HResult; begin
if not IsEqualGUID(IID, ISubject) then
GetController.QueryInterface(IID, Obj);
end;
Derive both TSubject and your aggregating class from
TInterfacedObject and just redeclare QueryInterface in TSubject only;
then you don't need to do anything with _AddRef or _Release.
Joanna
--
Joanna Carter (TeamB)
Consultant Software Engineer
TeamBUG support for UK-BUG
TeamMM support for ModelMaker
|
|
|
| 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
|
|