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/

