码迷,mamicode.com
首页 > 其他好文 > 详细

Clean Code 读书笔记三

时间:2015-06-07 09:33:59      阅读:110      评论:0      收藏:0      [点我收藏+]

标签:clean   java   代码   方法   

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

作者又该作何解释呢?

Clean Code 读书笔记三

标签:clean   java   代码   方法   

原文地址:http://blog.csdn.net/lemon89/article/details/46396723

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!