zoukankan      html  css  js  c++  java
  • 我读<代码整洁之道>--读书笔记整理

    第一章 整洁代码

      "我可以列出我留意到的整洁代码的所有特点,但其中有一条是根本性的,整洁的代码总是看起来像是某位特别在意他的人写的.几乎没有改进的余地,代码作者设么都想到了,如果你企图改进它,总会回到原点,赞叹某人留给你的代码" ---Michael Feathers 

      "整洁的代码只做好一件事" ---Bjarne Stroustrup 

    第二章 有意义的命名

      1 变量名要名副其实

      即顾名思义,变量.函数或者类的名称应该能告诉你,它为什么会存在,他做什么事,应该怎么用,名称应该尽量不需要注释补充亦能表示其含义.

      类名和对象应该是名词或者名词短语,方法名应该是动词或者动词短语.

      2 避免误导

    //不好的命名
    public boolean stateMachine(int smStatus) {
    //...
    }
    public boolean doAction() {
    //...
    }
    //好的命名
    public boolean moveStateMachineToStatus(int smStatus) {
    //...
    }
    public boolean doNextStepInProviderTechnique() {
    //...
    }

      3 做有意义的区分

        4 使用能读得出来的名称

      名字中不要有冗余,定义一个List类型变量,xxxList不如xxx的复数形式简洁,List字眼就属于冗余字段.

       5 命名布尔变量

       使用常见的布尔含义的命名: 如done;error;found;success;

       可以添加前缀is,has来转换布尔,但是isSuccess不如success可读性好

       避免使用负向变量命名:如notFound,设想if(!notFound)是不是觉得别扭.负向条件比正向条件更加难于理解,应该尽量避免.

    第三章    函数  

      1 短小

      这里是指一个函数的代码量,不是指函数名称要短小.函数应该是在做一件从语义上无法再次拆解的事情.

      2 一个函数只做一件事

      函数应该做一件事,做好这一件事,而且只做这一件事.

    写多长的函数比较合适?

      约定:不超过一屏幕,长度并不是问题,关键在于函数名称和函数体之前的语义距离,函数应该很短,也可以较长.只要保证函数只做这一件事.

    拆分函数的技巧?

      寻找注释,如果函数中某部分代码前面有一行注释,一般可以把这段代码替换成一个函数.此外,当感觉需要注释来说明一些什么的时候,就要考虑把要说明的这些东西封装成一个独立的函数.

      3 每个函数一个抽象层级

      这一点需要多一些说明,要确保函数只做一件事,函数的语句要在同一个抽象层级上.

    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fileId = request.getParameter("FILEID"); 
        if (fileId == null) {
            throw new ServletException("Invalid FileId"); }
        String partNum = request.getParameter("PART"); int part = 1;
        if (partNum != null) {
            part = Integer.valueOf(partNum).intValue(); }
            boolean isLast = "Y".equals(request.getParameter("LAST"));
            boolean getName="Y".equals(request.getParameter("GETNAME"));
            String fileName = 
            MitoDownloadCache.INSTANCE.getFileName(fileId, part); 
       if (fileName== null) {
            throw new ServletException("Invalid FileName"); }
        MitoConfig mitoCfg = new MitoConfig();
        File file = new File(mitoCfg.getPrintClientDataPath()+"/"+fileName); 
       if (!file.exists()) {
            throw new ServletException("File " + file.getAbsolutePath() + " not found");
    }
        if (getName) {
            doDownloadFilename(request, response, file.getName());
    } else {
        if (isLast) {
            MitoDownloadCache.INSTANCE.removeFileList(fileId); }
            doDownload(request, response, file); file.delete();
       }

      上面这个方法是我copy来的,不用研究这个函数的内容,只看一下结构,这个函数中既有比较高抽象层次的函数doDownloadFilename,doDownload,还有getFileName这种位于中间抽象层次的函数,更有很多像equals等较低抽象层次的概念.混杂了不同抽象层次,读起来就比较吃力,现在来改造一下:

    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        File file = fileToDownLoad(request); 
        if (downloadByName(request)) {
            doDownloadFilename(request, response, file.getName()); 
        }  else {
            removeFromCacheIfLast(request);
            doDownload(request, response, fileToDownLoad(request)); 
            file.delete();
    } }

      将功能拆分成红能更小更细的函数,封装成私有方法,起一个见名知义的函数名,使整个方法能够位于同一个抽象层次,然后在每个函数内部都跟着下一级抽象层次的函数,这样能给阅读带来极大的便利.(上面函数中私有方法这里不再展示)

      4 使用描述性名称

       函数越小,功能越集中,就越容易取一个合适的名字.另外不要害怕长名称,描述性很好的长名称比含糊不清的短名称要好.不要害怕花时间给函数起名字,起一个合适的名字本身就是编程的一部分.

      5 函数输入参数

      最理想的函数参数是0个(无参函数),其次是一个,二个,应该尽量避免三个以上的参数.

          尽量避免标示参数,即用一个boolean类型作为入参,比如下面这个函数,eat(true);这样读起来就有些摸不着头脑,可能读者需要去查看eat方法eat(boolean isHuntry)才能比较理解这个函数.

          如果函数必须要三个或三个以上的参数,就说明其中一些参数需要封装成类,如果很难封装成类,就说明这个函数的设计有问题.

      6 函数输出参数

      面向对象语言的方法应该尽量避免使用输出参数,当然那些util类型的工具类除外.this也有输出函数的意味,如果函数必须要修改某种状态,就修改所属对象的状态吧.

      7 抽离try/catch代码块

       try/catch代码块比较丑陋,搞乱了代码结构,最好把try/catch代码块的主体部分抽离出来,另外形成函数.

    函数应该只做一件事,错误处理就是一件事,因此,如果处理错误(带try/catch)的函数应该只处理错误,该函数只有try/catch不应该再包含其他内容.

      8 使用更少的代码

    //实例1:不好的代码格式
    public int size() {
      if (root == null) {
        return 0;
      }else {
        return root.numSiblings(); 
      }
    }
    //实例1:好的代码风格
    public int size() {
      return root != null ?root.numSiblings() : 0; 
    }
    //实例2 不好的代码
       List<Integer> list = Arrays.asList(1, 2, 3));
       list = Collections.unmodifieableList(list); 
       return list;
    //实例2 好的代码
       import static java.util.Arrays.*;
       import static java.util.Collections.*;
       ...
    
       return unmodifiableList(asList(1, 2, 3))

    实例2中给出的例子有些函数式编程的味道,这里只是提供一种思路,但大多数情况我们一般写出来的都是上面的格式.

    第四章 注释

      不写无意义的注释,什么是无意义的注释,如下:

      //当我写这段代码的时候,只有老天和我自己知道我在做什么

         //现在,只剩老天知道了

           ...

        //我不对以下代码负责

       //使他们逼我写的,是违背我意愿的  

      注释尽量做到简洁

      最好不写注释

      代码结合命名应该良好的描述功能,写注释说明表达意图的失败以及对代码功能的不自信,糟糕的代码是注释存在的动机之一.

      什么是必须要写的注释

        警告他人 版权协议 公共API 

    第五章 格式 

      1 格式的目的

      代码格式很重要,代码格式关乎沟通,而沟通是开发者的头等大事.

      2 垂直格式

        阅读代码都是从上往下,从左往右读的.在一个类中,在封包声明,导入声明,和每个函数之前都应该使用一个空白行来隔开.这可以给阅读带来愉悦的感受.

      空白行隔开了概念,每个空白行都是一条线索,标示出一个新的独立的概念,阅读代码的时候,我们的目光总是容易停留在空白行的前一行或后一行.

        变量声明 :变量声明应该尽量靠近其使用位置.类变量应该声明在类的顶部,

        相关函数:某函数A调用了函数B,应该尽量把他们放到一起,让A位于B的上方.

        概念相关:概念相关的代码应该放到一起,相关性越强,他们之间的距离就应该越短.

        避免过度缩进(代码梯子):

    //不好的代码风格
    public void startCharging() {
       if (customer.hasFunds()) {
          if (!station.isCharging()) {
             if (!station.currentlyBooked()) { 
                reallyStartCharging();
                return; 
             }
          }
       }
       throw new UnableToStartException(); 
    }
    //好的代码风格
    public void startCharging() {
       if (!customer.hasFunds()) throw new UnableToStartException
       if (station.isCharging()) throw new UnableToStartException
       if (station.currentlyBooked()) throw newUnableToStartException
        reallyStartCharging(); 
    }

      3 横向格式

      一行代码应该多宽?

      应当遵循无需拖动滚动条到右边的原则,每行代码控制在100个字符以内是良好的风格.

    第六章 对象和数据结构

      数据抽象

      当我们构建一个实体类时,会为变量设置为private,再为变量提供set/get方法,想一想为什么要这么做?实际上,即使变量都是私有,我们通过变量的set/get方法操作变量时,实现仍然被曝光了,那为何不直接将变量设置为public,然后直接操作变量呢?

      隐藏实现并非只是在变量之间放上一个函数层那么简单,更大的意义在于抽象,类并不是简单的用set/get方法将变量推向外间,而是暴露抽象接口,使得用户无需了解数据的实现就能够操作数据本体.  

      德墨忒尔定律(The Law of Demeter)

      得墨特定律认为,模块不应该了解它所操作的对象的内部情况.对象隐藏数据,曝光操作.

      类C的方法f只应该调用以下对象的方法

    1. C
    2. 由f创建的对象
    3. 作为参数传递给f的对象 
    4. 由C的实体变量持有的对象

      方法不应该调用任何由任何函数返回的对象的方法,一个很经典的比喻就是:不要和陌生人说话,只和朋友说话,还有一个比喻就是,人可以命令狗走路,但人不能直接命令狗的腿去走路,而应该由狗来控制自己的腿去走路.

      如果代码中充斥着"."构成的链式编程风格的代码.意味着该定律被被违反了.当然如果调用者是数据结构,没有行为只有数据,就可以忽略.

     

     

     ...未完

  • 相关阅读:
    Swift学习-Property
    Swift学习-protocol
    Swift学习-Class
    Swift学习-Enumerate、Structure
    iOS“此时无法下载应用”解决办法
    iTunes Connect用户职能与权限
    TestFlight Beta Testing 开发指南中英对照
    梳理一下KVC
    mac下使用github 上传代码(转)
    运用Runtime全局修改UILabel的默认字体
  • 原文地址:https://www.cnblogs.com/fingerboy/p/7411580.html
Copyright © 2011-2022 走看看