文章目录

  • clean code 之方法(函数)

clean code 之方法(函数)

  • 短小 ,再短小,更短小
    20行最佳

  • 只做一件事 准确说来每个方法应该是只做抽象概念上的的一件事
    只做一件事的方法是无法把逻辑分段的

  • 自顶向下的代码
    To say this differently, we want to be able to read the program as though it were a set of TO paragraphs, each of which is describing the current level of abstraction and referencing subsequent TO paragraphs at the next level down.

Notice
how each function introduces the next, and each function remains at a consistent level
of abstraction.

     To include the setups and teardowns, we include setups, then we include the test page con   tent, and then we include the teardowns.To include the setups, we include the suite setup if this is a suite, then we include the   regular setup.To include the suite setup, we search the parent hierarchy for the “SuiteSetUp” pageand add an include statement with the path of that page.To search the parent. . .

就是说,一个逻辑应该这样:
方法1:( 为了做A 我需要先做 B) 调用 方法2
方法2:(为了做B,我要做C,D)调用方法3,方法4
方法3:做C
方法4:做D

欣赏一下这段代码:

package fitnesse.html;import fitnesse.responders.run.SuiteResponder;import fitnesse.wiki.*;public class SetupTeardownIncluder {private PageData pageData;private boolean isSuite;private WikiPage testPage;private StringBuffer newPageContent;private PageCrawler pageCrawler;private SetupTeardownIncluder(PageData pageData) {this.pageData = pageData;testPage = pageData.getWikiPage();pageCrawler = testPage.getPageCrawler();newPageContent = new StringBuffer();}public static String render(PageData pageData) throws Exception {return render(pageData, false);}public static String render(PageData pageData, boolean isSuite)throws Exception {return new SetupTeardownIncluder(pageData).render(isSuite);}private String render(boolean isSuite) throws Exception {this.isSuite = isSuite;if (isTestPage()) {includeSetupAndTeardownPages();}return pageData.getHtml();}private boolean isTestPage() throws Exception {return pageData.hasAttribute("Test");}
//这个方法貌似做了四件事,但是是并列的四件事,这四件事都是 SetupAndTeardownPages 这件事的next level(关键之处),也可以理解为这件事的四个步骤。
// each function remains at a consistent level
// of abstractionprivate void includeSetupAndTeardownPages() throws Exception {includeSetupPages();includePageContent();includeTeardownPages();updatePageContent();}private void includeSetupPages() throws Exception {if (isSuite) {includeSuiteSetupPage();}includeSetupPage();}private void includeSuiteSetupPage() throws Exception {include(SuiteResponder.SUITE_SETUP_NAME, "-setup");}private void includeSetupPage() throws Exception {include("SetUp", "-setup");}private void includePageContent() throws Exception {newPageContent.append(pageData.getContent());}private void includeTeardownPages() throws Exception {includeTeardownPage();if (     ) {includeSuiteTeardownPage();}}private void includeTeardownPage() throws Exception {include("TearDown", "-teardown");}private void includeSuiteTeardownPage() throws Exception {include(SuiteResponder.SUITE_TEARDOWN_NAME, "-teardown");}private void updatePageContent() throws Exception {pageData.setContent(newPageContent.toString());}private void include(String pageName, String arg) throws Exception {WikiPage inheritedPage = findInheritedPage(pageName);if (inheritedPage != null) {String pagePathName = getPathNameForPage(inheritedPage);buildIncludeDirective(pagePathName, arg);}}private WikiPage findInheritedPage(String pageName)throws Exception {return PageCrawlerImpl.getInheritedPage(pageName, testPage);}private String getPathNameForPage(WikiPage page) throws Exception {WikiPagePath pagePath = pageCrawler.getFullPath(page);return PathParser.render(pagePath);}private void buildIncludeDirective(String pagePathName, String arg) {newPageContent.append("\n!include ").append(arg).append(" .").append(pagePathName).append("\n");}
}
  • switch statement ; if …else if()…else()…
public Money calculatePay(Employee e)
throws InvalidEmployeeType {switch (e.type) {case COMMISSIONED:return calculateCommissionedPay(e);case HOURLY:return calculateHourlyPay(e);case SALARIED:return calculateSalariedPay(e);default:throw new InvalidEmployeeType(e.type);}
}

想想这个方法有什么问题?

  1. 违法SRP

该方法不止做了一件事(作者这么说对么?难道这个不是next level abstraction?更上边的说法矛盾?那么怎么才算一件事呢?)
之所以说违法SRP,是因为这个方法可以说做了多件事,calculateCommissionedPay(e);calculateHourlyPay(e);calculateSalariedPay(e);
因为他们直接没有“then”的关系,他们是完全独立的同类型的事情。
也就是说,所谓一件事,你可以是a then b,then c.但是不能 if(…) do A; if(…) do B…

  1. 违法OCP
    新类型需要添加,则就必须修改代码。 可以扩展,但不能修改就是OCP

  2. 不够短,其实最严重的问题是 calculateCommissionedPay(e);calculateHourlyPay(e);calculateSalariedPay(e);里可能有相同结构的代码(如isPayday(Employee e, Date date),等等相同代码),那为什么不抽象出来呢

那么怎么重构?
作者认为通过 工厂去消化掉switch,动态的得到某个类型,利用多态的统一接口,在这个具体类型里去实现相应方法.
欣赏下:

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,作者觉得在用于多态的创建对象 是tolerant.什么意思?其实我的理解就是,你要用可以,应该用的抽象点,不能在具体方法里,具体逻辑里做.对吗?自己思考吧*/switch (r.type) {case COMMISSIONED:return new CommissionedEmployee(r);case HOURLY:return new HourlyEmployee(r);case SALARIED:return new SalariedEmploye(r);default:throw new InvalidEmployeeType(r.type);}}
}

那最后,原来的switch 变为:


public Money calculatePay(Employee e)
throws InvalidEmployeeType {e.calculatePay()
}
  • 方法命名宁可长,有描述性,也不要用什么鬼都看不懂的缩写!另外,就是命名一个"系列"的方法,保持他们的一脉相承
    如:

includeTeardownPages , includeSuiteTeardownPage , and includeTeardownPage

  • 方法参数越少越好,一般情况不要超过两个.

标识参数:
方法参数 为boolean 的这中标识参数,是不是可以说明你的方法做了两件事,1.true…2.false… 所以一般不要用标识参数

单参数(monadic):
通常两种意思,
1.Command——你要把这个参数转化或加工 valueOf(String s);
2.Query——你要提出关于这个参数的问题 isExists(T t)
3.另外还有一种不太常用,但是重要. 单参数的void方法,你要用这个参数去设置系统状态或其他什么——

The overall
program is meant to interpret the function call as an event and use the argument to alter the
state of the system, for example, void passwordAttemptFailedNtimes(int attempts) .

  • 参数对象
    多参数说明,这些参数有必要包装为一个类型

Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point center, double radius);

  • 参数列表(可变长参数) 同一类型的参数可以用参数列表

String.format("%s worked %.2f hours.", name, hours);
public String format(String format, Object… args)

  • 方法(参数) 对应 动词(名词)

writeField(name)

  • Command Query Separation
    public boolean set(String attribute, String value);
    作者的意思是说,方法要么做什么(void),要么回答什么(return something),他建议把上边的方法改为两个,一个判断并返回boolean is…(String attribute, String value);另一个 void set(String attribute, String value)。但是我看JDK源码大量违法。如:
public boolean remove(Object o) {if (o == null) {for (int index = 0; index < size; index++)if (elementData[index] == null) {fastRemove(index);return true;}} else {for (int index = 0; index < size; index++)if (o.equals(elementData[index])) {fastRemove(index);return true;}}return false;
}

作者又该作何解释呢?
这是我在stackoverflow的提问后,一个较为满意的答案:

  • 不要使用 错误码 这种东西
    比如:
public enum Error {OK,INVALID,NO_SUCH,LOCKED,OUT_OF_RESOURCES,WAITING_FOR_EVENT;
}

因为如果你要增加一个Error类型,使用到Error的代码都要重新编译、部署,不如使用exception(当然是衍生的).

  • try catch finally 只做Error Handling 这一件事
if (deletePage(page) == E_OK) {if (registry.deleteReference(page.name) == E_OK) {if (configKeys.deleteKey(page.name.makeKey()) == E_OK) {logger.log("page deleted");} else {logger.log("configKey not deleted");}} else {logger.log("deleteReference from registry failed");}
} else {logger.log("delete failed");return E_ERROR;
}

很明显,deletePage(page) 是Command(违反CQS)那就不要用来返回boolean并去做判断.
用 exception 代替 if esle 中对返回码的判断

try {deletePage(page);registry.deleteReference(page.name);configKeys.deleteKey(page.name.makeKey());
} catch (Exception e) {logger.log(e.getMessage());
}

try catch 里又有业务逻辑,作者建议try catch 里只有这个层面的抽象:
**提取try catch finally 代码块 **

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());
}

很明显,更具有条理!

总结:记住写代码就是讲一个逻辑故事,如何像写散文一样,写诗歌一样写出代码?

Clean Code 读书笔记三——方法(函数)相关推荐

  1. Clean Code读书笔记

    Clean Code读书笔记 一.整洁代码 如何做到整洁 怎么做 为什么 代码逻辑直接了当 便于理解 有意义的命名 便于理解 减少依赖关系 便于维护 分层战略完善错误 缺陷难以隐藏 有单元测试和验收测 ...

  2. 代码整洁之道 Clean Code 读书笔记

    目录 代码整洁之道 Clean Code 第一章 整洁代码 第二 三章 命名与函数 第四 五章注释与格式 第六章 对象和数据结构 第七章 错误处理 第八章 边界 第九章 单元测试 第十章 类 第十一章 ...

  3. 《Go 语言程序设计》读书笔记 (三) 方法

    方法 方法声明 在函数声明时,在其名字之前放上一个变量,即是一个方法.这个附加的参数会将该函数附加到这种类型上,即相当于为这种类型定义了一个独占的方法. package geometry import ...

  4. 《Go 语言程序设计》读书笔记 (三) 方法

    方法 方法声明 在函数声明时,在其名字之前放上一个变量,即是一个方法.这个附加的参数会将该函数附加到这种类型上,即相当于为这种类型定义了一个独占的方法. package geometryimport ...

  5. mysql数据库权威指南_MySQL_MySQL权威指南读书笔记(三),第二章:MYSQL数据库里面的数 - phpStudy...

    MySQL权威指南读书笔记(三) 第二章:MYSQL数据库里面的数据 用想用好MYSQL,就必须透彻理解MYSQL是如何看待和处理数据的.本章主要讨论了两个问题:一是SQL所能处理的数据值的类型:二是 ...

  6. 《How Tomcat Works》读书笔记(三)--Connector(连接器)

    <How Tomcat Works>读书笔记(三)--Connector(连接器) 这是<How Tomcat Works>第三四章的读书笔记.主要写了Tomcat4.0默认的 ...

  7. 《编程之美》读书笔记(三):烙饼问题与搜索树

    <编程之美>读书笔记三:烙饼问题与搜索树 薛笛 EMail:jxuedi#gmail.com 前面已经写了一些关于烙饼问题的简单分析,但因为那天太累有些意犹未尽,今天再充实一些内容那这个问 ...

  8. TCPIP详解Protocol 读书笔记(三) IP协议讲解

    TCP/IP详解:Protocol 读书笔记(三) Chapter3 IP:网际协议 文章目录 TCP/IP详解:Protocol 读书笔记(三) Chapter3 IP:网际协议 IP协议 IP数据 ...

  9. Hive学习笔记三之函数操作

    文章目录 5 函数 5.1 系统内置函数 5.2 常用内置函数 5.2.1 空字段赋值 5.2.2 CASE WHEN THEN ELSE END(类似于java中的switch case) 5.2. ...

最新文章

  1. 锁绑定多个条件Condition
  2. Linux下select, poll和epoll IO模型的详解
  3. sonar api 获取质量数据
  4. 计算机基础项目任务教学重构,面向计算思维培养的中职课程项目式重构研究
  5. Jupyter.net:使用Jupyter进行交互式计算的Windows应用程序
  6. 提高软件质量的10点
  7. centos7设置输入密码三次错误锁定账号
  8. if else if与if if语句的区别
  9. linux 安装phpMyAdmin
  10. 【洛谷P3804】统计每个子串出现的次数和长度(后缀自动机模版+拓扑序计数)
  11. jquery datatable 前端分页和后端分页例子
  12. 邯郸php,邯郸php程序员培训,邯郸php程序员培训费用,邯郸php程序员培训完工作好找吗...
  13. 夜神模拟器解决apk无法安装的问题
  14. 电子元器件行业SCM供应链平台降低管理成本,提升供应链系统效率
  15. [Phonegap+Sencha Touch] 移动开发29 安卓navigator.camera.getPicture得到图片的真实路径
  16. Zhong__PyCharm配置豆瓣源提升插件、依赖安装速度
  17. EasyUI的datagrid分页,动态隐藏或显示列
  18. STM32F103ZET6:CubeMX配置FSMC接口驱动SSD1963-7寸 TFTLCD
  19. 高新技术企业申报材料汇编
  20. placement new和placement delete

热门文章

  1. c#、java、钉钉、微信小程序、APP采集modbus TCP协义的PLC数据
  2. Eligible Segments (CF 1588 E)
  3. 【期货龙虎榜量价分析】红枣PVC创新高,多空大幅增仓博弈激烈
  4. 【上市啦】“Python 之父” 力荐的蓝皮书,你知道是哪本吗?
  5. 5款用于办公的电脑软件
  6. Android实现ListView的A-Z字母排序和过滤搜索功能
  7. 跳槽如何谈薪资?不以升职加薪为目的的跳槽,都是耍流氓!
  8. 4路电梯调度——pair program总结
  9. 孙振耀担任海辉董事会主席 自2008年3月生效
  10. 能做好上传下达吗(命令模式)