Hi again :-)

Rather interesting thread this has turned into.  Rather interesting code as 
well you posted.  :-)   Anyway, I just wanted to add a few more bits n 
bobs...

----- Original Message ----- 
From: "Rob Kennedy" <[EMAIL PROTECTED]>
To: <[email protected]>
Sent: Saturday, June 04, 2005 7:56 PM
Subject: [list] Re: [delphi-en] TObjectList.Sort issues


>> Are there advantages to it over Walter's?
>
> My code will work with whatever TList you have. You can create sorting
> callbacks for use with TList, TObjectList, TClassList, TComponentList,
> TThreadList, and whatever else you find that expects a TListSortCompare
> function pointer.
>
> Walter's, on the other hand, is specific to TObjectList. If you wanted
> to sort a TList with a method pointer, you'd need to make a TList
> descendant that had the same special Sort replacement as is already in
> TObjectListEx. Likewise for TComponentList and TClassList. We can't add
> that capability to _all_ of TList's descendants without modifying TList
> itself.

Your point reminded me of something I again read about recently, anyway, a 
principle that states that you should generally "favour composition over 
inheritance."  There are various reasons for this principle, one of which is 
probably the statisticity of the class hierarchy at runtime (whereas with 
composition things may still be modified etc.)  (For those interested in 
this principle - Googling for the phrase turns up lots of hits.)  Anyway, 
from the viewpoint of this principle my solution is therefore less 
favourable because it didn't favour composition over ineheritance - I didn't 
properly consider it at all actually... .

Following on from this (and having asked myself what favouring composition 
would've suggested in this problem),  the other thing that then ocurred to 
me was that actually there _is_ a way to "add  the Sort replacement to _all_ 
of TList's descendants without changing TList's source..."   essentially by 
using composition :-), in the form of the so called "Decorator pattern" 
(otherwise known as a wrapper.)   The idea is that instead of making a 
descendant class from TObjectList, we instead create a decorator class that 
can be attached to TList (or any of it's descendants), with the added sort 
behaviour.  Normally a decorator would  support the same general interface 
as the class it's decorating (apart from the additional functionality) so 
that it can be used in place of the class being wrapped in client code.

Anyway, in this way we can thus arbitrarily and dynamically add the extra 
sort behaviour to any TList descendant, without requiring to modify TList's 
source...  There are of course cons to this approach as well (One would be 
that especially in this problem context using Delphi, depending on how you 
wrote the decorator, it might not (probably will not) be assignment 
compatible in contexts where you'd possibly want it to be.   If a pure IList 
interface had been defined that TList implemented and everything else used, 
then this would be far less of a problem, but I digress.)

Anyway, I did a quick hack update to my previous suggestion source - here 
then is a TListDecorator class that is can be attached to any TList 
descendant (including TObjectList) as shown to add "object method sort" 
ability...  Note though: I cheat - I do *not* reimplement/surface a complete 
TList compatible interface in the wrapper, instead I just surface the 
wrapped list as a property.  In the form code I also maintain references to 
both the wrapped list and the wrapper.  Thus, this example is perhaps a bit 
ugly and isn't really in the "spirit" of how it should be for a proper 
wrapper, but even so, (returning to the actual problem) this solution to 
circumventing the sort function vs. method issue is still IMHO far less 
awkward than the original jiggery pokery that Dave wanted to improve I 
think.

Cheers

Walter

==========================
unit uListDecorator;

{ 04/06/2005: TList only supports object comparisons via a pure function 
pointer.
  Sometimes this is insufficient.  The TListDecorator class is a simple 
decorator class
  (ie a wrapper that supports the same methods as the class it wraps) for 
TList
  that adds this sort ability. HOWEVER, I cheat by NOT reproducing the 
entire TList interface
  and forwarding all the calls transparently to the wrapped TList as would 
be implied
  by a "proper" wrapper/decorator, but by simply surfacing the wrapped TList 
as a
  property on the "wrapper implementation".  Taking this shortcut keeps this
  class simple and avoids all the delegation methods that would otherwise be
  necessary, but means the code that uses this code must now properly "know" 
that
  it's using the wrapper and must explicitly either use an independent 
reference
  to the list being wrapped, or access it via the WrappedList property.

  Anyway, basically: If you want to be able to use a _method_ for a 
comparison function
  in a TList descendant, then simply instantiate one of these with the 
target list
  as the constructor parameter, and then use the wrapper's Sort method to do 
the sort...}

interface

uses Classes;

type
  TSortCompareMethod = function (Ptr, Ptr2 : Pointer) : Integer of Object;
  TListDecorator = class
  private
    FWrappedList : TList;
    procedure QuickSort(L, R: Integer; SCompare: TSortCompareMethod);
  public
    constructor Create(AListToWrap : TList);
    procedure Sort(CompareMethod: TSortCompareMethod);

    property WrappedList : TList read FWrappedList;
  end;

implementation

{ TObjectListEx }

constructor TListDecorator.Create(AListToWrap: TList);
begin
  FWrappedList := AListToWrap;
end;

procedure  TListDecorator.QuickSort(L, R: Integer;
  SCompare: TSortCompareMethod);
var
  I, J: Integer;
  P, T: Pointer;
begin
  { 04/06/2005: This method is based on QuickSort procedure from 
Classes.pas, (c) Borland Software Corp.
    modified to be part of TObjectListEx class.  It implements the standard 
quicksort algorithm,
    delegating comparison operation to an object method.  The Borland 
version delegates to a pure function
    pointer, which is problematic in some cases.}
  repeat
    I := L;
    J := R;
    P := FWrappedList.List^[(L + R) shr 1];
    repeat
      while SCompare(FWrappedList.List^[I], P) < 0 do
        Inc(I);
      while SCompare(FWrappedList.List^[J], P) > 0 do
        Dec(J);
      if I <= J then
      begin
        T := FWrappedList.List^[I];
        FWrappedList.List^[I] := FWrappedList.List^[J];
        FWrappedList.List^[J] := T;
        Inc(I);
        Dec(J);
      end;
    until I > J;
    if L < J then
      QuickSort(L, J, SCompare);
    L := I;
  until I >= R;
end;

procedure TListDecorator.Sort(CompareMethod: TSortCompareMethod);
begin
  if (FWrappedList.List <> nil) and (FWrappedList.Count > 0) then
    QuickSort(0, FWrappedList.Count - 1, CompareMethod);
end;

end.

===================================
unit uTestForm;

{Demo/Test form for TListDecorator}

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, CheckLst, Contnrs, uListDecorator;

type
  // a little class used for demo/testing
  TSortObj = class
    Name : String;
    constructor Create(AName : String);
  end;

  TObjectListExTestForm = class(TForm)
    Memo: TMemo;
    SortButton: TButton;
    PopulateMemoButton: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure SortButtonClick(Sender: TObject);
    procedure PopulateMemoButtonClick(Sender: TObject);
  private
    { Private declarations }
    FObjectlist : TObjectList;
    FListDecorator : TListDecorator;
    function MyCompareMethod(Ptr1, Ptr2 : Pointer) : Integer;
  public
    { Public declarations }
  end;

var
  ObjectListExTestForm: TObjectListExTestForm;

implementation

{$R *.dfm}

{ TSortObj }

constructor TSortObj.Create(AName: String);
begin
  Name := AName;
end;

{ TObjectListExTestForm }

procedure TObjectListExTestForm.FormCreate(Sender: TObject);
begin
  FObjectList := TObjectList.Create;
  FListDecorator := TListDecorator.Create(FObjectList);

  FObjectList.OwnsObjects := True;
  FObjectList.Add(TSortObj.Create('John'));
  FObjectList.Add(TSortObj.Create('Peter'));
  FObjectList.Add(TSortObj.Create('Zelda'));
  FObjectList.Add(TSortObj.Create('Amelie'));
end;

procedure TObjectListExTestForm.FormDestroy(Sender: TObject);
begin
  FListDecorator.Free;
  FObjectlist.Free;
end;

procedure TObjectListExTestForm.SortButtonClick(Sender: TObject);
begin
  // Sort the list using the deocrator.  We could also create, use and 
destroy the decorator entirely in this method
  // if we wanted to, and not have it hanging about concurrently with the 
list all the time.
  FListDecorator.Sort(MyCompareMethod);
end;

function TObjectListExTestForm.MyCompareMethod(Ptr1, Ptr2 : Pointer) : 
Integer;
var
  SortObj1, SortObj2 : TSortObj;
begin
  SortObj1 := TSortObj(Ptr1);
  SortObj2 := TSortObj(Ptr2);
  result := CompareStr(SortObj1.Name, SortObj2.Name);
end;

procedure TObjectListExTestForm.PopulateMemoButtonClick(Sender: TObject);
var
  ObjIndex : Integer;
begin
  Memo.Lines.Clear;
  for ObjIndex := 0 to FObjectlist.Count -1 do
    Memo.Lines.Add(TSortObj(FObjectList[ObjIndex]).Name);
end;

end.





-----------------------------------------------------
Home page: http://groups.yahoo.com/group/delphi-en/
To unsubscribe: [EMAIL PROTECTED] 
Yahoo! Groups Links

<*> To visit your group on the web, go to:
    http://groups.yahoo.com/group/delphi-en/

<*> To unsubscribe from this group, send an email to:
    [EMAIL PROTECTED]

<*> Your use of Yahoo! Groups is subject to:
    http://docs.yahoo.com/info/terms/
 


Reply via email to