【问题标题】:How to cope with bad code [closed]如何处理糟糕的代码[关闭]
【发布时间】:2010-01-20 08:41:35
【问题描述】:

作为一名开发人员,我在日常生活中遇到的最不愉快(也是最常见)的情况之一是我必须修复错误或在设计糟糕的代码中添加功能。现在,作为一名优秀的工匠,我想让代码处于比我发现它更好的状态。如果我不重构设计,通常无法实现新功能。好吧-他们可以,但这会使代码变得更糟。

不幸的是,这正是我经常遇到的困难。我觉得如果有一件事很难,那就是重构糟糕的代码,尤其是当你有截止日期的时候。接触或多或少有效的糟糕和复杂的代码是可怕的。结果,当我在代码中加入新功能而不修改现有代码时,我会引入更多混乱。

现在我的问题是我怎样才能学会处理糟糕的代码?我怎样才能学会理解庞大的代码库,然后在不破坏已经工作的东西并且不超过最后期限的情况下重构其中的一部分?有什么文献可以推荐吗?您对我有什么一般性的建议吗?

【问题讨论】:

    标签: refactoring


    【解决方案1】:

    一般提示:

    if (it is working)
       Do (not touch it);
    else
    {
       Make (as few modifications as possible)
       Or (it will stop working at all);
    }
    

    这是几代人的经验。

    【讨论】:

    • 我同意,但是看到它做得有多糟糕,你不会感到沮丧吗?好像我必须解决这个问题!
    • 每个程序员都有这个;)但是时间和金钱不允许这样做。
    • 这将引入更丑陋的 hack 和解决方法来杀死错误或引入新功能。如果多年后我总是走阻力最小的路线,代码就会变成一个不可触及的黑匣子(即黑洞)
    • @bitbonk:这仍然比 netscape 路线更具商业可行性:joelonsoftware.com/articles/fog0000000069.html
    • ...这就是为什么大泥球越来越坏的原因。
    【解决方案2】:

    Michael Feathers 写了一本关于这个主题的好书。

    Working Effectively with Legacy Code.

    Martin Fowler、Kent Beck 等人的另一本好书:

    Refactoring: Improving the Design of Existing Code.

    【讨论】:

    • 这两本书是一对。我建议先阅读重构。它会激发和挫败,因为你会说,“但我需要进行更改以使其可测试。我如何在不破坏代码的情况下做到这一点?”这就是羽毛书的用武之地。
    【解决方案3】:

    重构需要单元测试套件的安全带来消除“我破坏了它吗?”感觉。用一整套测试覆盖不好的代码将有助于你努力获得干净的代码。

    Pex 是一个我认为有用的工具(如果您在 .NET 世界中),用于为遗留代码创建测试。

    遗留代码 == 没有测试的代码!

    善良,

    【讨论】:

      【解决方案4】:

      当我必须处理向坏代码添加功能时,我通常的做法是:

      • 为每个必须工作的重要功能编写自动化测试(因为大多数糟糕的代码都没有任何测试)。
      • 进行代码更改。
      • 确保测试仍然有效。

      这至少给了你一些信心,你没有破坏一切。至于如何学习应对糟糕的代码,我想这只是关于经验。

      【讨论】:

      • 很多时候代码设计得很糟糕,以至于很难为它编写测试。
      • 总是可以编写至少一些个测试。
      • 您至少可以针对最高级别的接口编写测试。如果没有好的接口,添加一个提供好的接口的包装器,然后针对它编写测试。
      【解决方案5】:

      好吧,如果您要重构项目中的大量代码,我建议您使用一些不错的版本控制,这样您就可以轻松地进行分支和回退。鉴于,这可能是一扇敞开的大门,但在 imo 中至关重要。

      此外,在开始了解复杂的 OO 之前,请尝试将方法和函数分解为更小的部分。确保每个函数具有一定程度的原子性,这使得代码更易于维护、阅读和管理。 这都是关于小东西的,将其分解为逻辑操作单元,我正在对 1k 行方法进行重构操作。它做各种花哨的东西。 我的第一个目标是将尽可能多的东西分成更小的部分,完成后我会开始考虑更好的 OO 设计,这会容易得多,因为我对事物的把握要好得多。

      阿司匹林也很有效。

      【讨论】:

        【解决方案6】:

        我目前处于这种情况。我的做法是在接触代码之前先回答一些问题:

        1. 代码真的那么糟糕吗?如果是,常见的错误是什么? ==> 可能首先专注于那些
        2. 代码中的主要运行时流程是什么?也许您可以从中丢弃很多构造。
        3. 尝试在不更改代码的情况下分层/模块化代码。这会减少一些相互依赖
        4. 尝试用测试戳代码。如果代码库被纠缠得超出希望:使用PowerMock 之类的东西来模拟(还)不需要更改的对象
        5. 有一个可用的集成环境,您可以在其中测试生产附近环境中的更改。
        6. 不要回避重写部分代码库。但尽量不要在其中实施太多新东西
        7. 尝试组队,讨论设计、原则和解决方案

        这是一项艰巨的工作,没有人会为此感谢你。为小小的改进感到自豪,并享受完成的好工作:)

        【讨论】:

          【解决方案7】:

          我认为对您正在开发/改进的软件中的所有工作原理有一个大致的了解总是好的。这就是在开发过程之后或期间制作的设计文档和其他文档的用武之地。我相信,如果您之前的某人没有完成适当的文档,那么至少您应该在某处写几行关于您在整个开发过程中的经历.我通常使用 OneNote 或其他东西来记录我遇到的问题,并且通常会不断列出我认为需要重构的东西。如果在项目期间有一些停机时间,我通常会回到那个列表并尝试一点一点地改进。

          因此,基本上,如果您之前的某个人没有做对,那么至少您可以帮助其他遇到相同代码的开发人员减少问题,这将是一件好事。

          【讨论】:

            【解决方案8】:
            【解决方案9】:

            这取决于许多因素,但最重要的是您是否有权修改它。

            如果你这样做,重构它。例如,重命名类/函数/变量。提取和概括功能。请参阅重构:Improving the Design of Existing Code(主题的圣经)。 在开始执行此操作之前,请确保代码处于正确的版本控制 (VC) 中并拥有一组良好的测试用例。 VC 让您回滚,测试用例有助于捕捉意外的副作用。

            我建议像 Mercurial/Bazaar 和 Git 这样的分布式版本控制,因为它非常重构,不像添加功能那样完全结构化。

            如果没有测试(常见),您必须创建它们。阅读Working Effectively With Legacy Code。特别是关于“密封点”(不是暹罗猫:p)。

            如果您不创建更清洁的包装 API。

            例如:

            
            Old code ====================
            const ACT_SHOW = 'show';
            const ACT_HIDE = 'hide';
            function int DoThing(Object $Obj, Stirng $Action, Object $Param1, Object $Param1) {
                 ....;
            }
            Added code ==================
            enum Actions {
                show, hide;
            };
            class ActionDoer {
                private obj;
                ActionDoer($Object) {
                    this.obj = $Object;
                }
                function int act(Actions $Action, $Param1, $Param1) {
                    this.act($Action.toString(), $Param1, $Param1) ;
                }
                function int act(String $Action, $Param1, $Param1) {
                    DoThing(this.obj, $Action, $Param1, $Param1) ;
                }
                function int show() {
                    this.act(Actions.show, null, null);
                }
                function int hide(Color $ToBGColor, long $FadeTime) {
                    this.act(Actions.hide, $ToBGColor, $FadeTime);
                }
            }
            

            这样,旧代码不会被触及,并且可以使用新代码完成扩展。这种方法的一个很好的例子是 jQuery,旧的(默认)访问 DOM 的方法很痛苦。

            希望这会有所帮助。

            【讨论】:

              猜你喜欢
              • 2016-07-21
              • 2011-08-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-04-15
              • 1970-01-01
              相关资源
              最近更新 更多