你会如何重构这段代码?

时间:2010-05-07 16:10:30

标签: delphi design-patterns oop refactoring

这个假设的例子说明了我似乎无法通过的几个问题,即使我一直在努力! ...假设原始代码是一个长事件处理程序,在UI中编码,当用户单击网格中的单元格时触发。表示为伪代码:

if Condition1=true then
begin
  //loop through every cell in row, 
  //if aCell/headerCellValue>1 then
  //color aCell red
end
else if Condition2=true then
begin
  //do some other calculation adding cell and headerCell values, and
  //if some other product>2 then
  //color the whole row green
end
else show an error message

我看着这个并说“啊,重构战略模式!代码将更容易理解,更容易调试,以后更容易扩展!”我知道了。

我可以轻松地将代码分解为多个程序。

问题最终与范围有关。假设伪代码广泛使用网格属性,单元格中显示的值,甚至可能是内置网格方法。如何在不引用UI中的网格组件的情况下将所有这些移动到另一个单元 - 这会破坏松散耦合的所有“规则”,使OOP有价值? ......

我真的很期待回应。谢谢,一如既往 - Al C.

4 个答案:

答案 0 :(得分:8)

重构将代码放入单独的例程并不一定意味着将所有内容解耦。您也可以将每个案例重构为与您正在重构的事件处理程序属于同一个类的新方法。这些方法将具有与当前代码已有的网格组件相同的访问权限。

您正在编写事件的代码,以便在 表单上 网格。您是否真的预见到需要针对其他事件进行这些操作?或者在其他形式的其他网格上执行它们?如果没有,那么将所有内容脱钩只是一个学术练习,对您的产品没有任何意义。编写特定于应用程序的代码是可以的。

如果要解耦,那么执行此操作的方法是将参数添加到分解的例程中。如果例程需要使用网格而不确切知道它是哪个网格,那么将网格作为参数传递:

if Condition1 then
  ColorCellsRedAboveRatio(Grid, 1.0)
else if Condition2 then
  ColorRowsGreenAboveProduct(Grid, 2)
else
  Error;

答案 1 :(得分:2)

首先,永远不要将值与true进行比较。 这是

if Condition1 then
begin
end else if Condition2 then
begin
end;

即使值 为真,在最坏的情况下,与true相比也会失败。在Delphi-PRAXiS.net社区论坛中有一篇很好的(但德语)文章'ÜberdenUmgang mit Boolean',它展示了一个重现这种看似奇怪的行为的例子。

直接针对您的问题:

此代码在自定义绘制事件中会更好。这将被调用每个单元格并直接用正确的颜色绘制它。即使重新粉刷,它也能以正确的颜色绘制。在你的情况下,你只会绘制一次细胞,如果 - 由于任何原因 - 网格重新绘制你的颜色将丢失。

然后,毕竟,这段代码是字符串UI和组件相关的。如果您的表单上没有此网格,则不需要此代码。 你可以做些什么来解决一些问题,就是将从网格行检索到的值传递给外部单元,外部单元只进行计算并返回逻辑结果。然后,表单上的UI代码将获取此结果,并且必须决定如何显示它(即,什么颜色等)并将信息放在网格上。

答案 2 :(得分:0)

您可以考虑提取UI依赖单元。如果方法很长并且你想从类中提取一些东西,那么提取策略是合理的。

Rob建议您可以将所需的上下文传递给策略程序。您可以使用适当的抽象方法和适当的SubClasses引入SheetRenderingStrategy,例如。 HighlightTheseSpecialCellsStrategy。这些类仍然是UI的一部分,但可能澄清了意图并改进了模块化。

答案 3 :(得分:0)

在没有上下文的情况下回答有关重构的问题,就像回答有关设计的问题一样,没有上下文。因为你正在改变一个设计。我通常从“重构的理由”开始。也许我有超出的指标。 (看,一个有10,000行的类,当然它可以分成几个更连贯,更有凝聚力的类,耦合更少,内聚更紧密)。

所以,如果你发现自己有很多代码在事件处理程序中有几百个if ... else条件,就像我经常做的那样,我会忘记那个事件处理程序一分钟, 并按照上述人的说法将其减少到最小的面向对象模式:

如果是的话    DOA(更少的,参数) 否则如果b那么    DOB(是,一般地,较好的) 否则,如果c    DOC; ...

现在,如果doA,doB和doC属于另一个对象(它们共享状态,并修改/控制某些特定的字段集),那么我可能会将doA,doB,doC方法移动到另一个对象中。

但是,一般情况下,我不是深入了解事件处理程序执行所有操作的个别情况,而是还可以找到以下delphi模式:

procedure TForm1.BigGuiControlRightButtonClick(Sender;...);
begin
     BigThingController.RightClickMenuHandler(Sender, ....)
end;

procedure TForm1.BigGuiControlDoSomeThing(Sender:TObject);
begin
    BigThingController.DoSomeThing;
end;

procedure TForm1.Print(Sender);
begin
     DocumentManager.Print(Document);
end;

当我的TForm方法清晰可读时,我喜欢它。我不喜欢看到很多噪音,还有很多错误检查代码。我发现经过多年精心维护和调试的应用程序往往会逐渐变成一堆不可读的意大利面。如果重构的目标不仅仅是使代码看起来很漂亮,那么重构也应该有一些可衡量的质量目标。减少缺陷,崩溃等。有时,我使用重构作为时间来删除不再有用或以错误方式实现的功能。因此,当我完成时,我的代码更正确,而不仅仅是重构以适应代码应该如何编写的理想,这不会改变用户体验的质量。我是delphi开发人员,我是面向目标,质量导向,务实而不是造型器。其他人可能会有所不同。