zoukankan      html  css  js  c++  java
  • 一个代码重构的经典示例

         在《The Pragmatic Programmer: From Journeyman to Master》(中文译名为《程序员修炼之道--从小工到专家》)Tip25 “怎样配平资源“中有一个重构代码的经典案例,现转录如下:

              提示35

             Finish What You Start

             要有始有终

          在大多数情况下这条提示都很容易应用。它只是意味着,分配某项资源的例程或对象应该负责解除该资源的分配。让我们通过一个糟糕的代码例子来看一看该提示的应用方式——这是一个打开文件、从中读取消费者信息、更新某个字段、然后写回结果的应用。我们去除了其中的错误处理代码,以让例子更清晰:

          void readCustormer(const char *fName, Customer  *cRec) {

        cFile = fopen(fName, "r+");

        fread(cRec, sizeof(*cRec), 1, cFile);

         }

         void writeCustomer(Customer  *cRect) {

        rewind(cFile);

        fwrite (cRec, sizeof(*cRec), 1, cFile);

        fclose(cFile);

      }

        void updateCustomer(const char *fName, double newBalance) {

        Customer cRec;

        readCustomer (fName, &CRec);

        cRec.balance = newBalance;

        writeCustomer(&cRec);

        } 

        初看上去,例程updateCustomer相当好。它似乎实现了我们所需的逻辑——读取记录,更新余额,写回记录。但是,这样的整洁掩盖了一个重大的问题。例程readCustomer和writeCustomer紧密地耦合在一起——它们共享全局变量cFile。readCustomer打开文件,并把文件指针存储在cFile中,而writeCustomer使用所存储的指针在其结束时关闭文件。这个全局变量甚至没有出现在updateCustomer例程中。

        这为什么不好?让我们考虑一下,不走运的维护程序员被告知规范发生了变化——余额只应在新的值不为负时更新。她进入源码,改动updateCustomer:

     

      void updateCustomer(const char *fName, double newBalance) {

        Customer cRec;

        readCustomer (fName, &CRec);

        if (newBalance >= 0.0)  {

          cRec.balance = newBalance;

          writeCustomer(&cRec);

        }

        } 

        在测试时一切似乎都很好。但是,当代码投入实际工作,若干小时后它就崩溃了,抱怨说打开的文件太多。因为writeCustomer在有些情况下不会被调用,文件也就不会被关闭。

       这个问题的一个非常糟糕的解决方案是在updateCustomer中对该特殊情况进行处理:

        void  updateCustomer(const char *fName, double newBalance) {

        Customer cRec;

        readCustomer (fName, &CRec);

        if (newBalance >= 0.0)  {

          cRec.balance = newBalance;

          writeCustomer(&cRec);

        }

        else 

          fclose(cFile);

        } 

          这可以修正问题——不管新的余额是多少,文件现在都会被关闭——但这样的修正意味着三个例程通过全局的cFile耦合在一起。我们在掉进陷阱,如果我们继续沿着这一方向前进,事情就会开始迅速变糟。

          要有始有终这一提示告诉我们,分配资源的例程也应该释放它。通过稍稍重构代码,我们可以在此应用该提示:

        void readCustomer(FILE *cFile, Customer *cRec) {

        fread(cRec, sizeof(*cRec), 1, cFile);

       }

       void  writeCustomer(FILE *cFile, Customer *cRec) {

        rewind(cFile);

        fwrite(cRec, sizeof(*cRec), 1, cFile);

       }

       void updateCustomer(const char *fName, double newBalance) {

        FILE *cFile;

        Customer  cRec;

     

         cFile = fopen(fName, "r+");

         readCustomer(cFile, &cRec);

           if (newBalance >= 0.0) {

          cRec.balance = newBalance;

          writeCustomer(cFile, &cRec);

        }

        fclose(cFile);

     }

        现在updateCustomer例程承担了关于该文件的所有责任。它打开文件并(有始有终地)在退出前关闭它。例程配平了对文件的使用:打开和关闭在同一个地方,而且显然每一次打开都有对应的关闭。重构还移除了丑陋的全局变量。

        由上文可见,重构时遵循一些原则是很重要的。

     

  • 相关阅读:
    主要用到 DELPHI XE 10.2新增HASH函数
    个人使用Onenote和Evernote对比
    OneNote和Evernote的特征
    allure的HTML报告信息解疑
    记:ModuleNotFoundError: No module named 'pip'
    用例需注意的点
    Selenium Builder
    定位到元素后可进行的操作事件
    基本定位方法
    webdriver的基本操作
  • 原文地址:https://www.cnblogs.com/yangjd/p/10711309.html
Copyright © 2011-2022 走看看