MEGA Search
20.3 Million


Sign Up
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
>
>