代码简洁之二:函数只做一件事儿
函数应该做一件事。做好这件事。只做这一件事。
所以我们简化代码的一个简单方式就是不断拆分函数(Extract Method),一直拆分,拆分到不能再分出一个函数为止。
拆函数的过程就是一个概括目的、步骤,提取抽象层次动名词的过程。不要做只是解释代码的简单概括,要让函数中的语句处于一个相同抽象层次,如果是更细节的事儿,请再进一步拆分出一个函数出来。
让代码拥有自顶向下的阅读顺序。
以我之前写的一个列表显示的action(MVC框架里面的某个控制器里面的一个方法),最开始代码是这样的:
// 接收查询的参数 $minDate = trim($this->input->get_post('startDate', true)); $maxDate = trim($this->input->get_post('endDate', true)); $gameId = trim($this->input->get_post('gameId', true)); $adPositionId = trim($this->input->get_post('adPositionId', true)); $orderField = trim($this->input->get_post('orderField', true)); $order = trim($this->input->get_post('order', true)); $page = intval($this->input->get_post('page', true)); $pageSize = intval($this->input->get_post('pageSize', true)); if (!$order || !in_array($order, array('asc', 'desc'))) { $order = 'desc'; } if (!$orderField || !isset($this->_fieldsMappingArray[$orderField])) { $searchParam['sort_by'] = 'date'; } else { $searchParam['sort_by'] = $this->_fieldsMappingArray[$orderField]; } if (!$minDate) { $minDate = date('Y-m-d', strtotime('-1 day')); } if (!$maxDate) { $maxDate = date('Y-m-d'); } if (strtotime($minDate)) { $searchParam['minDate'] = strtotime($minDate . ' 00:00:00'); } if (strtotime($maxDate)) { $searchParam['maxDate'] = strtotime($maxDate . ' 23:59:59'); } // .......一系列的判断 // 开始调用查询 $this->load->model('webgame_model'); $result = $this->webgame_model->getStatisData(); // 一系列组装数据 $this-view('users/webgameStatisList.php', $data);
这些代码写得很”质朴“,但是也很冗余,在这个function里面干了太多的事儿,杂乱无章,随着传入参数越来越多,接收部分和处理部分的代码会越来越长。虽然我不想轻易的狗带,但是容我做一个悲伤的表情,好么?
而且其实很多列表的action都是类似的,完全可以封装一些公共处理的方法,让代码抽象层次变得低一些,最终落地。
不过,首先我们得整理一下思路和步骤,把思路写越详细越好,细到不能再分出一个步骤。我借鉴《代码简洁之道》里面的建议,用TO 的那种引导方式来梳理。
1.要获取查询结果,先拿处理查询参数,然后再判断参数合法性,然后再进行查询。 (这是最大的思路步骤)
2.要处理查询参数,要先拿到传过来的参数做一些逻辑处理。 ---->getSearchFileds()
3.要判断参数合法性,要进行一系列业务逻辑的判断,最后返回错误信息,统一处理。 ----> checkParam()
4.要进行查询获取结果,要先把引入model,然后处理组装数据,最后返回。把所有组装细节都放在查询获取里面。 ----> _searchWebGameStatis()
最终代码简化如下:
// 接收处理查询参数 $params = $this->getSearchFileds($this->searchFiledOptions, $_REQUEST); // 判断参数合法性 if ($tips = $this->checkParam()) { $this->message->tips($tips); } // 查询获取游戏统计数据 $statis = $this->_searchWebGameStatis($params); $statis['searchs'] = $params; // 其他导航信息组装 $this->template->cpView('users/webGameStatis', $statis);
关于getSearchFieds等的内部实现等,因为每个人的思路不同,我就不细表了。(以后单独开一篇文章细表也可以哒)但是整个简化思路就是这样。
当我们明确自己的目标和单一职责的步骤,世界都给我们让步。好的代码就是每一行都是函数的调用,而函数都只做一件事儿。读代码就像读一篇短小的文章,简单易懂,出了任何问题,责任链明确,直接去找那个function就好了。
古人没有骗我:不积跬步无以至千里。