编码之道:小函数的大威力

一屏之地,一览无余!对的!要的就是短小精悍!

翻开项目的代码,处处可见成百上千行的函数,函数体里面switch-case、if、for等交错在一起,一眼望不到头的感觉。有些变态的函数,长度可能得按公里计算了。神啊,请赐予我看下去的勇气吧!先不论逻辑如何,首先这长度直接就把人给吓到了。这些超大号函数是怎么来得呢?

更多内容:http://game-lab.org/archive.html

  • 直接从别处COPY一段代码,随便改改即可,造成大量重复代码。
  • 缺少封装,甚至说就没有封装,完全就是随意乱加一气,造成各个抽象层次的代码混合在一起,混乱不堪。
  • 成篇的异常处理和特殊处理,核心逻辑或许就是函数体开头、中间或结束那么几行而已。

这些超长的函数,给我们造成了很大的麻烦:阅读代码找BUG几乎是不可能的事情,没有调试器估计撞墙的心都有了;重复代码造成修改困难,漏掉任何一处迟早是要出问题的;各个层次的代码混在一起,阅读代码相当吃力,人的临时记忆是有限的,不断在各个层次之间切换,一会儿就给绕晕了。

解决这些问题最重要的就是要保持函数的短小,短小的函数阅读起来要好得多,同时短小的函数意味着较好的封装。下面谈谈关于函数,应该遵循的一些原则:

1. 原则:取个描述性的名字

  • 取个一眼就看出函数意图的名字很重要
  • 长而具有描述性的名称,要比短而让人费解的好(长度适中,也不能过分长)
  • 使用动词或动词+名词短语

在编码之道:取个好名字中已经介绍过,好名字的重要性,不再赘述。

2. 原则:保持参数列表的简洁

  • 无参数最好,其次一元,再次二元,三元尽量避免
  • 尽量避免标识参数
  • 使用参数对象
  • 参数列表
  • 避免输出和输入混用,无法避免则输出在左,输入在右
bool isBossNpc();
void summonNpc(int id);
void summonNpc(int id, int type);
void summonNpc(int id, int state, int type); // 还能记得参数顺序吗?

void showCurrentEffect(int state, bool show); // Bad!!!
void showCurrentEffect(int state); // Good!!
void hideCurrentEffect(int state); // 新加个函数也没多难吧?

bool needWeapon(DWORD skillid, BYTE& failtype); // Bad!!!

3. 原则:保持函数短小

  • 第一规则:要短小
  • 第二规则:还要更短小
  • 要做到“一屏之地,一览无余”更好

4. 原则:只做一件事

  • 函数应该只做一件事,做好这件事
  • 且只做这一件事

5. 原则:每个函数位于同一抽象层级

  • 要确保函数只做一件事,函数中的语句都要在同一个抽象层级上
  • 自顶下下读代码

6. 原则:无副作用

  • 谎言,往往名不副实

7. 原则:操作和检查要分离

  • 要么是做点什么,要么回答点什么,但二者不可兼得”)
  • 混合使用—副作用的肇事者

8. 原则:使用异常来代替返回错误码

  • 操作函数返回错误码轻微违法了操作与检查的隔离原则
  • 用异常在某些情况下会更好点
  • 抽离try-cacth
  • 错误处理也是一件事情,也应该封装为函数
bool RedisClient::connect(const std::string& host, uint16_t port)
{
    this->host = host;
    this->port = port;
    this->close();

    try
    {
        redis_cli = new redis::client(host, port);
        return true;
    }
    catch (redis::redis_error& e)
    {
        redis_cli = NULL;
        std::cerr << "error:" << e.what() << std::endl;
        return false;
    }

    return false;
}

9. 原则:减少重复代码”

重复是一些邪恶的根源!!!

10. 原则:避免丑陋不堪的switch-case

  • 天生要做N件事情的货色
  • 多次出现就要考虑用多态进行重构

BAD:

bool saveBinary(type, data) {
   switch (type) {
     case TYPE_OBJECT:
           ....
          break;
     case TYPE_SKILL:
           ...
          break;
     ....
   }
}
bool needSaveBinary(type) {
   switch (type) {
     case TYPE_OBJECT:
          return true;
     case TYPE_SKILL:
           ...
          break;
     ....
   }
}

class BinaryMember
{
  BinaryMember* createByType(type){
   switch (type) {
     case TYPE_OBJECT:
          return new ObjectBinaryMember;
     case TYPE_SKILL:
          return new SkillBinaryMember;
     ....
  }

  virtual bool save(data);
  virtual bool needSave(data);
};

class ObjectBinaryMember : public BinaryMember
{
   bool save(data){
       ....
   }
   bool needSave(data){
       ....
   }
};")))

最后

上面提到的原则,若要理解的更加深刻,建议去阅读《代码整洁之道》,里面有许多详尽的例子,对于写过几年代码的人来说,总会发现一些自己所在项目经常犯的毛病。

知道了这些原则,我们应该这样做:

当在添加新函数的时候:

  • 刚下手时违反规范和原则没关系
  • 开发过程中逐步打磨
  • 保证提交后的代码是整洁的即可

重构现有的函数,有下面情况的,见一个消灭一个:

  • 冗长而复杂
  • 有太多缩进和嵌套循环
  • 参数列表过长
  • 名字随意取
  • 重复了三次以上
时间: 2024-10-28 23:44:39

编码之道:小函数的大威力的相关文章

小项目创意大集合

每个程序员都可以入手的小项目创意大集合 我经常看有人发帖问关于软件项目创意点子的事,也看到了很多回帖,我自己也回了一些常见的软件项目创意.不过我觉得只列出三两个是远远不够的,因此就收集并这个软件项目创意列表,大家要找简单的编程软件项目创意学习练手的话,可以收藏并扩散本文.这些软件项目创意并不是论文级别的,只是想抛砖引玉让大家能从中受些启发. 下面你们会看到 120 多个个软件项目创意想法,都是我通过头脑风暴得来的.我将其根据主题分成了10 个分类,但有些软件项目创意其实涵盖了不止一个主题. 更新

关于SQL的几道小题详解

关于SQL的几道小题详解 当我们拿到题目的时候,并不是急于作答,那样会得不偿失的,而是分析思路,采用什么方法,达到什么目的,还要思考有没有简单的方法或者通用的方法等等,这样才会达到以一当十的效果,这样的惯性思维其实早在我们度高中的时候就被领教了,所谓“万变不离其宗”吧.以下各题来自日常所见,或QQ群,或面试题,或博客园. 题目一:如下表所示,现需要按照收款员统计收款和退款合计金额. 实现结果需如下显示: 分析:想要的结果(记为表B)和源数据(记为表A)相比,有共同的列(收款员),不同的是表A的金

Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果

目前Android的实现是:有来电时,音乐声音直接停止,铃声直接直接使用设置的铃声音量进行铃声播放. Android 4.3实现类似iOS在音乐播放过程中如果有来电则音乐声音渐小铃声渐大的效果. 如果要实现这个效果,首先要搞清楚两大问题: 1.来电时的代码主要实现流程. 2.主流音乐播放器在播放过程中,如果有来电,到底在收到了什么事件后将音乐暂停了? 一:来电时的代码主要实现流程 我不是第一研究来电代码的人,网上已经有高手对这个流程剖析过,不是不完全符合我的要求,我参考过的比较有价值的是如下两个

几个非常有用的js小函数

1 function $(v){ 2 if(typeof v==="function"){ 3 window.onload=v; 4 }else if(typeof v==="string") 5 { 6 return document.getElementById(v); 7 }else if(v==="object") 8 { 9 return v; 10 } 11 } 12 这个小函数模拟jQuery中的$函数做了一些非常简单的事情.用来获

逛园子,看到个练习题,小试了一把(淘宝ued的两道小题)

闲来无事,逛园子,充充电.发现了一个挺有意思的博文,自己玩了一把. 第一题:使用 HTML+CSS 实现如图布局,border-widht 1px,一个格子大小是 60*60,hover时候边框变为橘红色(兼容IE6+,考虑语义化的结构) 效果图: 简单分析一下: 使用伪类 :hover的时候相对定位 改变z-index, 代码如下: 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta c

查询优化--小表驱动大表(In,Exists区别)

Mysql 系列文章主页 =============== 本文将以真实例子来讲解小表驱动大表(In,Exists区别) 1 准备数据 1.1 创建表.函数.存储过程 参照  这篇(调用函数和存储过程批量插入数据)  文章中的第 1-7 步,注意,不要执行第8步 1.2 插入数据 现在来执行第8步. 1.2.1 向 Department 表中插入 100 条记录 CALL insert_dept(1000, 100) 1.2.2 向 Employee 表中插入 100000 条记录 CALL in

js小函数

前言 对于项目中常用的一些函数总结. note 阻止F5刷新 $(document).keydown(function(e){ var ev = window.event || e; var code = ev.keyCode || ev.which; if (e.keyCode == 116){ ev.keyCode ? ev.keyCode = 0 : ev.which = 0; cancelBubble = true; return false; } }) Enter键登录 $(docum

hive join 优化 --小表join大表

1.小.大表 join 在小表和大表进行join时,将小表放在前边,效率会高,hive会将小表进行缓存. 2.mapjoin 使用mapjoin将小表放入内存,在map端和大表逐一匹配,从而省去reduce. 例子: select /*+MAPJOIN(b)*/ a.a1,a.a2,b.b2 from tablea a JOIN tableb b ON a.a1=b.b1 在0.7版本后,也可以用配置来自动优化 set hive.auto.convert.join=true;

Python学习笔记八:文件操作(续),文件编码与解码,函数,递归,函数式编程介绍,高阶函数

文件操作(续) 获得文件句柄位置,f.tell(),从0开始,按字符数计数 f.read(5),读取5个字符 返回文件句柄到某位置,f.seek(0) 文件在编辑过程中改变编码,f.detech() 获取文件编码,f.encoding() 获取文件在内存中的编号,f.fileno() 获取文件终端类型(tty.打印机等),f.isatty() 获取文件名,f.name() 判断文件句柄是否可移动(tty等不可移动),f.seekable() 判断文件是否可读,f.readable() 判断文件是