From: Peter Morris |
|
Subject: Dynamic navigation for higher performance |
NewsGroup: borland.public.delphi.modeldrivenarchitecture.general |
Date Posted: 25-Apr-2003 at 17:15:45 PST |
Many times I started to read Rolf Lampa's article and it went over my head.
Now that I understand subscriptions, derived attributes, etc I decided to
reread it. I am so glad I did, it is absolutely brilliant!
If, like me, you read it when you was just getting started and found it a
bit deep then I suggest you go back and take another look now that you have
more experience. I will *definately* be implementing this sort of thing in
my own apps if ever the need arises.
http://www.howtodothings.com/showarticle.asp?article=455
--
Pete
=============
Read or write technical articles
http://www.HowToDoThings.com
Audio compression components, FastStrings library, DIB controls
http://www.DroopyEyes.com
|
From: Christel Rotter |
|
Subject: Re: Dynamic navigation for higher performance |
NewsGroup: borland.public.delphi.modeldrivenarchitecture.general |
Date Posted: 26-Apr-2003 at 11:9:48 PST |
I'm very impressed too!
This article answers many questions that I had before. Its logical structure
can help to understand many of the internal mechanism of "bold magic".
It shows how you can more and more standardize your development work
by putting more care on modeling process.
Standardized software development ! - Who can give an example that
compares with bold?
Christel Rotter
"Peter Morris" schrieb im Newsbeitrag
news:3ea95f99@newsgroups.borland.com...
> Many times I started to read Rolf Lampa's article and it went over my
head.
> Now that I understand subscriptions, derived attributes, etc I decided to
> reread it. I am so glad I did, it is absolutely brilliant!
>
> If, like me, you read it when you was just getting started and found it a
> bit deep then I suggest you go back and take another look now that you
have
> more experience. I will *definately* be implementing this sort of thing
in
> my own apps if ever the need arises.
>
> http://www.howtodothings.com/showarticle.asp?article=455
>
> --
>
> Pete
> =============
> Read or write technical articles
> http://www.HowToDoThings.com
>
> Audio compression components, FastStrings library, DIB controls
> http://www.DroopyEyes.com
>
>
|
From: David Farrell-Garcia |
|
Subject: Re: Dynamic navigation for higher performance |
NewsGroup: borland.public.delphi.modeldrivenarchitecture.general |
Date Posted: 28-Apr-2003 at 7:3:22 PST |
Bryan Crotaz wrote:
> Name me any other software in the world that gets faster as
> you run it!
Windows 98 gets to the reboot stage faster each time you run it. Does
that count? : - )
--
David Farrell-Garcia
Orca Software & Technologies
Posted With XanaNews 1.14.3.7
|
From: Rolf |
|
Subject: Re: Dynamic navigation for higher performance |
NewsGroup: borland.public.delphi.modeldrivenarchitecture.general |
Date Posted: 28-Apr-2003 at 11:19:38 PST |
Christophe Floury wrote:
> Rolf is a brilliant man that never cease to impress me.
> He's built one of the most advanced piece of software I have seen and has
> pushed many boundaries in the process.
> And even more interestingly, when I think of him, I consider his software
> achievements a very small part of his wide ranging interests.
>
> Christophe
Thank you Christophe and all. For all those warming words I owe you to share the code below (a complete unit). It's the result of many hours of tracking subscription bugs as a result of trying to optimize trivial code. And other bad experiences. The "access and subscription methods" may be of use for others too. The RIL Utility code pasted below is useful for :
1. Optimized read/subscribe of attributes links. SingleLinks are both subscribed to and "navigated" in the same operation. (same for Bool)
2. Ensuring correct subscriptions in derived links & attributes
3. Keeping coding "short and clean"
Try this replacing OCL expressions for deriveds in "hot spots" and notice the difference. The code is very much tested and verified. And very much used.
// rolf
unit RILUtils;
{ RIL = Rolf Ingvald Lampa :) }
interface
Uses
Classes,
BoldSystem,
BoldElements,
BoldAttributes,
BoldSubscription;
const
{ HasListContent methods are useful for efficient check of if a BoldObjectList
has any content. If it has, and IF it contains of list elements that are
not loaded into memory, it calls EnsureObjects on the list. For optimization
reasons both the Count parameter and the List variable (optional, overload)
can be used in a following for-loop. Note that this method is optimized in
that it DOES NOT ensure lists that are already ensured (=costly)- and when it
does it does it in a more efficient way than Bold does internally (i.e. calling
the costly operation EnsureObjects multiple times on already loaded lists
Caution - Don't use any of the out parameters if any of the functions returns
false ! }
{ list member support }
function HasListContent(const aBoldObjectList: TBoldobjectList): Boolean; overload;
function HasListContent(const aBoldObjectList: TBoldobjectList; out aCount: Integer): Boolean; overload;
function HasListContent(const aBoldObjectList: TBoldobjectList; out aCount: Integer; out aBoldObjectListResult): Boolean; overload;
function HasListContentAndSubscribe(const aBoldObjectList: TBoldobjectList; out aCount: Integer; const Subscriber: TBoldSubscriber): Boolean; overload;
function HasListContentAndSubscribe(const aBoldObjectList: TBoldobjectList; out aCount: Integer; const Subscriber: TBoldSubscriber; out aBoldObjectListResult): Boolean; overload;
function HasListContentIsEnsuredAndSubscribe(const aBoldObjectList: TBoldobjectList; out aCount: Integer; const Subscriber: TBoldSubscriber; out IsEnsured: Boolean): Boolean;
{ Use the SupportsListElem function in for-loops to ensure correct type of
a list element and, at the same time, also getting a variable result if
validation was ok }
function SupportsListElem(const aListElement: TBoldElement; const aClassType: TClass; out Obj): Boolean; overload;
function SupportsListElem(const aListElement: TBoldElement; const aClassType: TClass; out Obj; const StrongType: Boolean): Boolean; overload;
{ single link member support }
function SupportsRef(const aBoldObjectRef: TBoldObjectReference; const aClassType: TClass; out Obj): Boolean; overload;
function SupportsRef(const aBoldObjectRef: TBoldObjectReference; const aClassType: TClass; out Obj; const StrongType: Boolean): Boolean; overload;
function SupportsRefAndSubscribe(const aBoldObjectRef: TBoldObjectReference; const aClassType: TClass; out Obj; const Subscriber: TBoldSubscriber): Boolean; overload;
function SupportsRefAndSubscribe(const aBoldObjectRef: TBoldObjectReference; const aClassType: TClass; out Obj; const Subscriber: TBoldSubscriber; const StrongType: Boolean): Boolean; overload;
{ example of use:
var
Obj1: TYourClass1;
Obj2: TYourClass2;
Obj3: TYourClass3;
begin
if SupportsRefAndSubscribe(M_SingleLink1, TYourClass1, Obj1, Subscriber) and
SupportsRefAndSubscribe(Obj1.M_SingleLink2, TYourClass2, Obj2, Subscriber) and
SupportsRefAndSubscribe(Obj2.M_SingleLink3, TYourClass3, Obj3, Subscriber) then
M_StringResult.AsString := GetStringMemberValueAndSubscribe(Obj3.M_StringAttribute, Subscriber)
else
M_StringResult.AsString := '';
end;
}
{ cheapest possible check of if a reference is assigned - without loading the object ! }
function AssignedRefAndSubscribe(const aBoldObjectRef: TBoldObjectReference; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Boolean;
{ date member support }
function CopyDateMemberFromTo(const aSourceDate, aTargetDate: TBADate): Boolean; overload;
function CopyDateMemberFromTo(const aSourceDate, aTargetDate: TBADateTime): Boolean; overload;
function CopyDateMemberFromToAndSubscribe(const aSourceDate, aTargetDate: TBADate; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Boolean; overload;
function CopyDateMemberFromToAndSubscribe(const aSourceDate, aTargetDate: TBADateTime; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Boolean; overload;
function GetNotNullDate(const aDateMember: TBADateTime; var DateTimeResult: TDateTime): Boolean; overload;
function GetNotNullDate(const aDateMember: TBADate; var DateTimeResult: TDateTime): Boolean; overload;
function GetNotNullDateAndSubscribe(const aDateMember: TBADateTime; var DateTimeResult: TDateTime; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Boolean; overload;
function GetNotNullDateAndSubscribe(const aDateMember: TBADate; var DateTimeResult: TDateTime; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Boolean; overload;
{ bool member support }
function GetMemberValueAsStringAndSubscribe(const aBoldMember: TBoldAttribute; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): String;
function GetBoolMemberValueAndSubscribe(const aBoolMember: TBABoolean; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Boolean;
function GetStringMemberValueAndSubscribe(const aStringMember: TBAString; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): String;
function GetIntegerMemberValueAndSubscribe(const aIntegerMember: TBAInteger; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Integer;
function GetFloatMemberValueAndSubscribe(const aFloatMember: TBAFloat; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Double;
implementation
uses
BoldSystemRT,
Variants;
function HasListContentIsEnsuredAndSubscribe(const aBoldObjectList: TBoldobjectList; out aCount: Integer; const Subscriber: TBoldSubscriber; out IsEnsured: Boolean): Boolean;
begin
aBoldObjectList.DefaultSubscribe(Subscriber, breResubscribe);
{ Find out - in a very effecient way - wether the list is ensured or not }
aCount := aBoldObjectList.Count; // = loads locators only
Result := aCount>0;
IsEnsured := false;
if Result then
begin
IsEnsured := ((aCount=1) and (aBoldObjectList.Locators[0].BoldObject<>nil))
or
((aCount>1) and
(aBoldObjectList.Locators[0].BoldObject<>nil) and
(aBoldObjectList.Locators[1].BoldObject<>nil) and
(aBoldObjectList.Locators[aCount-2].BoldObject<>nil) and
(aBoldObjectList.Locators[aCount-1].BoldObject<>nil));
if not IsEnsured then
aBoldObjectList.EnsureObjects;
end;
end;
function HasListContent(const aBoldObjectList: TBoldobjectList): Boolean; overload;
var
Cnt: Integer;
begin
{ Find o
ut - in a very effecient way - wether the list is ensured or not }
Cnt := aBoldObjectList.Count; // = loads locators only
Result := Cnt>0;
if (Cnt>1) and
((aBoldObjectList.Locators[0].BoldObject=nil) or
(aBoldObjectList.Locators[Cnt-2].BoldObject=nil) or
(aBoldObjectList.Locators[Cnt-1].BoldObject=nil)) then
aBoldObjectList.EnsureObjects;
end;
function HasListContent(const aBoldObjectList: TBoldobjectList; out aCount: Integer): Boolean; overload;
begin
{ Find out - in a very effecient way - wether the list is ensured or not }
aCount := aBoldObjectList.Count; // = loads locators only
Result := aCount>0;
if (aCount>1) and
((aBoldObjectList.Locators[0].BoldObject=nil) or
(aBoldObjectList.Locators[aCount-2].BoldObject=nil) or
(aBoldObjectList.Locators[aCount-1].BoldObject=nil)) then
aBoldObjectList.EnsureObjects;
end;
function HasListContent(const aBoldObjectList: TBoldobjectList; out aCount: Integer; out aBoldObjectListResult): Boolean; overload;
begin
TBoldObjectList(aBoldObjectListResult) := aBoldObjectList;
{ Find out - in a very effecient way - wether the list is ensured or not }
aCount := aBoldObjectList.Count; // = loads locators only
Result := aCount>0;
if (aCount>1) and
((aBoldObjectList.Locators[0].BoldObject=nil) or
(aBoldObjectList.Locators[aCount-2].BoldObject=nil) or
(aBoldObjectList.Locators[aCount-1].BoldObject=nil)) then
aBoldObjectList.EnsureObjects;
end;
function HasListContentAndSubscribe(const aBoldObjectList: TBoldobjectList; out aCount: Integer; const Subscriber: TBoldSubscriber): Boolean; overload;
begin
aBoldObjectList.DefaultSubscribe(Subscriber, breResubscribe);
{ Find out - in a very effecient way - wether the list is ensured or not }
aCount := aBoldObjectList.Count; // = loads locators only
Result := aCount>0;
if (aCount>1) and
((aBoldObjectList.Locators[0].BoldObject=nil) or
(aBoldObjectList.Locators[aCount-2].BoldObject=nil) or
(aBoldObjectList.Locators[aCount-1].BoldObject=nil)) then
aBoldObjectList.EnsureObjects;
end;
function HasListContentAndSubscribe(const aBoldObjectList: TBoldObjectList; out aCount: Integer; const Subscriber: TBoldSubscriber; out aBoldObjectListResult): Boolean; overload;
begin
TBoldObjectList(aBoldObjectListResult) := aBoldObjectList;
aBoldObjectList.DefaultSubscribe(Subscriber, breResubscribe);
{ Find out - in a very efficient way - wether the list is ensured or not }
aCount := aBoldObjectList.Count; // = loads locators only
Result := aCount>0;
if (aCount>1) and
((aBoldObjectList.Locators[0].BoldObject=nil) or
(aBoldObjectList.Locators[aCount-2].BoldObject=nil) or
(aBoldObjectList.Locators[aCount-1].BoldObject=nil)) then
aBoldObjectList.EnsureObjects;
end;
function SupportsListElem(const aListElement: TBoldElement; const aClassType: TClass; out Obj): Boolean;
begin
Result := aListElement is aClassType;
if Result then
TBoldObject(Obj) := TBoldObject(aListElement);
end;
function SupportsListElem(const aListElement: TBoldElement; const aClassType: TClass; out Obj; const StrongType: Boolean): Boolean;
begin
if StrongType then
Result := aListElement.ClassType=aClassType
else
Result := aListElement is aClassType;
if Result then
TBoldObject(Obj) := TBoldObject(aListElement);
end;
function SupportsRef(const aBoldObjectRef: TBoldObjectReference; const aClassType: TClass; out Obj): Boolean;
begin
Assert(aBoldObjectRef<>nil, 'Invalid parameter - (aBoldObjectRef)');
Result := (aBoldObjectRef.Locator<>nil) and (aBoldObjectRef.Locator.EnsuredBoldObject is aClassType);
if Result then
TBoldObject(Obj) := aBoldObjectRef.BoldObject;
end;
function SupportsRef(const aBoldObjectRef: TBoldObjectReference; const aClassType: TClass; out Obj; const StrongType: Boolean): Boolean;
begin
Assert(aBoldObjectRef<>nil, 'Invalid parameter - (aBoldObjectRef)');
if not StrongType then
Result := (aBoldObjec
tRef.Locator<>nil) and (aBoldObjectRef.Locator.EnsuredBoldObject is aClassType)
else
Result := (aBoldObjectRef.Locator<>nil) and (aBoldObjectRef.Locator.EnsuredBoldObject.ClassType=aClassType);
if Result then
TBoldObject(Obj) := aBoldObjectRef.BoldObject;
end;
function SupportsRefAndSubscribe(const aBoldObjectRef: TBoldObjectReference; const aClassType: TClass; out Obj; const Subscriber: TBoldSubscriber): Boolean;
begin
Assert(aBoldObjectRef<>nil, 'Invalid parameter - (aBoldObjectRef)');
aBoldObjectRef.DefaultSubscribe(Subscriber, breResubscribe);
Result := (aBoldObjectRef.Locator<>nil) and (aBoldObjectRef.Locator.EnsuredBoldObject is aClassType);
if Result then
TBoldObject(Obj) := aBoldObjectRef.BoldObject;
end;
function SupportsRefAndSubscribe(const aBoldObjectRef: TBoldObjectReference; const aClassType: TClass; out Obj; const Subscriber: TBoldSubscriber; const StrongType: Boolean): Boolean;
begin
Assert(aBoldObjectRef<>nil, 'Invalid parameter - (aBoldObjectRef)');
aBoldObjectRef.DefaultSubscribe(Subscriber, breResubscribe);
if not StrongType then
Result := (aBoldObjectRef.Locator<>nil) and (aBoldObjectRef.Locator.EnsuredBoldObject is aClassType)
else
Result := (aBoldObjectRef.Locator<>nil) and (aBoldObjectRef.Locator.EnsuredBoldObject.ClassType=aClassType);
if Result then
TBoldObject(Obj) := aBoldObjectRef.BoldObject;
end;
function AssignedRefAndSubscribe(const aBoldObjectRef: TBoldObjectReference; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Boolean;
begin
Assert(aBoldObjectRef<>nil, 'Invalid parameter - (aBoldObjectRef)');
aBoldObjectRef.DefaultSubscribe(Subscriber, aBoldEvent);
Result := aBoldObjectRef.Locator<>nil;
end;
function GetMemberValueAsStringAndSubscribe(const aBoldMember: TBoldAttribute; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): String;
begin
aBoldMember.DefaultSubscribe(Subscriber, aBoldEvent);
Result := aBoldMember.AsString;
end;
function GetIntegerMemberValueAndSubscribe(const aIntegerMember: TBAInteger; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Integer;
begin
aIntegerMember.DefaultSubscribe(Subscriber, aBoldEvent);
Result := aIntegerMember.AsInteger;
end;
function GetFloatMemberValueAndSubscribe(const aFloatMember: TBAFloat; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Double;
begin
aFloatMember.DefaultSubscribe(Subscriber, aBoldEvent);
Result := aFloatMember.AsFloat;
end;
function GetStringMemberValueAndSubscribe(const aStringMember: TBAString; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): String;
begin
aStringMember.DefaultSubscribe(Subscriber, aBoldEvent);
Result := aStringMember.AsString;
end;
function GetBoolMemberValueAndSubscribe(const aBoolMember: TBABoolean; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Boolean;
begin
aBoolMember.DefaultSubscribe(Subscriber, aBoldEvent);
Result := aBoolMember.AsBoolean;
end;
function CopyDateMemberFromTo(const aSourceDate, aTargetDate: TBADate): Boolean; overload;
begin
Result := not aSourceDate.IsNull;
if Result then
aTargetDate.AsDate := aSourceDate.AsDate
else
aTargetDate.SetToNull;
end;
function CopyDateMemberFromTo(const aSourceDate, aTargetDate: TBADateTime): Boolean; overload;
begin
Result := not aSourceDate.IsNull;
if Result then
aTargetDate.AsDateTime := aSourceDate.AsDateTime
else
aTargetDate.SetToNull;
end;
function CopyDateMemberFromToAndSubscribe(const aSourceDate, aTargetDate: TBADateTime; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Boolean; overload;
begin
aSourceDate.DefaultSubscribe(Subscriber, aBoldEvent);
Result := CopyDateMemberFromTo(aSourceDate, aTargetDate);
end;
function CopyDateMemberFromToAndSubscribe(const aSourceDate, aTargetDate: TBADate; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Boolean; overload;
begin
aSourceDate.DefaultSubscribe(Subscriber, aBoldEvent);
Result := CopyDateMemberFromTo(aSourceDate, aTargetDate);
end;
function GetNotNullDate(const aDateMember: TBADateTime; var DateTimeResult: TDateTime): Boolean;
begin
Result := not aDateMember.IsNull;
if Result then
DateTimeResult := aDateMember.AsDateTime;
end;
function GetNotNullDate(const aDateMember: TBADate; var DateTimeResult: TDateTime): Boolean;
begin
Result := not aDateMember.IsNull;
if Result then
DateTimeResult := aDateMember.AsDate;
end;
function GetNotNullDateAndSubscribe(const aDateMember: TBADateTime; var DateTimeResult: TDateTime; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Boolean;
begin
aDateMember.DefaultSubscribe(Subscriber, aBoldEvent);
Result := not aDateMember.IsNull;
if Result then
DateTimeResult := aDateMember.AsDateTime;
end;
function GetNotNullDateAndSubscribe(const aDateMember: TBADate; var DateTimeResult: TDateTime; const Subscriber: TBoldSubscriber; const aBoldEvent: TBoldEvent = breReEvaluate): Boolean;
begin
aDateMember.DefaultSubscribe(Subscriber, aBoldEvent);
Result := not aDateMember.IsNull;
if Result then
DateTimeResult := aDateMember.AsDate;
end;
initialization
end.
|
From: Rolf |
|
Subject: Re: Dynamic navigation for higher performance (code fix...) |
NewsGroup: borland.public.delphi.modeldrivenarchitecture.general |
Date Posted: 28-Apr-2003 at 10:7:15 PST |
Oops, fixed the example code (mixing it all up in the example code when renaming back to old names compliant with the model in the article...)
Rolf wrote:
> Joe Otten wrote:
>
> > "Rolf" wrote in message
> >
> > but what about:
> >
> > var
> > LOOPER: TVehicleUnit;
> > begin
> > LOOPER := Self;
> > // Traverse ahead
> > LOOPER.M_hauler.DefaultSubscribe(Subscriber, breResubscribe);
> > if Assigned(LOOPER.Hauler) then
> > begin
> > M_CombinationFirst.BoldObject := LOOPER.Hauler.CombinationFirst;
> > LOOPER.Hauler.M_CombinationFirst.DefaultSubscribe(Subscriber);
> > end
> > else
> > M_CombinationFirst.BoldObject := Self;
> > end;
> >
> > The reason I ask is that I was a little suprised, as the second would seem
> > more in keeping with the philosophy of your article, in that in a 3 car
> > train, the 3rd car makes use of the a result cached by the second car.
> >
> > I guess the answer is that you are mostly dealing with 1 or 2 car
> > arrangements, and so yours is more efficient. The mathematician in me has an
> > instinctive preference for code which places O(n) subscriptions rather than
> > O(n^2) :)
> >
> > I wasn't quite sure what you meant by:
> >
> > // This link will be the fast "short cut" used by many many
> > // functions in this scope and other links and attributes, thus
> > // meaning optimization, not "extras" or "candy" in the model.
> >
> > Does this deal with my question?
> >
> > Joe
Yes & Yes.
You are perfectly right about how the code should be ! Thanks for noticing it in
the example code !
As a matter of fact I fixed this in my own code after I wrote the article but I forgot to update the article. You see, this is the down side of leaving redundant code behind here and there and everywere... :) The same principle goes for many other links in the scope, like for instance a link called "CombinationLoadItems" (was that one included in the article...?). Anyhow, se an example of that below if it wasn't in the article. The basic principle should always be exactly as you point out - All trailing vehicle units should (re)use the FrontMost unit's derived results regarding Combination links because it is always the cheapest to let one unit (the front most) do all the detailed deriving and then let all the other units benefit from the hard work already being done.
procedure TPlanMission._CombinationLoadItems_DeriveAndSubscribe(DerivedObject: TObject; Subscriber: TBoldSubscriber);
var
i, Cnt: Integer;
VehicleUnit: TPlanMission;
begin
CombinationLoadItems.Clear;
if M_Hauler<>nil then // reuse the front most vehicle units result
begin
CombinationFirst.DefaultSubscribe(Subscriber, breResubscribe);
CombinationFirst.CombinationLoadItems.DefaultSubscribe(Subscriber, breResubscribe);
M_CombinationLoadItems(CombinationFirst.CombinationLoadItems);
end
else // is front most
begin
CombinationUnits.DefaultSubscribe(Subscriber, breResubscribe);
Cnt := CombinationUnits.Count;
for i := 0 to Cnt-1 do // avoid touching "member shell's" more than nessesary
begin
VehicleUnit := CombinationUnits[i];
VehicleUnit.BatchItems.DefaultSubscribe(Subscriber, breResubscribe);
if VehicleUnit.BatchItems.Count>0 then
M_CombinationLoadItems.AddList(VehicleUnit.BatchItems);
end;
end;
end; // TPlanMission._CombinationLoadItems
>
> // Rolf
|
From: Rolf |
|
Subject: Re: Dynamic navigation for higher performance |
NewsGroup: borland.public.delphi.modeldrivenarchitecture.general |
Date Posted: 28-Apr-2003 at 9:55:45 PST |
Joe Otten wrote:
> "Rolf" wrote in message
>
> but what about:
>
> var
> LOOPER: TVehicleUnit;
> begin
> LOOPER := Self;
> // Traverse ahead
> LOOPER.M_hauler.DefaultSubscribe(Subscriber, breResubscribe);
> if Assigned(LOOPER.Hauler) then
> begin
> M_CombinationFirst := LOOPER.Hauler.CombinationFirst;
> LOOPER.Hauler.M_CombinationFirst.DefaultSubscribe(Subscriber);
> end
> else
> M_CombinationFirst.BoldObject := Self;
> end;
>
> The reason I ask is that I was a little suprised, as the second would seem
> more in keeping with the philosophy of your article, in that in a 3 car
> train, the 3rd car makes use of the a result cached by the second car.
>
> I guess the answer is that you are mostly dealing with 1 or 2 car
> arrangements, and so yours is more efficient. The mathematician in me has an
> instinctive preference for code which places O(n) subscriptions rather than
> O(n^2) :)
>
> I wasn't quite sure what you meant by:
>
> // This link will be the fast "short cut" used by many many
> // functions in this scope and other links and attributes, thus
> // meaning optimization, not "extras" or "candy" in the model.
>
> Does this deal with my question?
>
> Joe
Yes & Yes.
You are perfectly right about how the code should be ! Thanks for noticing it in
the example code !
As a matter of fact I fixed this in my own code after I wrote the article but I forgot to update the article. You see, this is the down side of leaving redundant code behind here and there and everywere... :) The same principle goes for many other links in the scope, like for instance a link called "CombinationLoadItems" (was that one included in the article...?). Anyhow, se an example of that below if it wasn't in the article. The basic principle should always be exactly as you point out - All trailing vehicle units should (re)use the FrontMost unit's derived results regarding Combination links because it is always the cheapest to let one unit (the front most) do all the detailed deriving and then let all the other units benefit from the hard work already being done.
procedure TPlanMission._CombinationLoadItems_DeriveAndSubscribe(DerivedObject: TObject; Subscriber: TBoldSubscriber);
var
i, Cnt: Integer;
HaulerObj: TPlanMission;
begin
CombinationLoadItems.Clear;
if M_Hauler<>nil then // reuse the front most vehicle units result
begin
HaulerObj.CombinationParcels.DefaultSubscribe(Subscriber, breResubscribe);
M_CombinationLoadItems(Hauler.CombinationParcels);
end
else // is front most
begin
CombinationUnits.DefaultSubscribe(Subscriber, breResubscribe);
Cnt := CombinationUnits.Count;
for i := 0 to Cnt-1 do // avoid touching "member shell's" more than nessesary
begin
HaulerObj := CombinationUnits[i];
HaulerObj.BatchItems.DefaultSubscribe(Subscriber, breResubscribe);
if HaulerObj.BatchItems.Count>0 then
M_CombinationLoadItems.AddList(HaulerObj.BatchItems);
end;
end;
end; // TPlanMission._CombinationLoadItems
// Rolf
|
From: Joe Otten |
|
Subject: Re: Dynamic navigation for higher performance |
NewsGroup: borland.public.delphi.modeldrivenarchitecture.general |
Date Posted: 27-Apr-2003 at 21:34:7 PST |
"Rolf" wrote in message
news:3EAB9CA0.8ED8DA8C@rilnet.com...
>
> Glad you liked it.
>
> // rolf
Great article. Just curious about something. You wrote:
procedure TVehicleUnit._CombinationFirst_DeriveAndSubscribe(...);
var
LOOPER: TVehicleUnit;
begin
LOOPER := Self;
// Traverse ahead
LOOPER.M_hauler.DefaultSubscribe(Subscriber, breResubscribe);
while Assigned(LOOPER.Hauler) do
begin
LOOPER := LOOPER.Hauler;
LOOPER.M_hauler.DefaultSubscribe(Subscriber, breResubscribe);
end;
M_CombinationFirst.BoldObject := LOOPER;
end;
but what about:
var
LOOPER: TVehicleUnit;
begin
LOOPER := Self;
// Traverse ahead
LOOPER.M_hauler.DefaultSubscribe(Subscriber, breResubscribe);
if Assigned(LOOPER.Hauler) then
begin
M_CombinationFirst := LOOPER.Hauler.CombinationFirst;
LOOPER.Hauler.M_CombinationFirst.DefaultSubscribe(Subscriber);
end
else
M_CombinationFirst.BoldObject := Self;
end;
The reason I ask is that I was a little suprised, as the second would seem
more in keeping with the philosophy of your article, in that in a 3 car
train, the 3rd car makes use of the a result cached by the second car.
I guess the answer is that you are mostly dealing with 1 or 2 car
arrangements, and so yours is more efficient. The mathematician in me has an
instinctive preference for code which places O(n) subscriptions rather than
O(n^2) :)
I wasn't quite sure what you meant by:
// This link will be the fast "short cut" used by many many
// functions in this scope and other links and attributes, thus
// meaning optimization, not "extras" or "candy" in the model.
Does this deal with my question?
Joe
|
From: Christophe Floury |
|
Subject: Re: Dynamic navigation for higher performance |
NewsGroup: borland.public.delphi.modeldrivenarchitecture.general |
Date Posted: 27-Apr-2003 at 15:0:40 PST |
Rolf is a brilliant man that never cease to impress me.
He's built one of the most advanced piece of software I have seen and has
pushed many boundaries in the process.
And even more interestingly, when I think of him, I consider his software
achievements a very small part of his wide ranging interests.
Christophe
|
From: Rolf |
|
Subject: Re: Dynamic navigation for higher performance |
NewsGroup: borland.public.delphi.modeldrivenarchitecture.general |
Date Posted: 27-Apr-2003 at 11:2:24 PST |
Glad you liked it.
// rolf
Peter Morris wrote:
> Many times I started to read Rolf Lampa's article and it went over my head.
> Now that I understand subscriptions, derived attributes, etc I decided to
> reread it. I am so glad I did, it is absolutely brilliant!
>
> If, like me, you read it when you was just getting started and found it a
> bit deep then I suggest you go back and take another look now that you have
> more experience. I will *definately* be implementing this sort of thing in
> my own apps if ever the need arises.
>
> http://www.howtodothings.com/showarticle.asp?article=455
>
> --
>
> Pete
> =============
> Read or write technical articles
> http://www.HowToDoThings.com
>
> Audio compression components, FastStrings library, DIB controls
> http://www.DroopyEyes.com
|
From: Bryan Crotaz |
|
Subject: Re: Dynamic navigation for higher performance |
NewsGroup: borland.public.delphi.modeldrivenarchitecture.general |
Date Posted: 26-Apr-2003 at 17:18:27 PST |
And his system runs on a single server, in a single process too. He talks
about it having a "warm-up" time as the derived relations and attributes
gradually all get cached. As you use it more after a reboot, it gets faster
and faster! Name me any other software in the world that gets *faster* as
you run it!
bryan
"Peter Morris" wrote in message
news:3ea95f99@newsgroups.borland.com...
> Many times I started to read Rolf Lampa's article and it went over my
head.
> Now that I understand subscriptions, derived attributes, etc I decided to
> reread it. I am so glad I did, it is absolutely brilliant!
>
> If, like me, you read it when you was just getting started and found it a
> bit deep then I suggest you go back and take another look now that you
have
> more experience. I will *definately* be implementing this sort of thing
in
> my own apps if ever the need arises.
>
> http://www.howtodothings.com/showarticle.asp?article=455
>
> --
>
> Pete
> =============
> Read or write technical articles
> http://www.HowToDoThings.com
>
> Audio compression components, FastStrings library, DIB controls
> http://www.DroopyEyes.com
>
>
|
|
|