Language agnostic 如何认识到短代码块可以重构成更干净的东西?

Language agnostic 如何认识到短代码块可以重构成更干净的东西?,language-agnostic,refactoring,code-cleanup,Language Agnostic,Refactoring,Code Cleanup,我有一段几周前写的代码(代码的目的与其说重要,不如说重要的是它的结构): 今晚我正在看它,当我清理它时,我意识到清理后的版本必须更简洁: //Figure out the final image width if (_glyphSize.Width > 0) imageSize.Width = _glyphSize.Width else if (not _glyph.Empty) imageSize.Width = _glyph.GetWidth else imageSi

我有一段几周前写的代码(代码的目的与其说重要,不如说重要的是它的结构):

今晚我正在看它,当我清理它时,我意识到清理后的版本必须更简洁:

//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;
注意:我已经精简了代码,简化了逻辑流程,并对源语言进行了OBF训练

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

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


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

  • 如果他们有许可,他们可以做这件事
  • 如果他们没有权限,但覆盖已生效
我最初将其编码为:

if ((HasPermission || (!HasPermission and OverrideEnabled))
{
   ...do stuff
}
关于
if
子句的逻辑条件似乎有些冗长。我试着回到我的布尔代数课程,找出如何简化它。最后我能做到,所以我画了一张真值表:

Permission  Override   Result
    0           0        0
    0           1        1
    1           0        1
    1           1        1
当我看到它时,它是一个操作。所以我的
如果
语句变成:

if (HasPermission  or OverrideEnabled)
{
   ...
}
这是显而易见和简单的。所以现在我想知道为什么我一开始就看不到这一点



这让我又回到了我的问题:为了认识到某些代码块需要一些TLC,我可以/应该寻找什么样的指示符号?

下面是一些来自代码完成的指南,我不知道。这是一本关于这类事情的好书

  • 块中嵌套的if-else和重复语句
  • 渴望循环
  • 可以在函数中放置重复的行/语句或经常使用的操作
  • 如果由于某些原因,您正在一次又一次地复制和粘贴一行代码

  • 我发现离散数学对我现在写if语句的方式有影响。通常,我看到我在两个块中编写两个相同的IF语句,然后我会做一些心理“因子分解”。

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

    也就是说,如果“a”为真,那么
    if(a)
    if(a或b)
    在逻辑和功能上是等价的;当解释器在真变量之后看到
    时,解释器停止计算。当
    a
    b
    是变量时,这一点并不重要,但如果它们是可调用的[例如
    if(a()或b())
    ],当
    a
    为true时,
    b()
    将不会得到计算

    通过了解以下内容,您可以节省大量击键(和处理器时间):

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

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

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

    //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及其下属定义如下:

    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;
    

    现在,您已经分离了宽度和高度逻辑,并且注意到它是相同的-如果您要将
    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);
    

    至少在我看来,这已经开始看起来很不错了。

    我不喜欢改变系统状态的
    if
    逻辑子句。此外,一些编译器可以选择关闭该功能,在这种情况下,您的代码可能会微妙地引入错误。我不确定我是否喜欢强制堆栈推送和子例程调用一些不需要的东西。我们生活在一个富足的时代,传统的约束——堆栈、CPU、,磁盘——对于大多数应用程序来说已经不存在了。当你可以为人类做得更好的时候,为什么还要为机器进行优化呢?而且,如果这样做有好处的话,一个好的编译器会把它内联起来。在解释语言中可能不会出现这种情况,但话说回来,性能可能不在优先级列表的首位。我也不喜欢引入包含3行的方法。因为现在三个陈述变成了四个,为了理解发生了什么,你必须跟着一个跳跃。我同意将重复或冗长的代码段重构到它们自己的方法中。但是替换一个代码块谁的工作是通过将其拆分为另外两个函数来计算最终大小?您没有帮助提高可读性或可维护性。shorter并不总是意味着在大多数情况下更易于维护,相反,在使其正常工作后,可维护应该是第一个目标。@fuzzy:这正是我为什么要追求shorter。最初较长的版本似乎太复杂了。所以在这种情况下,越短越好。但如果我试图用三元运算符把它缩短,那么你有权开枪打我。我认为这太过分了。imageSize是一个结构,有两个成员:宽度和高度。将size.Width=value替换为size.SetDimenion(directionWidth,value);如果有什么东西可读性较差,那就是这样。@Ian你当然有权发表你的意见,但我发现我们对X&Y的处理通常会产生大量重复代码,消除重复会使代码更容易理解并减少错误。
    length = _glyphSize.getDimension(direction);
    if (length == 0 && !_glyph.Empty)
        length = _glyph.getDimension(direction);
    imageSize.setDimension(direction, length);