• 2010-10-31

    代码之丑(二)(续)

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/81550344.html

    sinojelly在《代码之丑(二) 》的评论里问了个问题,“把这个type列表变成声明式”,什么样的声明式?

    好吧!我承认,我偷懒了,为了省事,一笔带过了。简单理解声明式的风格,就是把描述做什么,而不是怎么做。一个声明式编程的例子是Rails里面的数据关联,为人熟知的has_many和belongs_to。通过声明,模型类就会具备一些数据关联的能力。

    具体到实际开发里,声明式编程需要有两个部分:一方面是一些基础的框架性代码,另一方面是应用层面如何使用。框架代码通常来说,都不像应用层面代码那么好理解,但有了这个基础,应用代码就会变得简单许多。

    针对之前的那段代码,按照声明性编程风格,我改造了代码,下面是框架部分的代码:

    #define BEGIN_STR_PREDICATE(predicate_name) \
    bool predicate_name(const char* field) { \
      static const char* predicate_true_fields[] = {
       
    #define STR_PREDICATE_ITEM(item) #item ,

    #define END_STR_PREDICATE \
      };\
      \
      int size = ARRAY_SIZE(predicate_true_fields);\
      for (int i = 0; i < size; i++) { \
        if (strcmp(field, predicate_true_fields[i]) == 0) {\
            return true;\
        }\
      }\
    \
      return false;\
    }

    这里用到了C/C++常见的宏技巧,为的就是让应用层面的代码写起来更像声明。对比一下之前的函数,就会发现,实际上二者几乎是一样的。有了框架,就该应用了:

    BEGIN_STR_PREDICATE(shouldExecute)
      STR_PREDICATE_ITEM(PreDropGroupSubs)
      STR_PREDICATE_ITEM(StopUserGroupSubsCancel)
      STR_PREDICATE_ITEM(QFStopUserGroupSubs)
      STR_PREDICATE_ITEM(QFStopUserGroupSubsCancel)
      STR_PREDICATE_ITEM(QZStopUserGroupSubs)
      STR_PREDICATE_ITEM(QZStopUserGroupSubsCancel)
      STR_PREDICATE_ITEM(SQStopUserGroupSubs)
      STR_PREDICATE_ITEM(SQStopUserGroupSubsCancel)
      STR_PREDICATE_ITEM(StopUseGroupSubs)
      STR_PREDICATE_ITEM(SQStopUserGroupSubsCancel)
      STR_PREDICATE_ITEM(StopUseGroupSubs)
      STR_PREDICATE_ITEM(PreDropGroupSubsCancel)
    END_STR_PREDICATE

    shouldExecute就此重现出来了。不过,这段代码已经不再像一个函数,而更像一段声明,这就是我们的目标。有了这个基础,实现一个新的函数,不过是做一段新的声明而已。

    接下来就是如何使用了,与之前略有差异的是,这里为了更好的通用性,把字符串作为参数传了进去,而不是原来的整个类对象。
      shouldExecute(r.type);

    虽然应用代码变得简单了,但写出框架的结构是需要一定基础的。它不像应用代码那样来得平铺直叙,但其实也没那么难,只不过很多人从没有考虑把代码写成这样。只要换个角度去思考,多多练习,也就可以驾轻就熟了。

     


    本文已经首发于InfoQ中文站 ,版权所有,原文为《专栏:代码之丑(二) 》,如需转载,请务必附带本声明,谢谢。

    InfoQ中文站 是一个面向中高端技术人员的在线独立社区,为Java、.NET、Ruby、SOA、敏捷、架构等领域提供及时而有深度的资讯、高端技术大会如QCon 、免费迷你书下载如《架构师 》等。

    分享到:

    历史上的今天:

    2009 Away Day 2009-10-31
    身体最重要 2004-10-31
    引用地址:

    评论

  • 这段代码如果用vector<string>保存判别字符串,然后在用
    find算法会更简单
  • 看了这个后,我感觉这就像一个新的类, 里的有一个属性 与一个方法.
    属性为fields, 方法为cmpField(field).
  • to hurricane1026:

    template对字符串做参数的支持,确实是个问题。
    如果我选还是会选择做成单独的函数。
  • 采用声明式的编程重构原来的旧代码,这种想法是很好的。
    但是采用宏定义来实现,在策略上是否稍有欠妥?
    使用metadata来做这个声明更加合理,在c中做成单独的函数应该也就可以啦。
    回复飞天一叶说:
    用宏的方式是一种可选的方案,事实上,我也倾向于少用宏。我不知道你想说的这个metadata是怎样的,可以给出一个更具体的例子大家来讨论一下。
    2010-11-18 13:09:43
  • 谢谢,解释得非常清楚:)

    我之前没仔细想过应用这种技巧,其实是把函数变成了声明。学习了!
  • 个人觉得这样的代码风格并不见得好。openssl,wtl里都有大量的类似上面的宏,给读代码的人带来了大量的麻烦,而且几乎啥工具都很难让人简单的阅读这样的代码了,如果可以选择,首先应该选择template,宏应该作为不得已的选择。现代c++里,template几乎没有什么做不了的事情。
    回复hurricane1026说:
    我在blog中提到,这种代码分成两个部分,框架和应用。框架部分的技巧是为了让应用部分代码更清楚,而框架本身却是可读性要差一些。

    欢迎你为这段代码贡献一个template的版本。:)
    2010-11-02 22:27:05