第三部分我们将在函数级别对代码做一些改动以改善代码的可读性。
第10章 抽取不相关的子问题
本章的建议是“积极地发现并抽取出不相关的子问题”。
1、提炼纯工具代码并放置到项目中一个公共的地方(例如util)。
2、创建大量通用的代码。“自顶向下”与“自底向上”相结合。
3、简化(包装)已有的接口,使其表现得更整洁易用。
第11章 一次只做一件事
本章的关键思想是“应该把代码组织得一次只做一件事”。
如果一个函数有很难读的代码,那么可以尝试把它所做的所有任务列出来。其中一些任务可以很容易的变成单独的函数(或类),另一些或许可以简单地成为一个函数中的逻辑段落。当然,假设在刚开始编写代码时,我们也可以使用同样的做法,根据需求把函数要完成的任务一一列出来,然后再一一实现之(这是意图主导编程倡导的做法)。
第12章 把想法变成代码
本章的思想有点“意图主导编程”的味道。
把一个想法用自然语言解释是个很有价值的能力,这需要把一个想法精炼成最重要的概念。这样做不仅仅帮助他人理解,而且也帮助你自己把这个想法想得更清晰。这一章,作者展示了用这一方法来撰写代码的过程:
第一步,像对着一个同事一样用自然语言描述代码要做什么。
第二步,注意描述中所用的关键字和短语。
第三步,写出与描述所匹配的代码。
最后,重读代码,做进一步的改进(重构)。
1、清楚的描述逻辑
2、尽可能的使用函数库
3、也可以用自然语言描述解决方案,以解决更大的问题。把同样的技术递归应用于每一个或大或小的函数实现上。
如果你不能把问题表述明白或者用词语来做设计,估计是缺少了什么东西或者什么东西没有定义。说明你还没有弄明白自己想要做什么?把一个问题或想法变成语言真的可以让问题或想法变得更具体。
第13章 少些代码
本章是我最感兴趣的话题。
知道什么时候不写代码可能对于一个程序员来说是他所要学习的最重要的技巧。我们所写的每一行代码都需要测试和维护。通过重用库,我们可以节约代码并保证代码质量,功能也更加可靠。
1、YAGNI原则(You Aren't Going to Need It)
2、保持小代码库:
1)创建越多越好的”工具代码“来减少重复代码;
2)减少无用代码或没有用的功能;
3)让整个项目保持分开的子项目状态;
4)总之,要小心代码的”重量“。让它保持又轻又好。
3、熟悉周边的库;为什么重用库有这么大的好处?以下是答案:
一个常被引用的统计结果是一个平均水平的软件工程师每天能写出10行可以放到最终产品的代码。”最终产品中的代码“这几个字的份量很重!在一个成熟的库中,每一行代码都代表着相当大量的设计,调试,重写,文档,优化和测试。——这样的代码是很有价值的。这就是为什么我宁愿花十分钟去学习了解一个简单库函数的使用,而不愿自己花5分钟来实现。
第四部分精选了两个重要的话题,第一个话题是关于测试代码的可读性的;第二个专题是一个实际例子的设计演化过程。
第14章 测试与可读性
测试对于不同的人意味着不同的事。在这里(程序员),”测试(代码)“是指以检查另一段代码为目的的代码。
1、使测试易于阅读和维护
测试代码应该和产品代码具有同样的可读性。测试代码可以看做非正式的文档,它记录了真实代码如何工作和应该如果调用。如果测试代码变得难以阅读和修改,那么可能1)程序员会不敢修改真实代码;2)程序员会不愿意增加新的测试。
2、让错误消息具有可读性
3、选择好的测试输入
1)应该选择一组最简单的输入,它能完整地使用被测代码。
2)对代码的压力测试来讲,应该使用编程的方法产生大型输入数据。
4、一个功能的多个测试
与其建立单个”完美“的输入来完整地执行你的代码,不如多写几个简单的小测试,或者通常更有简单也更有效。
5、”喧宾夺主“不可取
1)切勿牺牲真实产品代码的可读性来适应测试代码。
2)别让测试成为产品开发的阻碍。
第15章 设计并改进“分钟/小时计算器”
一个好的例子胜过千言万语。
头文件:
#ifndef MINHOURCOUNTER_H #define MINHOURCOUNTER_H #include <ctime> #include <list> // Track the cumulative counts over the past minute and the past hour. // Useful, for example, to track recent bandwidth usage. class MinHourCounter { public: MinHourCounter(); // Add a new data point (count >= 0). // For the next minute, MinuteCount() will be larger by +count. // For the next hour, HourCount() will be larger by +count. void Add(int count); // Return the accumulated count over the past 60 seconds. int MinuteCount(); // Return the accumulated count over the past 1 hour. int HourCount(); private: struct Event { Event(int count, time_t time) : m_count(count), m_time(time) { } int m_count; time_t m_time; // typedef long time_t; }; std::list<Event> m_minute_events; std::list<Event> m_hour_events; int m_minute_count; int m_hour_count; void ShiftOldEvents(time_t time); }; #endif // MINHOURCOUNTER_H
#include "minhourcounter.h" MinHourCounter::MinHourCounter() { } void MinHourCounter::Add(int count) { const time_t now_time_stamp = time(0); // ??? ShiftOldEvents(now_time_stamp); m_minute_events.push_back(Event(count, now_time_stamp)); m_minute_count += count; m_hour_count += count; } int MinHourCounter::MinuteCount() { const time_t now_time_stamp = time(0); ShiftOldEvents(now_time_stamp); return m_minute_count; } int MinHourCounter::HourCount() { const time_t now_time_stamp = time(0); ShiftOldEvents(now_time_stamp); return m_hour_count; } void MinHourCounter::ShiftOldEvents(time_t time) { const time_t minute_ago = time - 60; const time_t hour_ago = time - 3600; // minute_events while (!m_minute_events.empty() && m_minute_events.front().m_time <= minute_ago) { m_hour_events.push_back(m_minute_events.front()); m_minute_count -= m_minute_events.front().m_count; m_minute_events.pop_front(); } while (!m_hour_events.empty() && m_minute_events.front().m_time <= hour_ago) { m_hour_count -= m_hour_events.front().m_count; m_hour_events.pop_front(); } }
一些有用的细节:
1、名字 delta 通常用在值可以为负数的场合;count有计数的意思,通常暗示着“非负数”。
2、保持for循环的传统格式 for (begin; end; advance) {} 最容易理解。因此当for循环的判断部分由双重逻辑判断时,可以将其中之一修改为循环中的if()判断,满足则break;