3

我有一个用于对节点树(VirtualTreeView)中的节点进行排序的过程 从 FMM4 报告中提取的所有内存泄漏都存储在 TMemoryLeakList 类的对象中(这些是我要排序的列表),它们存储在列表中称为 TGroupedMemoryLeakList,并且 TMLL 和 TGMLL 都扩展了 TObjectList。如果我想保持能够在升序和降序排序之间进行选择以及在四种不同数据类型中的一种排序之间进行选择的功能,我“必须”实现八种不同的比较方法(4 种排序类型 * 2 种排序方向)我继续主排序方法,因为我的 TMLL 列表扩展了 TObjectList。主要的排序方法如下所示

fSortType 和 fSortDirection 字段的值是从 GUI 组合框获取的。这八个通用比较函数之一看起来像这样。剩下的七个是这个的复制/粘贴变体。

有没有什么合理的方法可以重构大量的复制粘贴代码,并且仍然保留选择特定排序类型和方向的功能?

4

3 回答 3

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,在这种情况下,您可以引用所有权列表及其排序方向和排序类型,以便在单个比较例程中使用。

于 2012-10-01T11:40:41.990 回答
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, ...

然后,您将从该数组常量中填充工作指针

于 2012-10-01T10:01:05.950 回答
0

这是我的解决方案。除了完全重写这两个过程之外,我还在我的 TMemoryLeakList 类中添加了两个“静态”变量,并删除了以前的同名实例变量。这样,排序函数就可以全局访问它们。

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,fSortDirectionTMemoryLeakList但似乎不可能将成员函数传递给继承的排序函数(来自TObjectList),或者是吗?

于 2012-10-01T14:30:10.983 回答