西西河

主题:【原创】乱侃软件工程师的素养 1 -- poorfat

共:💬71 🌺108
分页树展主题 · 全看首页 上页
/ 5
下页 末页
  • 家园 【原创】乱侃软件工程师的素养 1

    前言

    软件工程师的素养这个问题太大了,鄙人只能就我工作中的所见所闻谈一些感想。这个系列没有什么逻辑次序,想到哪儿写到哪儿,所以就叫“乱侃".

    首先谈谈我比较痛恨的某些不良编程习惯。

    鄙人作为一个测试工程师,经常需要检测程序员们写的代码。有一次一个程序返回了一个错误,于是我用我的IDE打开那个程序看看这个错误大概会是从哪个地方出现的。于是我就看到了这样一段代码:(以下用伪码表示)

    if ( !function_one(...) || !function_two(...) || !function_three(...) )

    {

    return OMG_WE_FAILED;

    }

    看到这里我心里已经把这位程序员骂了几遍了。你把三个不同的函数调用都写在一个IF语句里,你叫我怎么知道哪个出错了呢?把这三个函数调用分别写到三个IF语句里难道你会死啊?

    有人也许会说,连上调试机debugger一看不就知道了? 不错。 但是你要记住,这个程序将来是要拿去给客户运行的,而客户不一定懂如何调试。 客户只会给你打电话抱怨说你的产品又坏了。假设你的运气好,客户发现错误代码是“OMG_WE_FAILED”,那么你更大的麻烦就来了:究竟是三个函数里的那一个返回了这个错误呢?想去吧,抓破脑皮也想不出的。

    如果你把三个函数调用分别放在三个IF语句里,并且分别用不同的错误代码,那么你至少可以知道哪一个函数出错了。而且这也并不增加很多工作量,但却可以减少将来查错的工作量,何乐而不为呢?

    所以说,作为一个好的软件工程师,写程序的时候要时刻记得将来这个程序如何检测。程序的可测性(testability)也是很重要的。

    作为软件工程师,我们大多数时间不需要写高级深奥的算法。 但是像这样简单的程序也不是随便写写就好了的。如果这也写不好,干脆别吃这碗饭了。

    元宝推荐:铁手,
    • 家园 曾在if判断里把一个正则表达式写成六行的菜鸟飘过

      血泪经验:程序的可测性(testability)是非常重要的。。。

    • 家园 【原创】素养 (5) 留下痕迹案例2

      再强调一下:别忘了留下痕迹,从程序所有可能的执行路径都要记得留下踪迹。

      我有一次测试这样一段程序。用正面测试(positive tests)都是好的。一用反例来测试,就碰到问题了。

      请看这段程序:(以下用伪码表示)

      bool Run(executableName, ParamList, &fullCommandLine, &error)

      {

      if ( ShellExecute("cmd /c " + executableName + " " +ParamList) )

      {

      fullCommandLine = "cmd /c " + executableName + " " +ParamList;

      return true;

      }

      error = GetShellError();

      return false;

      }

      void RunCommandWithLogging(executableName, ParamList)

      {

      ...

      var cmdline = "";

      var errorlevel = 0;

      if ( Run(executableName + " " +ParamList, cmdline, errorlevel) )

      {

      log("the command line=[" + cmdline+"] has succeeded. ");

      }

      else

      {

      log("the command line=[" + cmdline+"] has failed. Errorlevel="+errorlevel);

      }

      ...

      }

      诸位看出问题来没有?每当我用一个错误的命令放入RunCommandWithLogging()函数,我就看到日志文件里有这样一行:

      the command line=[] has failed. Errorlevel=blah

      没错,就是一对空的方括号[]。问题就在这里。当命令行有错误的时候,日志文件里就没有记录那条错误的命令行,而只有一对空的方括号。

      为什么会这样呢?应为当ShellExecute()失败的时候,变量fullCommandLine没有被赋值。就这么简单。因此,Run()函数应该订正如下:

      bool Run(executableName, ParamList, &fullCommandLine, &error)

      {

      if ( ShellExecute("cmd /c " + executableName + " " +ParamList) )

      {

      fullCommandLine = "cmd /c " + executableName + " " +ParamList;

      return true;

      }

      else

      {

      fullCommandLine = "cmd /c " + executableName + " " +ParamList;

      error = GetShellError();

      return false;

      }

      }

      这个案例的教训就是,如果你要记录日志,别忘了所有的代码路径(code path)都要留下日志。

    • 家园 不见得,讨论一下

      这个写法实际上是有performance上的好处的,这是因为C里面对if中条件处理的编译优化决定的,如果这些func123比较expensive的话还是挺不错的。分在三个嵌套的IF里面的话也可以,但是只要返回码一样对你还是没什么帮助。

      当然你说到可测试性等等确实是一个很好的点,我只是说事情的因素有很多,最后的实现是一个综合考虑的事情。

      其实QA和程序员之间的误解和战斗在过去,现在,将来都是一个永恒的话题。你的帖子给我的印象是带了一个预设的前提就是可测试性是对你来说最重要的,但是显然程序员有别的因素要考量,因此不见得同意你的观点。

      • 家园 硬件发展到这个地步已经没有必要追求编程小技巧了

        现在的挑战是怎么样管理超大规模的项目和源代码,所以一般来说强调的是软件工程方面的东西,这其中当然也包括了楼主提到的软件测试。

        • 家园 细节

          细节缺失是可怕并容易忽略的事情,大量的细节缺失导致的结果只有失败,并且关键细节的缺失往往是致命的。

        • 家园 这个受华主席凡是论的毒太深了,凡事都是有条件的

          看看我也受影响了不是。

          你的观点对于企业的应用那是相当有道理的,但仍然不排除开发底层驱动,游戏等对那一点点运行效率的追求。在海量数据时代,对效率的追求重又引起重视。

          但对于绝大部分的IT工作,尤其是以企业的应用为目的,则你这个的观点具普遍性,而且所反映的指导思想,比如对软件工程与编成技巧的不同重视程度,也是老印的软件业能够在欧美横扫千军,而老中的软件业发展出低级蓝领程序员与微软研究院共存的尴尬局面的最大原因。

        • 家园 你的这个观点

          已经是一个讨论了无数次的经典话题了,不是一两个帖子几行字可以说清楚的。 我们还是求同存异的好。

      • 家园 完全可以兼顾性能和可读性:

        if(!function_1()) {

        return error_code_1;

        } else if(!function_2()){

        return error_code_2;

        } else if(!function_3()){

        return error_code_3;

        }

        • 家园 这样写好点

          if(!function_1()) {

          return error_code_1;

          } else if(!function_2()){

          return error_code_2;

          } else if(!function_3()){

          return error_code_3;

          }

          这样写清楚点。

          if ( !function_one(...) || !function_two(...) || !function_three(...) )

          {

          return OMG_WE_FAILED;

          }

          这个隐含的问题是,当第一个表达式不为0时,后面两个函数是不会执行的,这样写很容易造成后面维护的人理解出现歧义

        • 家园 这个不错。
分页树展主题 · 全看首页 上页
/ 5
下页 末页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河