实现TObjectList的排序,无需复制/粘贴代码

时间:2012-10-01 09:53:47

标签: delphi sorting refactoring

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

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

是否有任何合理的方法可以重构大量的复制粘贴代码并仍保留选择特定排序类型和方向的功能?

3 个答案:

答案 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 ),或者是吗?