如何识别短代码块可以重构为更干净的东西?

时间:2010-01-23 03:04:49

标签: language-agnostic refactoring code-cleanup

我有几个星期前编写的代码(代码的目的并不像它的结构那么重要):

if (_image.Empty)
{
   //Use the true image size if they haven't specified a custom size
   if (_glyphSize.Width > 0)
      imageSize.Width = _glyphSize.Width //override
   else
      imageSize.Width = _image.GetWidth;

   if (_glyphSize.Height > 0) then
      imageSize.Height = _glyphSize.Height
   else
      imageSize.Height = _image.GetHeight
}
else
{
   //No image, but they can still override it with a custom size
   if (_glyphSize.Width > 0) then
      imageSize.Width = _glyphSize.Width
   else
      imageSize.Width = 0;

   if (_glyphSize.Height > 0)
      imageSize.Height = _glyphSize.Height
   else
      imageSize.Height := 0;
}

今晚我正在检查它,当我清理它时,我意识到清洁版本必须更加简洁:

//Figure out the final image width
if (_glyphSize.Width > 0)
   imageSize.Width = _glyphSize.Width
else if (not _glyph.Empty)
   imageSize.Width = _glyph.GetWidth
else
   imageSize.Width = 0;

//Figure out the final image height
if (_glyphSize.Height > 0)
   imageSize.Height = _glyphSize.Height
else if (not _glyph.Empty)
   imageSize.Height = _glyph.GetHeight
else
   imageSize.Height = 0;

注意:我已经将代码修改为裸逻辑流,并且使用了源代码语言。

最后,我采用了嵌套的if,然后将它们倒转。这样做可以缩短时间。我的问题是:我将来如何认识到这一点?

有什么迹象表明我刚刚编写了一些可以重构为更短的代码?


几个星期前的另一个例子是类似于权限检查:用户可以执行操作:

  • 如果他们有权限,他们可以这样做
  • 如果他们没有权限,但覆盖有效

我最初编码为:

if ((HasPermission || (!HasPermission and OverrideEnabled))
{
   ...do stuff
}

if条款的逻辑条件似乎有点罗嗦。我试图回到我的布尔代数课程,找出如何简化它。最后我可以做到,所以我最终绘制了一个真值表:

Permission  Override   Result
    0           0        0
    0           1        1
    1           0        1
    1           1        1

当我看到它是 OR 操作时。所以我的if声明成了:

if (HasPermission  or OverrideEnabled)
{
   ...
}

这是显而易见的。所以现在我想知道我怎么也看不到这一点。


这让我回到了我的问题:为了识别某些代码块需要一些TLC,我可以/应该寻找什么样的迹象?

4 个答案:

答案 0 :(得分:2)

以下是Code Complete的一些指导原则。对于这类事情来说,这是一本好书。

  1. 在块中嵌套if-else和重复语句
  2. Long for-loops
  3. 重复的行/语句或常用操作可以放在函数中
  4. 如果由于某些原因你一遍又一遍地复制和粘贴一行代码
  5. 我发现离散数学会影响我现在写if语句的方式。通常,我看到我在两个区块中写了两个相同的IF语句,然后我会做一些心理上的'因子'。

答案 1 :(得分:1)

与布尔评估特别相关,值得注意的是大多数(?)现代语言都实现了懒惰的评估。

也就是说,如果“a”为真,那么if(a)if(a or b)在逻辑上和功能上都是等价的;解释器在真正的变量之后看到or时会停止评估。当ab是变量时,这不是很重要,但如果它们是可调用的[例如}当if(a() or b())为真时,b()},a将无法评估。

通过以下方法学习,您可以节省大量的击键次数(和处理器时间):

if(!userExists()):
    if(!createUser()):
        errorHandling()
    else:
        doStuff()
else: doStuff()

变为

if(userExists() or createUser()): doStuff()
else: errorHandling()

答案 2 :(得分:1)

做得好。现在,当我看到这个:

//Figure out the final image width
if (_glyphSize.Width > 0)
...

//Figure out the final image height
if (_glyphSize.Height > 0)
...

我认为还有更多的重构要做。将代码提取到方法中不仅是消除冗余代码的好方法。这也是使代码自我记录的好方法:

我倾向于将代码缩减为:

set_final_image_size

使用set_final_image_size及其minions定义如下:

def set_final_image_size:
  imageSize.Width = final_image_width;
  imageSize.Height = final_image_height;

def final_image_width:
  if (_glyphSize.Width > 0)
     return _glyphSize.Width;
  else if (not _glyph.Empty)
     return _glyph.GetWidth;
  else
     return 0;

def final_image_height:
  if (_glyphSize.Height > 0)
     return _glyphSize.Height;
  else if (not _glyph.Empty)
     return _glyph.GetHeight;
  else
     return 0;

答案 3 :(得分:0)

现在您已经分离了宽度和高度逻辑,并注意到它是相同的 - 如果您要在课程中添加getDimension(Direction direction)setDimension(Direction direction, int length),该怎么办?现在你已经

if (_glyphSize.getDimension(direction) > 0)
   imageSize.setDimension(direction, _glyphSize.getDimension(direction))
else if (not _glyph.Empty)
   imageSize.setDimension(direction, _glyph.getDimension(direction))
else
   imageSize.setDimension(direction, 0);

提取当地人带来了我们:

length = _glyphSize.getDimension(direction);
if (length > 0)
   imageSize.setDimension(direction, length)
else if (not _glyph.Empty)
   imageSize.setDimension(direction, _glyph.getDimension(direction))
else
   imageSize.setDimension(direction, 0);

进一步说明:

length = _glyphSize.getDimension(direction);
if (length == 0 && !_glyph.Empty)
    length = _glyph.getDimension(direction);
imageSize.setDimension(direction, length);

至少在我的眼里,开始看起来很漂亮。