我有一个在节点树中排序节点的过程(VirtualTreeView) 从FMM4报告中提取的所有内存泄漏都存储在类TMemoryLeakList(这些是我要排序的列表)的对象中,这些对象存储在名为TGroupedMemoryLeakList的列表列表中,并且TMLL和TGMLL都扩展为TObjectList。如果我想保持能够在升序和降序排序之间进行选择并在四种不同数据类型之一之间进行选择的功能,我必须'实现八种不同的比较方法(4种排序类型* 2种排序方向)我转到主排序方法,因为我的TMLL列表扩展了TObjectList。主要排序方法类似于this。
从GUI组合框中获取字段fSortType和fSortDirection的值。 这八个通用比较函数中的一个看起来像this。 剩下的七个是这一个的复制/粘贴变体。
是否有任何合理的方法可以重构大量的复制粘贴代码并仍保留选择特定排序类型和方向的功能?
答案 0 :(得分:5)
关于重构的好问题,但我不喜欢你可能正在寻找的答案。一些额外的代码行或一些额外的例程没有任何问题。尤其是后者,在这种情况下,命名主动有助于提高可读性。
我的建议是:保留设计,但缩短代码:
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;
你不能让它小得多,然后保持相同的可读性水平。
当然,您可以按照Arioch 'The的建议重写Sort
例程:
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;
但那么:为什么不重写QuickSort例程以消除对单独的比较例程的需要?
或者,您可以向TMemoryLeak添加所有权,在这种情况下,您可以引用拥有列表及其排序方向和排序类型,以便在单个比较例程中使用。
答案 1 :(得分:3)
使用函数指针。
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;
然后你的GUI元素应该像
一样 case ListSortType.ItemIndex of
itemBySzie : comparator1 := sortBySizeProcAsc;
....
end;
DoNewSort;
PS:确保在用户第一次点击任何GUI元素之前正确指定了这些指针;
PPS:您可以进一步重新排列
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, ...
然后你将填充该数组常量的工作指针
答案 2 :(得分:0)
这是我的解决方案。除了完全重写这两个过程外,我还在TMemoryLeakList类中添加了两个“静态”变量,并删除了同名的前一个实例变量。这样,Sort函数可以全局访问它们。
TMemoryLeakList=class(TObjectList)
class var fSortType :TMlSortType;
class var fSortDirection :TMLSortDirection;
...
end
procedure TMemoryLeakList.Sort;
begin
inherited sort(sortBySomethingSomething);
end;
function sortBySomethingSomething(Item1, Item2: Pointer): Integer;
var
a, b : string;
ret : Integer;
begin
ret := 1;
if(TMemoryLeakList.fSortDirection = sdAsc) then
ret := -1;
case TMemoryLeakList.fSortType of stSize:
begin
a := IntToStr(TMemoryLeak(Item1).Size);
b := IntToStr(TmemoryLeak(Item2).Size);
end;
end;
case TMemoryLeakList.fSortType of stClassName:
begin
a := TMemoryLeak(Item1).ClassName;
b := TMemoryLeak(Item2).ClassName;
end;
end;
case TMemoryLeakList.fSortType of stID:
begin
a := IntToStr(TMemoryLeak(Item1).ID);
b := IntToStr(TMemoryLeak(Item2).ID);
end;
end;
case TMemoryLeakList.fSortType of stCallStackSize:
begin
a := IntToStr(TMemoryLeak(Item1).CallStack.Count);
b := IntToStr(TMemoryLeak(Item2).CallStack.Count);
end;
end;
//...jos tu
if a=b then
Result:=0
else if a>b then
Result:=-1*ret
else if a<b then
Result:=1*ret;
end;
我想重写此解决方案,以便在fSortType,fSortDirection
中使用实例有界变量TMemoryLeakList
,但似乎无法将成员函数传递给继承的排序函数(来自TObjectList
),或者是吗?