《代码整洁之道》总结二之整洁的函数
来源:互联网 发布:雅思作文怎么准备 知乎 编辑:程序博客网 时间:2024/05/16 13:47
函数是所有程序中的第一组代码。
1、短小
函数的第一规则是短小。第二条规则是还要更短小。每个函数都一目了然,每个函数都只说一件事,而且每个函数都依次序把你带到下一个函数。这就是函数应达到的短小的程度。
代码块和缩进
if语句、else语句、while语句等,其中的代码块应该只有一行。函数的缩进层级不该多于一层或两层。
2、只做一件事
函数应该做一件事。做好这件事。只做这一件事。要判断函数是否不止做了一件事,就是看是否能再拆分出一个函数,该函数不仅只是单纯地重新诠释其实现。
3、每个函数一个抽象层级
要确保函数只做一件事,函数中的语句都要在同一抽象层级上。函数中混杂不同抽象层级,往往让人迷惑。读者无法判断某个表达式是基础概念还是细节。一旦细节与基础概念混杂,更多的细节就会在函数中纠结起来。
自顶向下读代码:向下规则
代码拥有自顶向下的阅读顺序。每个函数后面都跟着位于下一抽象层级的函数,这样在查看函数列表时,就能循抽象层级向下阅读了。
4、switch语句
写出短小的switch语句很难。即便是只有两种条件的switch语句也要比单个代码块或函数大得多。写出只做一件事的switch语句也很难。switch天生要做N件事。但是又无法避开switch语句,但是可以确保每个switch都埋藏在较低的抽象层级。可以通过多态来实现。
public Monkey calculatePay(Empoyee e) throws InvalidEmployeeType{ switch(e.type){ case COMMISSIONED: return calculateCommissionedPay(e); case HOURLY: return calculateCommissionedPayHourlypay(e); case SALARIED: return calculateSalariedPay(e); default: throw new InvalidEmployeeType(e.type); }}
上述函数的问题:
一、太长,当出现新的雇员类型事,还会更长。
二、做了不止一件事。
三、违反了单一权责原则,因为有好几个修改它的理由。
四、违反了闭合原则,每当添加新类型时,就必须修改。
五、最麻烦的是可能是到处都有类似结构的函数。例如可能会有isPayDay(Employee e ,Date date),或 deliveryPay(Employee e ,Money pay)等等,他们的结构都有同样的问题。
该问题的解决方案是将switch语句埋到抽象工厂底下,不让任何人看到。该工厂使用switch语句为Employee的派生物创建适当的实体,而不同的函数,如calculatePay、isPayday和deliverPay等,则藉由Employee接口多态地接受派遣。
public abstract class Employee{ public abstract boolean isPayday(); public abstract Money calculatePay(); public abstract void deliverPay(Money pay);}---------------------------------------------------------public interface EmployeeFactory{ public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;}---------------------------------------------------------public class EmployeeFactoryImpl implements EmployeeFactory{ public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType{ switch(r.type){ case COMMISSIONED: return new CommissionedEmployee(r); case HOURLY: return new HourlyEmployee(r); case SALARIED: return SalariedEmployee(r); default: throw new InvalidEmployeeType(r.type); } }}
5、使用描述性的名称
别害怕长名称。长而具有描述性的名称,要比短而另人费解的名称好。
命名方式要保持一致。使用与模块名一脉相承的短语、名称和动词给函数命名。
6、函数参数
最理想的的参数数量是零(零参数函数),其次是一(单参数函数),再次是二(双参数函数),应尽量避免三(三参数函数)。有足够特殊的理由才能用三个以上参数(多参数函数)--------------所以无论如何也不要这么做。
如果函数看起来需要两个、三个或三个以上参数,就说明其中一些参数应该封装为类了。
7、无副作用
在函数checkPassword中调用了Session.initialize()。副作用就在于调用了initialize()方法。chekPassword函数,顾名思义,就是用来检查密码的。该名称并未暗示它会初始化该次会话。所以,当某个误信了函数名的调用者想要检查用户有效性时,就得冒抹除现有会话数据的风险。
8、分隔指令与询问
函数要么做什么事,要么回答什么事,但二者不可兼得。
9、使用异常替代饭后错误码
从指令式函数饭后错误码轻微违反了指令与询问分隔的规则。它鼓励了在if语句判断中把指令当做表达式使用。
if(deletePage(page) == E_OK)
这不会引起动词/形容词混淆,但却导致更深层次的嵌套结构。当返回错误码时,要求调用者立刻处理错误。
抽离try/catch代码块
try/catch代码块丑陋不堪。它们搞乱了代码结构,把错误处理与正常流程混为一谈。最好把try和catch代码块的主体部分抽离出来,另外形成函数。
public void delete(Page Page){ try{ deletePageAndAllReferences(page); }catch(Exception e){ logError(e); }}private void deletePageAndAllReferences(Page page) throws Exception{ deletePage(page); registry.deleteReference(page.name); configKeys.deleteKey(page.name.makeKey);}private void logError(Exception e){ logger.log(e.getMessage());}
上述中,logError函数只与错误处理有关,很容易理解然后就忽略掉。deletePageAndAllReference函数只与完全删除一个page有关。错误处理可以忽略掉。有了这样美妙的区隔,代码就更易于理解和修改了。
函数应该只做一件事。错误处理就是一件事。因此,处理错误的函数不该做其它的事。使用异常代替错误码,新异常就可以从异常类派生出来,无需重新编译或重新部署。
10、别重复自己
重复可能是软件中一切邪恶的根源。许多原则与实践规则都是为控制欲消除重复而创建。
11、结构化编程
只要函数保持短小,偶尔出现的return、break或continue语句没有坏处,甚至比单入单出原则更有表达力。另一方面,goto只在大函数中才有道理,所以应该尽量避免使用。
12、如何写出这样的函数
写函数时,一开始都冗长而复杂。然后打磨这些代码,分解函数、修改名称、消除重复。缩短和重新安置方法。有时还拆散类。同时保持测试通过。
大师级的程序员把系统当做故事来讲,而不是当作程序来写。
- 《代码整洁之道》总结二之整洁的函数
- 代码整洁之道总结(二)
- <<代码整洁之道>>读书笔记二 函数
- 代码整洁之道(二)-------------函数
- 代码整洁之道总结
- 阅读《代码整洁之道》的总结
- 《代码整洁之道》的一些总结
- 《代码整洁之道》--函数
- 代码整洁之道------函数
- 代码整洁之道-函数
- 代码整洁之道--函数
- 代码整洁之道 函数
- 代码整洁之道-函数
- 代码整洁之道--函数
- [代码整洁之道]-函数
- 代码整洁之道--函数
- 代码的整洁之道
- [代码整洁之道]-整洁代码
- JSONP跨域获取JSON数据(含jQuery方法)——李帅醒博客
- ASCII码
- CSS基础-5 浮动,margin的bug,清除浮动
- 学习
- angular打开本地html文件报错
- 《代码整洁之道》总结二之整洁的函数
- QT5.7在VS2015中使用(2)
- ch-09 文件管理和外排序练习题
- Android学习 (十九) 之Fragment
- Linux内存管理浅析(一)
- 伴随矩阵,可逆矩阵相关思路分析之一
- 数据库创建
- Leetcode 115 Distinct Subsequences(不同的子序列)
- 勾股定理一日一证连载174