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” page
        and 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 abstraction
    private 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……..

  2. 违法OCP

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

  3. 不够短,其实最严重的问题是 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.你要把这个参数转化或加工 valueOf(String s);

2.你要提出关于这个参数的问题 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;
}

作者又该作何解释呢?

时间: 2024-08-04 09:50:37

Clean Code 读书笔记三的相关文章

clean code 读书笔记一

什么是 clean code ? 大神对优雅代码的定义: I like my code to be elegant and efficient. The logic should be straightforward to make it hard for bugs to hide, the dependencies minimal to ease maintenance, error handling complete according to an articulated strategy,

Clean Code 读书笔记五

Use Exception Rather than Return Code(使用异常而不是错误处理) public class DeviceController {... public void sendShutDown() { DeviceHandle handle = getHandle(DEV1); // Check the state of the device if (handle != DeviceHandle.INVALID) { // Save the device status

Clean Code 读书笔记九

要点汇总: 一般性问题 方法名称应该准确表达其具体行为 比如: Date newDate = date.add(5)//加5天?5个月? 最好使用addDaysTo 或increaseByDays 明确方法的行为. 使用多态 代替 switch或if else 比如: class RequestHandler { public void handleRequest(int action) { switch(action) { case LOGIN: doLogin(); break; case

Clean code 读书笔记二

细节汇总: 拒绝在容器类型的命名中使用 该容器类型 List flaggedCells = new ArrayList(); As we'll see later on, even if the container is a List , it's probably better not to encode the container type into the name. -相同的意思,使用一致的拼写,并保证专词专意,一次一意 Spelling similar concepts similar

《你必须知道的.NET》读书笔记三:体验OO之美

一.依赖也是哲学 (1)本质诠释:"不要调用我们,我们会调用你" (2)依赖和耦合: ①无依赖,无耦合: ②单向依赖,耦合度不高: ③双向依赖,耦合度较高: (3)设计的目标:高内聚,低耦合. ①低耦合:实现最简单的依赖关系,尽可能地减少类与类.模块与模块.层次与层次.系统与系统之间的联系: ②高内聚:一方面代表了职责的统一管理,一方面又代表了关系的有效隔离: (4)控制反转(IoC):代码的控制器交由系统控制而不是在代码内部,消除组件或模块间的直接依赖: (5)依赖注入(DI): ①

《世界是数字的》读书笔记 三

<世界是数字的>读书笔记 三 第六章 软件系统 操作系统是软件中的基础层,他负责管理计算机硬件,并为其他被称作应用程序的程序运行提供支持. 6.1操作系统 操作系统控制和分配计算机资源.首先,他负责管理CPU,调度和协调当前运行的程序.操作系统通常都需要管理数十个同时运行的进程或任务. 其次,操作系统管理RAM.他把程序加载到内存中以便执行指令. 最后,操作系统管理和协调外接设备的活动. 6.2操作系统怎么工作 计算机启动时首先要加载代码,加载的过程中还要检查硬件,比如哪些设备已经接入电脑,,

悟道—位IT高管20年的职场心经(读书笔记三)

悟道--一位IT高管20年的职场心经 第三章 世事洞明皆学问 职场就是你的大半个世界 是你一辈子也读不完的一大本书 想明白一个道理, 看明白一件事儿, 你就向成功迈进了一步. 1.1  "四行"说 四行是指: 第一,  你自己得行.自己的基础的能力是必须的,得靠自己学习. 第二,  得有人说你行.需要有伯乐,实际上是你得有一个自己的圈子,并且这些人都人认同你. 第三,  说你行的人得行.自己周围的圈子,里面也必须有牛人,只有在牛人的范围内,才能突显你自己的才能. 第四,  你身子骨得行

《R实战》读书笔记三

第二章  创建数据集 本章概要 1探索R数据结构 2使用数据编辑器 3数据导入 4数据集标注 本章所介绍内容概括如下. 两个方面的内容. 方面一:R数据结构 方面二:进入数据或者导入数据到数据结构 理解数据集 一个数据集通常由一个表格组合而成,行表示观测,列表示变量.病人的数据集如表1所示. 表1 病人数据集 数据集能够反映数据结构.数据类型和内容. 数据结构 R数据结构如图2所示. 图2:R数据结构 数据结构即数据的组织方式,R数据结构包括向量.矩阵.数组.数据框和列表等. R向量 R向量是一

《大型网站技术架构》读书笔记三:大型网站核心架构要素

一.性能—响应时间决定用户 (1)浏览器端: ①浏览器缓存: ②使用页面压缩: PS:Gzip压缩效率非常高,通常可以达到70%的压缩率,也就是说,如果你的网页有30K,压缩之后就变成了9K左右.想要启用Gzip压缩,提高浏览速度,可以浏览这篇文章:http://www.chinaz.com/web/2012/1017/278682.shtml ③合理布局页面: CSS:把样式表置于顶部:避免使用CSS表达式(expression_r):使用外部JavaScript和CSS:削减JavaScri