I have a procedure for sorting nodes in a node tree (VirtualTreeView) All memory leaks, extracted from FMM4 report, are stored in objects of a class TMemoryLeakList(these are the list I want to sort), which are stored in a list of lists called TGroupedMemoryLeakList, and both TMLL and TGMLL extend TObjectList. If I want to keep the functionality of being able to chose between ascending and descending sort order and choosing between sorting by one of four different data types, I 'have to' implement EIGHT different comparison methods (4 sort types * 2 sort directions) which I pass on to the main sorting method, because my TMLL list extends TObjectList. The main sorting method look like this.
The values for the fields fSortType and fSortDirection are acquired from the GUI comboboxes. One of those eight generic comparison functions looks like this. The seven remaining are copy/pasted variations of this one.
Is there any rational way to refactor this huge amount of copy paste code and still keep the functionality of choosing a specific sort type and direction?
Nice question about refactoring, but I dislike the answer you presumably are looking for. There is nothing wrong with a few extra lines of code, or a few extra routines. Especially the latter in which case naming actively assist in more readability.
My advice would be: leave the design as you have, but shorten the code:
function CompareSizeAsc(Item1, Item2: Pointer): Integer;
begin
Result := TMemoryLeak(Item2).Size - TMemoryLeak(Item1).Size;
end;
function CompareSizeDesc(Item1, Item2: Pointer): Integer;
begin
Result := TMemoryLeak(Item1).Size - TMemoryLeak(Item2).Size;
end;
function CompareClassNameAsc(Item1, Item2: Pointer): Integer;
begin
Result := CompareStr(TMemoryLeak(Item1).ClassName,
TMemoryLeak(Item2).ClassName);
end;
procedure TMemoryLeakList.Sort;
begin
case FSortDirection of
sdAsc:
case FSortType of
stSize: inherited Sort(CompareSizeAsc);
stClassName: inherited Sort(CompareClassNameAsc);
stCallStackSize: inherited Sort(CompareCallStackSizeAsc);
stId: inherited Sort(CompareIdAsc);
end;
sdDesc:
case FSortType of
stSize: inherited Sort(CompareSizeDesc);
stClassName: inherited Sort(CompareClassNameDesc);
stCallStackSize: inherited Sort(CompareCallStackSizeDesc);
stId: inherited Sort(CompareIdDesc);
end;
end;
end;
You can't get it much smaller then this ánd preserve the same level of readability.
Of course, you could rewrite the Sort routine as suggested by Arioch 'The:
procedure TMemoryLeakList.Sort;
const
Compares: array[TSortDirection, TSortType] of TListSortCompare =
((CompareSizeAsc, CompareClassNameAsc, CompareCallStackSizeAsc,
CompareIdAsc), (CompareSizeDesc, CompareClassNameDesc,
CompareCallStackSizeDesc, CompareIdDesc));
begin
inherited Sort(Compares[FSortDirection, FSortType]);
end;
But then: why not rewrite the QuickSort routine to eliminate the need for separate compare routines?
Alternatively, you could add ownership to TMemoryLeak in which case you have a reference to the owning list and its sort direction and sort type, for use within óne single compare routine.
Use function pointers.
var comparator1, comparator2: function (Item1, Item2: Pointer): Integer;
function sortComplex (Item1, Item2: Pointer): Integer;
begin
Result := comparator1(Item1, Item2);
if 0 = Result then Result := comparator2(Item1, Item2);
end;
Then you GUI elements should behave like
case ListSortType.ItemIndex of
itemBySzie : comparator1 := sortBySizeProcAsc;
....
end;
DoNewSort;
PS: make sure that you correctly specify those pointers even before user 1st click any GUI element;
PPS: you can rearrange this even further like
type t_criteria = (bySize, byName,...);
t_comparators = array[t_criteria] of array [boolean {Descending?}]
of function (Item1, Item2: Pointer): Integer;
const comparator1table: t_comparators =
( {bySize} ( {false} sortBySizeProcAsc, {true} sortBySizeProcDesc),
{byName} ( {false} sortByNameProcAsc, ...
Then you would fill working pointers from that array constants
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With