zoukankan      html  css  js  c++  java
  • Clean Code读书笔记(2)---函数

    1. 短小

    函数应该尽可能短小。每个函数20行封顶最佳。

    2. 只做一件事(单一职责)

    每个函数应该只做一件事,这样也可以保证函数尽可能短小。函数名字可以对代码进行自我描述。这样可读性会非常好。

    3. 每个函数只做一个抽象层级的事

    对于第二点 “一件事”的定义,每个人可能也有不同的理解。我们必须利用另一个概念来定义“一件事”。 抽象层级。函数类每行代码都必须在同一抽象层级上,这样函数就算是只做了一件事。那抽象层级又是什么呢?

    可以将程序定义成一系列的To起头的段落,每一个段落都描述当前抽象层级,并引用下一抽象层级。例如:

    To include the setup and teardown, we include setups, then we include the test page content, and then include the teardowns. (这里面include setup, include test page content, include teardowns是同一抽象层级,可以通过定义新的函数来隔离他们的复杂度。)

    To include the setups, we include the suite setup if this is a suite, then we include the regular setup(这里定义了include the setup这一个抽象层级,这里面的suite setup, regular setup是同一抽象层级,)

    To include the suite setup, we search the parent hierarchy and add an include statement with the path of that page(这里定义了include the suite setup这一个抽象层级,search, add an include statement属于同一抽象层级)

    分析一下如下代码,加深一下理解:

    package com.zboot.cleancode.chapter03;
    
    public class HtmlUtil {
    
        public static String testableHtml(PageData pageData, boolean isSuiteSetup) throws Exception {
    
            boolean isTestPage = pageData.hasAttribute("Test");
            if (isTestPage) {
                WikiPage testPage = pageData.getwikiPage();
                StringBuffer newPageContent = new StringBuffer();
                includeSetupPages(testPage, newPageContent, isSuiteSetup);
                newPageContent.append(pageData.getContent());
                includTearDownPage(testPage, newPageContent, isSuiteSetup);
                pageData.setContent(newPageContent.toString());
            }
            return pageData.getHtml();
        }
       // 上面这个函数其实包含了多个抽象层级,在同一抽象层级的应该是
    1. if has "test" attribute, include setup and tear down page(至于如何include setup and tear down,应该是下一抽象层级的事)
    2. generate html
    private static void includeSetupPages(WikiPage testPage, StringBuffer newPageContent, boolean isSuiteSetup) {
    
            WikiPage setup = PageCrawlerImpl.getInheritedPage("SetUp", testPage);
            if (setup != null) {
                WikiPagePath setupPath = testPage.getPageCrawler().getFullPath(setup);
                String setupPathName = PathParser.render(setupPath);
                newPageContent.append("!include -setup.").append(setupPathName).append("
    ");
            }
    
            if (isSuiteSetup) {
                WikiPage suiteSetup = PageCrawlerImpl.getInheritedPage(SuiteResponder.SUITE_SETUP_NAME, testPage);
                if (suiteSetup != null) {
                    WikiPagePath pagePath = suiteSetup.getPageCrawler().getFullpath(suiteSetup);
                    String pagePathName = PathParser.render(pagePath);
                    newPageContent.append("!include -setup.").append(pagePathName).append("
    ");
                }
            }
    
        }
    // 上面这个函数也包含了多个抽象层级,处于同一抽象层级的应该是
    1. include setup information(至于具体如何inlcude,是下一抽象层级的事)
    2. if should include suite setup, include suite setup information
    private static void includTearDownPage(WikiPage testPage, StringBuffer newPageContent, boolean isSuiteSetup) { WikiPage teardown = PageCrawlerImpl.getInheritedPage("TearDown", testPage); if (teardown != null) { WikiPagePath tearDownPath = testPage.getPageCrawler().getFullPath(teardown); String tearDownPathName = PathParser.render(tearDownPath); newPageContent.append(" ").append("!include -teardown.").append(tearDownPathName).append(" "); } if (isSuiteSetup) { WikiPage suiteTeardown = PageCrawlerImpl.getInheritedPage(SuiteResponder.SUITE_TEARDOWN_NAME, testPage); if (suiteTeardown != null) { WikiPagePath pagePath = suiteTeardown.getPageCrawler().getFullPath(suiteTeardown); String pagePathName = PathParser.render(pagePath); newPageContent.append("!include -teardown.").append(pagePathName).append(" "); } } } }
    // 上面这个函数也包含了多个抽象层级,处于同一抽象层级的应该是
    1. include teardown information(至于具体如何inlcude,是下一抽象层级的事)
    2. if should include suite setup, include suite teardown information
     

    经过分析,其抽象层次结构如下:

     然后代码按照这个思路可以重构成如下这样(由于多个函数需要共享一些参数,比如重新生成页面内容的StringBuilder,所以将整个过程封装为类型):

    package com.zboot.cleancode.chapter03;
    
    public class HtmlUtilCleanCode {
        private WikiPage testPage;
        private StringBuffer newPageContent;
        private boolean isSuiteSetup;
        private PageCrawler pageCrawler;
        private PageData pageData;
    
        public static String reader(PageData pageData) {
            return reader(pageData, false);
        }
    
        public static String reader(PageData pageData, boolean isSuite) {
            return new HtmlUtilCleanCode(pageData).reader(isSuite);
        }
    
        private HtmlUtilCleanCode(PageData pageData) {
            this.testPage = pageData.getwikiPage();
            this.pageCrawler = testPage.getPageCrawler();
            newPageContent = new StringBuffer();
        }
    
        private String reader(boolean isSuite) {
            this.isSuiteSetup = isSuite;
            if (this.isTestPage()) {
                this.includSetupAndTearPages();
            }
            return this.pageData.getHtml();
        }
    
        private boolean isTestPage() {
            return this.pageData.hasAttribute("test");
        }
    
        private void includSetupAndTearPages() {
            includeSetupPages();
            includePageContent();
            includeTearPages();
        }
    
        private void includeSetupPages() {
            if (this.isSuiteSetup) {
                include("SuiteResponder.SUITE_SETUP_NAME", "-setup");
            }
            includeSetupPage();
        }
    
        private void includeSetupPage() {
            include("SetUp", "-setup");
        }
    
        private void includePageContent() {
            newPageContent.append(pageData.getContent());
        }
    
        private void includeTearPages() {
            if (this.isSuiteSetup) {
                include("SuiteResponder.SUITE_SETUP_NAME", "-teardown");
            }
            includeTearPage();
        }
    
        private void includeTearPage() {
            include("TearDown", "-teardown");
        }
    
        private void include(String pageName, String args) {
            WikiPage innerPage = PageCrawlerImpl.getInheritedPage(pageName, this.testPage);
            if (innerPage != null) {
                WikiPagePath pagePath = innerPage.getPageCrawler().getFullpath(innerPage);
                String pagePathName = PathParser.render(pagePath);
                newPageContent.append("!include -").append(args).append(".").append(pagePathName).append("
    ");
            }
        }
    
    }

    4.使用描述性的名称

    函数功能越单一,越短小,就越便于取一个好的名字。函数名称要有自我描述的功能。读者看到函数名变知道函数的用途是什么。命名方式要保持一致。使用同一的短语,名词和动词给函数命名。

    5. 函数参数

    函数参数尽量保持在3个及3个以下。如果超过3个最好将其中一些参数封装成对象。

    6. 无副作用

    函数名用来描述函数的用途,如果再函数内部做了一些函数名描述不了的事情。则产生了副作用,我们应该尽量要避免函数有副作用。举例:

    public boolean checkPassword(String username, String password) {
      User user = UserGateway.findByName(userName);
      if(user != null) {
        if(user.Password == password) {
           Session.Initialize(); //这里产生了副作用。调用者如果不看代码的话有可能错误地调用该方法。
           return true;
        }
      }
      
      return false;  
    }

    7.分隔指令和询问 (读写应该分离)

    不好的例子:public boolean set(string attribute, string value)来实行写入数据到某个attribute.这里如果要写入数据,就不要有boolean类型的返回值。

    8. 使用异常替代返回错误码

    9. 抽离Try/Catch 块

    把Try Catch逻辑剥离出来单独作为一个函数,遵循单一职责原则。例如:

    public void delete(Page page) {
       try {
           deletePageAndAllReference(page);
       }
       catch(Exception e) {
           logError(e)
       }
    }
    
    private void deletePageAndAllReference(Page page){
    //.....
    }
    
    private void logError(Exception e) {
    //....
    }

    10. 遵守DRY原则

    Don't Repeat Yourself,一旦你发现有几处代码重复,说明你需要进行重构。抽取公共方法了。

  • 相关阅读:
    提升CPU性能的几个方面
    浅谈CPU性能问题
    计算机组成原理 1— 冯.诺依曼体系结构
    让你的微信公众平台中支持QQ在线客服功能
    common.js
    layui中解决ashx筛选数据时中文乱码问题
    html 使用rem开发
    html有用的占位符
    前端教学网站
    纯js实现回到锚点
  • 原文地址:https://www.cnblogs.com/Code-life/p/14202779.html
Copyright © 2011-2022 走看看