如何保持我的代码简单?

时间:2009-05-03 02:24:34

标签: language-agnostic

- 编辑 -

我认为这是一个有效的问题,可能有多个答案(定义为here)。这不是讨论也不是民意调查。此外,这个问题显然不具争议性,因为到目前为止,没有一个受访者似乎互相争论。

- 原创文本 -

惊人!我现在做软件大约15年了,我仍然不知道我正在做什么:)

说真的,我仍然在努力学习基础知识:

  • 过度训练与YAGNI
  • 为了满足最后期限和削减业务而偷工减料
  • 风险创新与繁琐的好东西
  • 享受独自工作的自由与团队的力量

但最糟糕的是代码复杂性。我的代码往往会得到 evil 。几周后它甚至会咬住我自己。我付出了巨大的努力来保持它简单,可读,可维护,优雅,美观,有凝聚力,松散耦合,基于非常直接的抽象。但所有这些努力都耗尽了!

不要误会我的意思,按照大多数人的标准,我的代码非常好。我的意思是它灵活,或多或少可单元测试,并做它需要做的事情。但是,这远非简单。

每一次变革都需要大量的重构。如果另一个人打开我的代码是为了添加一个功能,他就必须做一些愚蠢的事情。即使他是GENIUS,即使他是我自己的不熟悉代码库的CLONE,他也无法做到这一点。为什么,为什么上帝的缘故呢?是不是有一些方法论,一些技术,一些冥想技巧,什么都没有?!

如何保持我的代码简单?

15 个答案:

答案 0 :(得分:12)

由于@neodymium以犀利的方式指出,不会让您的算法驱动代码。为了举例说明你在一周前写的C#中的以下悲伤装置的实际意义:

public void DoArray(string[] strArr) {
    string s = null;

    for(int i = 0; i < strArr.Length; i++)
    {
        if(strArr[i].Equals("Needle"))
            s = strArr[i];
    }

    if(s != null)
        Console.WriteLine("I've found " + s);
}   

通过查看代码可能需要一段时间才能找到您所写的内容。但最后你还记得这个方法在字符串数组中查找“Needle”。你惊叹于你的算法是多么的巧妙,但对你花了一两分钟才意识到它正在做的事实感到烦恼。

不要通过评论开始清楚地说明事情!

你的第一直觉可能是写评论以帮助你的工作人员,但是停在正确的地方!评论只不过是apology。相反,让我们在代码中清楚明白...... 晶莹剔透

为了做到这一点,试着想想你以前从未见过自己的代码。通过对自己说话来强迫它:“这是什么”和“为什么会这样”。然后我们将开始重构这个东西。我们可以做的第一个简单的重构是将方法重命名为更合适的方法。怎么样:

public void FindNeedle(string[] strArr) { … }

我们还能做些什么?我们可以:

  • strArr重命名为更合适的内容,例如haystack
  • 将返回类型更改为bool并更改方法,以便在找到针时返回。
  • 将方法的Console.WriteLine(…)部分移出此方法的上下文,以便调用代码可以这样做:if ( FindNeedle(myArray) ) WriteFoundNeedle();
  • 使用foreach代替for

代码最终可能如下(您的milage可能会有所不同):

public bool HasNeedle(string[] haystack) {
    foreach(string straw in haystack)
    {
        if(straw.Equals("Needle"))
            return true;
    }
    return false;
}

// Is called by the following:
if ( HasNeedle(strArr) )
{
    Console.WriteLine("I've found the needle!");
}

以小步骤重构

为了使代码更清晰,易懂和易读,可以做很多事情,这可以通过重构代码来实现。尽可能小的步骤进行重构。就像移动或封装逻辑一样,将它们命名为更符合逻辑和可读性的东西。像一个冗长而复杂的if语句那样简单:

if ( strArr[pos - 1].Equals("do") && strArr[pos + 1].Equals("then") )

...可以通过将逻辑语句移动到its own method来重构为更简单的东西:

if ( CurrentIsSurroundedByDoThen(strArr, pos) )

有很多方法可以重构。我建议你读一些,Martin Fowler写过一本关于它的书,但也有on-line catalog available with code samples and/or class diagrams。选择你应该做什么或不做什么重构必须在假设它会使你的代码比以前更强可维护的情况下决定。

附录

请记住保留您的重构simple并记住SOLID principles

此外,如果您通过编写单元测试the right way开始您的项目,那么重构将变得更加自然。重构越多,代码就越清晰。

答案 1 :(得分:6)

我感到你的痛苦,伙计。复杂系统中简化的斗争是软件工程的斗争。如果你已经钉了它,你可能没有做足够严重的工程问题。而艰难并不总是意味着复杂,它可能是“明天实施x以防止天空下降。”

为了简单起见...... TDD彻底提到,完全同意。 TDD是一种将代码集中在它需要做的事情上的技巧,而不是更多。经常提到的重新考虑因素。完全同意。

简单性与复杂性以及单独工作......不能单独运输代码。每次办理登机手续都会获得代码评论,并鼓励代码审核人员为您提供煤炭。这将使您保持正确的妥协和平衡。每天至少与某人谈论您的代码一次。轮流审稿人。我的工作更清晰,对队友来说更好。不关心它们是多么绿。实际上,越绿越好,确保清晰的代码。

独自工作......独自工作在R&amp; D中占有一席之地,而非代码。充其量,单独的牛仔项目会让很酷的东西变得难以维持。单独完成的工作总是需要一个月或两个时间来重新实现并重新考虑由凡人维护的代码并修复一些巨大的疏忽。如果在您运送牛仔代码后的那个月或两个月内,您真的很痛苦。

编辑:在细节方面,我发现有关Domain Drive Design的各种书籍非常有助于提供创建超清代码的方法。 DDD不适用于所有问题。

如果你确实找到了简单和过度工程之间平衡的答案......那么,我甚至不知道该怎么做。我想我会感到无聊并找到另一项工作。

答案 2 :(得分:5)

  
    

如何保持代码简单?

  

使用代码实现正确的算法。

不要让你的算法编写代码。

答案 3 :(得分:4)

对我来说,测试驱动开发有所不同。当我在没有测试的情况下编写代码时,我会考虑太多场景,担心代码是否有效,写出大量额外的东西以确保它能够正常工作等等。

当我进行TDD时,代码变得非常简单,因为测试使我编写了正确的代码,我没有那么防守,但我确信它满足了所有要求,因为测试通过了。

我发现另一件有用的事情是检查开源项目的代码。他们中的许多人都拥有易于他人理解和修改的代码,因此可以很好地了解如何实现这一目标。在这方面我最喜欢的是JMock。

答案 4 :(得分:4)

其中一个较简单的,我最喜欢的是introduce explaining variables

此:

if ( (platform.toUpperCase().indexOf("MAC") > -1) &&
      (browser.toUpperCase().indexOf("IE") > -1) &&
       wasInitialized() && resize > 0 )
 {
   // do something
 }

变为:

final boolean isMacOs     = platform.toUpperCase().indexOf("MAC") > -1;
final boolean isIEBrowser = browser.toUpperCase().indexOf("IE")  > -1;
final boolean wasResized  = resize > 0;

if (isMacOs && isIEBrowser && wasInitialized() && wasResized)
{
    // do something
}

答案 5 :(得分:3)

经常重构。

这是我发现使代码变得简单的唯一可靠方法。如果不首先开始处理代码,有时候你无法想象出复杂性。

答案 6 :(得分:2)

或多或少的单元可测试意味着它不是可单元测试的。 :)

如果你不能轻易地编写测试,没有跳过很​​多箍或设置工作,那么这似乎是一个问题,IMO,并且应该要求重构。

我尝试遵循三个原则: 让它起作用 改正它 快点。

第一个是让它工作,主要是单元可测试的。 :)

然后我重构并改进单元测试,完成它们。这是你应该去的下一步,因为听起来你可能忽略了这个概念。此外,尝试删除任何硬编码值以使其更灵活。将它们放在xml文件,数据库中或作为常量文件中的常量。

然后,我描述并优化了瓶颈。

您可能希望查看xetreme编程(XP)并了解仅实现所需功能的概念,而不是尝试实现您认为需要的功能。它应该简化代码。

答案 7 :(得分:2)

我通常会记住这句话。

  

奥卡姆剃刀最简单的(解释|解决方案)通常是最好的。

我建议您发布一些代码并要求批评和评论。你可以从其他人那里学到很多东西。试试吧:))

答案 8 :(得分:2)

当您考虑是否添加或删除某些内容时,请问自己是否属于提供80%差异的20%的努力。

如果没有或您有疑问,请将其删除。如果删除的内容很重要,很快就会显而易见。

保持告诉自己“做到简单”也有帮助。去简单,忘记纠正,纠正会来,并吻你。

答案 9 :(得分:1)

我认为这是一个常见问题。我们都试图创建优雅的代码和干净的代码,有时候我们最终做得很快而且很脏,因为我们处于压力之下,时间和预算都不是无限的。

我试着跟随DRY和KISS。 “不要重复自己”和“保持简单愚蠢”。我也遵循“一件事”的规则。方法,类,命名空间应该只做一件事。

老实说,我已经尝试过从测试驱动的开发角度做一个项目,我发现很难进入那个思维模式,Red,Green,Refactor,但它真的有帮助打破旧习惯。

我一直是紧密耦合的受害者,不得不把事情搞砸到他们自己的组件中,这是残酷的并打破了应用程序,但你可能会陷入困境。

这是一个模糊的问题,我同意,社区维基!

P.S。我认为某些内容不是moreless,而是isisn't

答案 10 :(得分:1)

脱离我的头脑:在重构的时间表中建立时间。使重构成为要求的一部分,而不仅仅是经过深思熟虑。

这将基于希望提供更易于维护,可重复使用的产品,因此未来的开发人员不会惊慌失措并且不得不入侵。

我真的认为答案是让重构成为你生活中更重要的一部分,并对其进行纪律处分。 (我也需要这个!类似的问题......我想我们大多数人都在这艘船上。)

答案 11 :(得分:1)

只添加所需的东西 - 不要将来证明 - 只对实际问题作出反应

答案 12 :(得分:0)

您可以尝试经常运行复杂度指标,确定复杂性的上限,以及超出限制时的重构。复杂性指标是客观的。它们为您提供了量化软件复杂程度和衡量进度的方法。也许你正在打败那些非常好的代码。或者您认为代码可能是复杂度指标上的简单得分。

我知道这些指标很粗糙。真正重要的是复杂性的主观心理体验,而不是McCabe复杂性数字,而后者是对前者的粗略近似。另一方面,我发现这些指标非常有用。

答案 13 :(得分:0)

关于KISS主题:

1)不要觉得必须修复核心代码中的所有小错误/功能。有时最好留下他们记录下来,并让调用代码的人担心如何解决它们。 (无论如何,他很可能永远不会触发那个特定的bug,所以任何检查都会浪费处理。)这样可以保持代码的小巧,快速和简单。如果你想制作一个万无一失但却臃肿的版本,请将它与库代码分开编码......

2)不要让其他人改变你的代码!他可以调用你的代码,或者他可以扩展你的代码,所以如果它已经在完成它的工作,他真的需要改变核心吗?不要让他将SimpleAndSpeedySearchPage变成SuperAdvancedSearchPageWithCowbell,让他通过扩展或调用你的代码来构建SuperAdvancedSearchPageWithCowbell。

如果您的队友确实开始在整齐的小图书馆中添加内容,请自行进行重构。把你的好代码拉到一个超类,然后把他的代码留给你。

总结:一旦您的代码执行其基本工作,请停止工作,并将其设置为只读!如果要添加“高级功能”和“特定于应用程序的错误修正”,请将它们添加到其他位置。 100个简单类比10个膨胀类更好。

答案 14 :(得分:0)

想到的几件事情:

  1. 在深入实施之前,尝试提前计划您的实施策略 - 可能在纸面上或与同行讨论。可能已经存在可用于处理问题部分的库函数。
  2. 记下伪代码,概述实施步骤。
  3. 尝试实现这些方法,以便每个方法都有一个目的,或者将其他方法联系在一起。应该可以一目了然地看出任何方法应该做什么。这是TDD可以帮助您专注于更容易测试的小方法的地方。较小的方法也有特定的名称,所以这个名字已经告诉了你很多。此外,一些简单的复杂性指标,如this question中的一个,可以帮助您确定您的方法是否尝试做太多。
  4. 当你的实现工作时不要停止 - 尝试重构,直到它尽可能简单易维护。总是问自己,如果其他人会读这个,他们会理解吗?