精华内容
下载资源
问答
  • 代码整洁之道 总结

    千次阅读 2019-05-19 17:31:15
    1.什么样的代码才是整洁的 从字面意思上理解,整洁的代码,对于程序员来说非常的一目了然,简单、整洁,结构清晰,逻辑清楚。代码其实是一种语言,传递的...首先便是要有保持代码整洁的意识,书中反复提到的提到的...

    1.什么样的代码才是整洁的

    从字面意思上理解,整洁的代码,对于程序员来说非常的一目了然,简单、整洁,结构清晰,逻辑清楚。代码其实是一种语言,传递的是逻辑,如果这份代码可以像我们说话一样快速的将逻辑传递给读者,那么这样一份代码就是一份整洁的代码。可以借助沃德原则:"如果每段代码欧让你感到深合已意,那就是整洁代码"

     

    2.如何保持代码整洁

    首先便是要有保持代码整洁的意识,书中反复提到的提到的一条童子军军规:让营地比你来时更干净。要写好整洁的代码,需要我们心中有着一个意识,我要写好我的代码,我的代码要让自己和别人看着舒服。

     

    3.命名

    3.1 介绍

    软件中命名随处可见,包括变量、函数、参数、类、目录等等,我们就像是一个父亲,给自己的孩子取一个让别人看到就知道的名字。

    3.2 名副其实

    通过名称就知道要表达的意思。例如:表示已经消逝的时间天数,使用elapsetimeInDays就比d好得多。表示插入数据成功,用insertSuccess就比flag好得多。

    3.3 避免误导

    命名时避免使用与本意相悖的词。例如:hp,aix,sco不应做变量名,因为它们是UNIX平台的专有名词。使用accountList来指一组账号,除非它真的是List类型。如果容纳账号的容器是Set,那就会引起错误的判断。在命名时最好不要把容器内容带上,使用accountGroup或者bunchOfAccounts更好。

    提防使用相差比较小的名称。例如:XYZControllerForEfficentHandingOfStrings和ZYZControllerForEfficientStorageOfStrings。

    误导性真正可怕的例子:用小写字母l和大写字母O做变量。

    3.4 有意义的区分

    光是添加数字或是废话来做区分是远远不够的。

    添加数字的反例:Spring中关于属性的拷贝方法 BeanUtils.copyProperties(source,target),参数就使用source和target来表示,而不是a1,a2.

    使用废话的反例:存在类名Product,ProductInfo,ProductData。名称不同,但意思无区别。废话都是冗余,Variable永远不该出现在变量名中。

    3.5 使用读得出来的名称

    例如: (生成日期,年月日时分秒) 变量命名时 gennerationTimeStamp 就比genymdhms好的多,前者比后者读起来好得多,也更好理解。

    3.6 使用可搜索的名称

    单字母名称和数字常量有个问题,很难在一大篇文字中找出。找MAX_CLASS_PER_STUDENT很容易,想找数字7就很麻烦。单字母名称仅用于短方法中的本地变量。名称长短应与其作用域相对应,比如在一个类中经常使用的,就可以通过大写字母的方式来表示。

    3.7 避免使用编码

    反例: 变量名加入成员前缀 m_name  表示接口时加上I字母 接口名:IShapeFactory

    3.8 避免思维映射

    在循环体中通过变量来判断条件时,使用i,j或者k就比其他字母好。千万别用小写字母l和打字字母O。

    3.9 类名

    类名和对象名应该是名字或者名字短语。如:Customer、WikiPage、Account

    3.10 方法名

    方法名应该是动词或动词短语,如:postPayment,deletePage或save。

    3.11 别扮可爱

    例如: 将数据一半进行删除 deleteHalfData 就比 ringingFinger(打响指) 好,只有看过复联的人才知道。

    3.12 每个概念对应一个词

    给每个抽象概念选一个词,并且一以贯之。例如:插入统一同insert或者save。不要有时用insert,有时用save.

    3.13 别用双关语

    例如:在一个类中有两个add方法。一个是求和,一个是添加数据。要么把求和改成sum,要么把添加数据改成append.

    3.14 使用解决方法领域名称

    只有程序员才会阅读你的代码。根据所涉领域来命名不是最好的方法,可以用一些计算机科学术语,算法名,模式名,数学术语来命名。例如:AccountVisitor,JobQueue

    3.15 使用源自所涉问题领域的名称

    如果不能使用程序猿熟悉的术语来命名,那就采用所设计问题领域的名称来命名吧。至少,维护代码的程序猿可以去请求领域专家。

    3.16 添加有意义的语境

    很少有名称是能自我说明的。你需要用有良好命名的类,函数或名称空间来放置名称,给读者提供语境。

    例如: 有名为firstName,lastName,street,houseNumber,city,state和zipcode的变量,当它们一起出现时,很明确构成一个地址。但如果是state单独出现时,就很难推断时表示地址了。可以通过addrState来标识。更好的方案是将类命名为Address。

    3.17 不要添加没用的语境

    假设有一个名为“豪华版加油站”(Gas Station Deluxe)的应用,在其中给每个类加上GSD前缀就不是一个好点子。只要短名称足够清楚,就不要添加不必要的语境。

    例如:GSDAccountAddress 就可以改成 AccountAddress

    3.18 小结

    我们有时候会怕其他开发者反对重命名。如果讨论一下就知道,如果名称改的更清楚,其实大家都会感激你。

     

    4.函数

    4.1 短小

    函数的第一规则是要短小。第二条规则是还要更短小。

    4.2 只做一件事

    函数应该只做一件事,做好这件事。

    4.3 每个函数一个抽象层级

    要确保函数只做一件事,函数中的语句都要在同一抽象层级上。函数中混杂不同抽象层级,往往让人迷惑。

    反例: 一个方法中调用了其他方法.有getHtml()抽象层比较高的,也有PathParser.render(pathPath)抽象层级中等的,也有builder.append("xxxx")抽象层级底层的。

    阅读代码时自顶向下阅读。每个函数后面都跟着位于下一个抽象层级的函数。

    例如: A方法中调用了B方法。B方法应该跟在A方法后面,如果A中调用了多个方法,这些方法也应该按照一定逻辑顺序排在A后面。

    4.4 使用描述性的名称

    例如:includeSetupAndTeardownPages、includeSetupPages、includeSuiteSetupPage和includeSetupPage等。看到这些方法名,我们就知道这个方法是在做什么事情了。函数越短小,功能越集中,那就越好取名字。别害怕长名称,长而具有描述的名称,比短小而令人费解的名称好。长而具有描述的名称,要比描述性的长注释好。

    4.5 函数参数

    最理想的函数是零参数函数,其次是单参数函数,再次是二,应尽量避免三参数函数。参数不易对付,它带有太多概念性。

    4.5.1 一元函数

    向函数传入单个参数由三种普遍的理由。一个是问关于那个参数的问题,例如 boolean fileExist("MyFile").另一个可能是操作该参数,将其转化成其他东西。例如 UserDTO transfer(User user)。还有一个就是事件,有输出参数无输出参数。程序将函数看作一个事件。

    4.5.2 二元函数

    有两个参数的函数要比一元函数难懂。例如:writeField(name)比writeField(outputStream,name)好懂。如果可以的话,把outputstream提取成当前类的成员变量,从而无需再传递它。

    4.5.3 三元函数

    有三个参数的函数要比二元函数难懂的多,排序,琢磨,忽略的问题都会加倍提现,建议在写三元函数前一定要想清楚。

    4.5.4 参数对象

    如果函数需要三个,或三个以上的参数,就说明其中的一些参数应该封装为类了。例如:Circle makeCircle(double x,double y,double radius) 和Circle makeCircle(Point center,double radius);

    4.6 动词与关键字

    给函数去个好名字,能较好地解释函数的意图,以及参数的顺序和意图。函数和参数应当形成一种非常良好的动词/名词对形式。例如:writeField(name)就比witre(name)表示的意图更加清晰

    4.7 无副作用

    副作用是一种谎言。函数承诺只做一件事,但还是会做其他被藏起来的事。例如:

    boolean checkPassword(userName,password){

    User user = UserGateway.findByName(userName);

    if(user == null){

    return false;

    }

    if(password.equals(user.getPassword()){

    Session.initialize();

    return true;

    }

    }

    在验证用户名和密码的方法中,进行了session的初始化操作,这就是该方法的副作用,方法名并没有暗示会进行session初始化的操作,当某个误信了函数名的调用者想要检查密码时,就可能导致当前session被抹除的风险。可以通过修改方法名为checkPasswordAndInitializeSession(),虽然哪还是违反了"只做一件事"的原则。可以将session的初始化放到另一个方法,将检查密码和初始化session分开,在上层来调用两个方法。

    4.8 分隔指令与询问

    函数要么做什么事,要么回答什么事,但二者不可兼得。

    反例:if(set("username","horizon")).该set方法的逻辑是先判断 是否有username属性,如果有,则设置horizon,返回true,如果没有则返回false.

    应该要把判断属性和设置值分两步来做.

    if(attributeExists("username")){

    setAttribute("username","horizon");

    }

    4.9 使用异常替代返回错误码

    从指令式函数返回错误码轻微违反了指令与询问分隔的规则。

    例如: if(deletePage(page) == E_OK) 当返回错误码时,就是在要求调用者根据返回值去处理逻辑。这样会导致深层次嵌套。

    直接在deltePage方法中,对错误的逻辑直接抛出异常,就不用去挨个判断返回了.

    例如:try{

    deletePage(page);

    }catch(Exception e){

     

    }

    4.10 抽离try/catch代码块

    try/catch代码块丑陋不堪,它们搞乱了代码结构,把错误处理与正常流程混为一谈。最好把try和catch中的代码块的主体部分抽离出来。

    例如:

    try{

    methodA();

    }catch(Exception e){

    logError();

    }

    4.11 错误处理就是一件事

    函数应该只做一件事,错误处理就是一件事。

    4.12 别重复自己

    重复可能是软件中一切邪恶的根源,许多原则与实践规则都是为空志与消除重复而创建。在我们的开发过程中,相同的逻辑可能在多处被使用。这时,我们就应该根据逻辑将相同的逻辑抽离出来。

    4.13 小结

    遵循这些规则,函数就会短小,有个好名字,就可以被很好的归置。不过永远别忘记,真正的目标在于讲述系统的故事,而你编写的函数必须干净利落地拼装到一起,帮助你讲故事。

     

    5.注释

    5.1 介绍

    注释的恰当用法是弥补我们在用代码表达意图时遭遇的失败,如果你发现自己需要写注释,再想想看是否有办法通过代码来表达。只有代码能忠实地告诉你它做的事情

    5.2 注释并不能美化糟糕的代码

    写注释的常见动机是糟糕的代码的存在,与其花时间编写解释你搞出的糟糕的代码的注释,不如花时间清洁那堆糟糕的代码。

    5.3 用代码来阐述

    有时,代码本身不足以解释其行为。但是其可能只有那么一两行,许多程序猿就觉得没有单独抽出去的必要,这种观点纯属错误。

    例如:

    //check to see if the employee is eligible for full benefits

    if((employee.flags & HOURLY_FLAG) && emplyee.age > 65)

    代码中对逻辑的判断添加了注释,但是完全有更优的方案,直接把判断的代码抽离成一个方法

    if(employee.isEligibleForFullBenefits())

    5.4 好注释

    有些注释是必须的,也是有利的。不过要记住,唯一真正好的注释是你想办法不去写的注释

    5.4.1 法律信息

    有时,公司代码规范要求编写与法律有关的注释。如果可能,就指向一份标准许可或其他外部文档,不要把所有条款放在注释中。

    5.4.2 提供信息的注释

    //Return an instance of the Responder being tested

    protected abstract Responder responderInstance(); 这类注释有时管用,但更好的方式是尽量利用函数名称传达信息。比如:将函数名改成responderBeingTested(),注释就是多余的了。

    5.4.3 对意图的解释

    有时,注释不仅提供了有关实现的有用信息,而且还提供了决定某个决定后面的意图。

    //the status of extract,2 means requested,3 means extracting.

    int getExtractStatus()

    5.4.4 阐释

    有时,注释把某些晦涩难懂的参数或返回值的意义翻译为某种刻度形式,也会是有用的。当然,更好的方法是尽量让参数或返回值自身就足够清楚

    例如: assertTrue(a.compareTo(b) == 0)  // a == b

    5.4.5 警示

    有时,用于警告其他程序猿会出现某种后果的注释也是有用的。

    例如:

    public static SimpleDateFormat makeStadardHttpDateFormat(){

    //SimpleDateFormat is not thread safe, so we

    //need to create each instance independently

    SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");

    }

    5.4.6 todo注释

    有时,有理由用//TODO形式在源代码中放置要做的工作列表。

    放大:注释可以用来放大某种看来不合理之物的重要性

    例如:

    String listItemContent = match.group(3).trim();

    //the trim is real important.It removes the starting

    //spaces that could cause thr item to be recognized

    //as another list.

    new ListItemWidget(this, listItemContent, this.level + 1);

    return buildList(text.substring(match.end()));

    公共API中的Javadoc:如果你在编写公共API,就该为它编写良好的Javadoc。就像其他注释一样,Javadoc也可能误导,不实用或者提供错误信息

    5.5 坏注释

    大多数注释都属此类。通过,坏注释都是糟糕的代码的支撑或借口,或者对错误决策的修正。

    5.5.1 喃喃自语

    如果知识因为你觉得应该或者因为过程需要就添加注释,那就是无谓之举。如果要写注释,就要话必要的时间写出最好的注释。

    5.5.2 多余的注释:

    例如:class User {

    //the name of user

    private String name;

    }

    5.5.3 误导性注释

    有时,尽管初衷可嘉,程序猿还是会写出不够精确的注释,最终导致误导了读者。

    5.5.4 循规式注释

    所谓每个函数都要有Javadoc或每个变量都要有注释的规矩是不可取的,这类注释徒然让代码变得散乱。

    5.5.5 日志式注释

    有人在每次编写代码时,在模块开始出添加一条注释,表示本次修改的原因和日期。很久之前没有代码控制仓库还可以理解,不过有了代码仓库之后,这种注释就完全没有必要了。

    5.5.6 废话注释

    有时,你会看到纯然是废话的注释。

    例如:

    /**

    Default constructor
    **/
    protected User(){

     

    }

    5.5.7 位置标记

    有时,程序猿喜欢在源代码中标记某个特别位置.

    例如:

    // Actions //

    5.5.8 括号后面的注释

    有时,程序猿会在括号后面放置特殊的注释。尽管这对于含有深度嵌套的长函数可能有意义。不过,最好的方法应该是将长函数拆分成一个个短函数

    5.5.9 归属与署名

    代码仓库能够记住是谁在合适添加了什么,没必要用那些签名来搞脏代码。

    5.5.10 注释掉的代码

    直接把代码注释掉是讨厌的做法,不用了就直接删掉,有代码仓库帮我们保存,不用留着。

    5.5.11 HTML注释

    源代码注释中的HTML标记是一种错误的做法,在代码中应避免它。

    5.5.12 信息过多

    别在注释中添加有趣的历史性话题或无关的细节描述。

    5.5.13 不明显的联系

    注释及其描述的代码之间的联系应该显而易见。如果你要写注释,至少让读者能看着注释和代码,并且理解注释所谈何物

    5.5.14 函数头

    短函数不需要太多描述。为只做一件事的短函数选个好名字,通常要比写函数头注释要好

    5.5.15 非公共代码中的Javadoc

    虽然Javadoc对于公共API非常有用,但对于不打算作公公用途的代码就完全没有必要用Javadoc了。

    6.格式

    6.1 目的

    代码格式很重要,必须严肃对待。或许你认为"代码能工作"才是专业开发着的头等大事。然而,事实并不是这样的,今天编写的功能,下一版可能被修改,原始代码修改之后很久,其代码风格和可读性仍会影响后续开发的人。即便代码不存在,你的风格和律条仍然存活下来。

    6.2 垂直格式

    大部分出色的系统中,单个文件长度差不多200行-500行左右,尽管这并非不可违背的原则,也应该接受。短文件比长文件易于理解。

    6.2.1 向报纸学习

    源文件要向报纸那样,名称应当简单且一目了然,源文件最顶部应该给出高层次的概念和算法,细节应该往下逐次展开,直至最底层的函数和细节。

    6.2.2 概念间垂直方向上的区隔

    代码都是从上往下,从左往右读。每组代码行展示一条完整的思路,这些思路用空白行区隔开。例如:封包声明,导入声明和每个函数之间,都由空白行隔开。

    6.2.3 垂直方向上的靠近

    如果说空白行隔开了概念,那么靠近的代码则暗示了它们之间的紧密关系,紧密相关的代码应该互相靠近。

    例如:同一个概念的方法应该靠近在一起

    6.2.4 垂直距离

    对于那些关系密切、放置与同一源文件中的概念,它们之前的区隔应成为易懂性的衡量标准,应避免读者在源文件和类中跳来跳去。

    1.变量声名:变量声明应尽可能靠近其使用位置 

    反例:  

    InputStream is = null;

    //间隔了十多行代码

    is = new FileInputStream("myFile");

    2.实体变量:实体变量应该在类的顶部声明

    3.相关函数:若某个函数调用了另外一个,就应该把它们放在一起,而且调用者应尽可能在被调用者上面。

    4.概念相关:概念相关的代码应该放在一起,相关性越强,彼此之前的距离就该越短。

    6.2.5 垂直顺序

    我们想自上向下展示函数调用依赖顺序,被调用函数应该在执行调用函数的下面,这样就建立了一种自顶向下贯穿模块的良好信息流。

    6.3 横向格式

    应该尽力保持代码行短小,一般来说,代码行的长度不要超过120个字符

    6.3.1 水平方向上的区隔与靠近

    我们使用空格字符将紧密相关的事物连接在一起,也用空格字符把相关性较弱的事物分隔开。

    1.赋值:赋值语句的等号两边由空格隔开,因为赋值有两个确定的要素,左边和右边,空格字符加强了分隔效果。

    例如: int lineSize = line.length();

    2.函数名和参数:不在函数名和左圆括号之间加空格,因为函数与参数密切相关,如果隔开,就会显得没有关系

    3.运算符:乘法因子之间不加空格,因为它们具有较高优先级。加减法之前用空格隔开,因为加减法优先级较低

    6.3.2 缩进

    大多数类声明,不缩进。类的方法相对该类缩进一个层级,方法的实现相对方法声明缩进一个层级。代码块的实现相对于其容器代码块索引一个层级,以此类推。不要在短小的if语句,while循环中违反缩进规则。

    反例:public CommentWidget(ParentWidget parent, String text){super(parent, text);}

    6.3.3 空范围

    有时,while或for语句的语句体为空。我们很容易被while循环语句同一行末尾的分号欺骗,尽量不要这样使用。

    反例: while(dis.read(buf, 0, readBufferSize) != -1);

    6.4 团队规则

    每个程序猿都有自己喜欢的格式规则,但如果在一个团队工作,就是团队说了算。一组开发者应当认同一种格式风格,每个成员都应该采用那种规则。

     

    7.对象和数据结构

    7.1 数据抽象

    对类的设计应该要隐藏具体的实现,隐藏实现关乎抽象。类并不是简单地用取值器和赋值器将其变量推向外间,而是暴露抽象接口,以便用户无需了解数据的实现就能操作数据本体。

    例如:

    具象点:

    public class Point{

    public double x;

    public double y;

    }

    抽象点:

    public interface Point{

    double getX();

    double getY();

    void setCartesian(double x, double y);

    void setPolar(double r, double theta);

    }

    7.2 数据、对象的反对称性

    对象把数据隐藏于抽象之后,暴露操作数据的函数。而数据结构暴露其数据,不提供有意义的函数。它们其实是对立的。

    对象和数据结构之间存在二分原理:过程式代码难以添加新数据结构,因为必须修改所有函数。面向对象代码难以添加新函数,因为必须修改所有类。这其实就是一个抽象和具体的一个差异,对象其实是比较抽象的,而数据结构是具体的。

    7.3 得墨忒耳律

    得墨忒耳律(The Law of Demeter)认为,模块不应了解它所操作对象的内部情形,对象隐藏数据,暴露操作。方法不应调用由任何函数返回的对象的方法,换言之,只跟朋友谈话,不与陌生人谈话。

    反例:ctx.getOptions().getScratchDir();因为它调用了getOptions()返回值的getScratchDir()函数。

    7.4 数据传送对象

    最为精炼的数据结构,是一个只有公共变量、没有函数的类,这种数据结构就是我们用的DTO。在数据库通信、解析传递的参数时,是最常用的。

    7.5 小结

    对象暴露行为,隐藏数据。便于添加新对象类型而无需修改现有行为,同时也难以在既有对象中添加新行为。

    数据结构暴露数据,没有明显的行为。便于向既有数据结构添加新行为,同时也难以向已有函数添加新数据结构。

     

    8.错误处理

    8.1 使用异常而非返回码

    如果我们在某些判断某些操作的执行情况时,手动返回操作成功或者失败。那么就会导致一个问题,我们必须在调用这个方法之后立即检查执行情况。不如直接在操作失败是直接抛出异常,直接调用时不抛异常,就表示操作是成功的

    例如: 

    public Boolean handle(){

         int x = operation();

         return x > 0;

    }

    通过operation的返回值来判断是否操作成功,不如在operation内部操作失败时直接抛出异常。

    public Boolean handle(){

         operation();

         return true;

    }

    8.2 先写Try-Catch-Finally语句

    异常的好处之一是,它们在程序中定义了一个范围。执行try部分的代码时,你是在表明可随时取消执行,并在catch语句中续接。在编写可能抛出异常的代码时,先写出try-catch-finally语句,这能帮你定义应该需要做什么,无论try代码块中执行的代码出什么错都一样。

    8.3 使用可控异常时catch链不要过深

    当我们在方法里主动throw了一个异常之后,可能catch语句在这个方法的三个层级之上,这意味这每个调用该方法的函数都要修改,捕获新异常。以此类推,最终得到的就是一个从底层贯穿到顶层的修改链。

    8.4 给出异常发生的环境说明

    你抛出的每个异常,应创建信息充分的错误消息,和异常一起传递出去,包括失败的操作和失败类型。

    8.5 定义常规流程

    按照8.1所说,使用异常而非返回码,这样的话代码会变得简洁,但是可能会出现下面这种情况。

    try{

         MealExpenses expenses = expenseReportDAO.getMeals(employee.getId());

         total += expenses.getTotal;

    }catch(MealExpensesNotFound e){

         total += getMealPerDiem();

    }

    业务逻辑是,如果消耗了餐食,则计入总额。如果没有消耗,则员工得到当日餐食补贴。我们在判断是否消耗餐食时,通过抛出数据没有找到的异常来做逻辑判断,这其实是不合理的。应该在expenseReportDAO.getMeals()方法中,如果没有查询到对应的数据,就返回一个参数补贴的对象,而不是在异常里面去继续处理逻辑。

    8.6 别返回null值

    要讨论错误处理,就要提及到容易引发错误的做法,返回null值就是其中的一个。我们需要在每次调用都去判断是否为null,其实在增加我们的工作量,如果忘记检查,程序就会失控。不如直接抛出异常或者判处特例对象。

    例如:

    List<Employee> employees = getEmployees();

    if(employees != null){

       for(Employee e : employees){

          System.out.println(e.getName());

    }

    }

    其实,我们只要在getEmployees()方法中,如果结果为null时,返回一个空的List,就没有必要去判断是否为null了

    8.7 小结

    如果将错误处理隔离看待,独立于主要逻辑之外,我们就能单独处理它,也极大提升了代码的可维护性和整洁性。

    9.最终收获

    我知道了我要写好我的代码。

    展开全文
  • 代码整洁之道总结

    2017-11-13 19:05:58
    整洁规则1、使用有意义的命名,有更好的命名随时替换 2、使用可搜索的名称 3、不需要使用前缀或后缀 4、类名和对象名应该是名词或名词短语 5、方法名应该是动词或动词短语 6、别用双关语,做到一词一义 7、添加有...

    整洁规则

    (一)时时保持代码整洁

    (二)有意义的命名

    1、使用有意义的命名,有更好的命名随时替换
    2、使用可搜索的名称
    3、不需要使用前缀或后缀
    4、类名和对象名应该是名词或名词短语
    5、方法名应该是动词或动词短语
    6、别用双关语,做到一词一义
    7、添加有意义的语境
    

    (三)函数

    1、函数第一规则是要短小
    2、代码块和缩进(if、else、while等语句,其中代码块应该只有一行)
    3、函数应该做一件事,做好这件事,只做这一件事
    4、最理想的函数参数是零
    5、抽离try/catch代码块的主体
    

    (四)注释

    1、注释要少量而有力的表达
    2、用代码来阐述
    3、好注释包括法律信息、提供信息、警示、todo形式等
    4、坏注释包括多余的、误导性的、归属与署名、注释掉的代码
    

    (五)格式

    1、垂直方向上使用空白行隔开
    2、水平方向上使用空格隔开
    3、while和for语句的语句体不为空
    

    (六)对象和数据结构

    1、得墨忒耳律---模块不应该了解他所操作对象的内部情形
    2、火车失事---连串的调用被认为是很肮脏的风格
    3、最为精炼的数据结构,是一个只有公共变量、没有函数的类
    

    (七)错误处理

    1、使用异常而非返回码
    2、
    
    展开全文
  • 1、任何函数只能做一件事这是迄今为止软件...2、函数名应该描述他们所做的事情3、删除重复的代码尽你最大的努力来避免重复的代码。重复代码不好,因为它意味着如果你修改一些逻辑,那就有不止一处地方要同步修改了。...

    1、任何函数只能做一件事

    这是迄今为止软件工程里最重要的一个规则。当函数做超过一件事的时候,他们就难于实现、测试和理解。当你隔离函数只剩一个功能时,他们就容易被重构,然后你的代码读起来就更清晰。如果你光遵循这条规则,你就领先于大多数开发者了。


    2、函数名应该描述他们所做的事情


    3、删除重复的代码

    尽你最大的努力来避免重复的代码。重复代码不好,因为它意味着如果你修改一些逻辑,那就有不止一处地方要同步修改了。

    经常你容忍重复代码,因为你有两个或更多有共同部分但是少许差异的东西强制你用两个或更多独立的函数来做相同的事。移除重复代码意味着创造一个处理这组不同事物的一个抽象,只需要一个函数/模块/类。

    抽象正确非常重要,这也是为什么你应当遵循SOLID原则(奠定Class基础的原则)。坏的抽象可能比重复代码还要糟,因为要小心。在这个前提下,如果你可以抽象好,那就开始做把!不要重复你自己,否则任何你想改变一件事的时候你都发现在即在更新维护多处。


    4、不要用标志作为函数的参数,标志告诉你的用户函数做很多事了。函数应当只做一件事。 根据布尔值区别的路径来拆分你的复杂函数。

    5、避免副作用

    如果一个函数不是获取一个输入的值并返回其它值,它就有可能产生副作用。这些副作用可能是写入文件、修改一些全局变量,或者意外地把你所有钱转给一个陌生人。

    现在你确实需要在程序中有副作用。像前面提到的那样,你可能需要写入文件。现在你需要做的事情是搞清楚在哪里集中完成这件事情。不要使用几个函数或类来完成写入某个特定文件的工作。采用一个,就一个服务来完成。

    关键点是避免觉的陷阱,比如在没有结构的对象间共享状态,使用可以被任意修改的易变的数据类型,没有集中处理发生的副作用等。如果你能做到,你就能比其他大多数程序员

    更愉快。

    比如:错误的代码

    $name = 'Ryan McDermott';
    
    function splitIntoFirstAndLastName() 
    
    {  $name = preg_split('/ /', $name);}
    
    splitIntoFirstAndLastName();
    
    var_dump($name); // ['Ryan', 'McDermott'];

    正确的代码

    function splitIntoFirstAndLastName($name) 
    
    {  return preg_split('/ /', $name);}
    
    $name = 'Ryan McDermott';
    
    $newName = splitIntoFirstAndLastName(name);

    6、不要写全局变量

    在大多数语言中污染全局变量是一个坏的实践,因为你可能和其他类库冲突并且你api的用户不明白为什么直到他们获得产品的一个异常。让我们看一个例子:如果你想配置一个数组,你可能会写一个全局函数像config(),但是可能和试着做同样事的其他类库冲突。这就是为什么单例设计模式和简单配置会更好的原因


    比如:不好的程序

    function config() {
      return  [
        'foo': 'bar',
      ]
    };

    好的程序

    class Configuration {
      private static $instance;
      private function __construct($configuration) {/* */}
      public static function getInstance() {
         if(self::$instance === null) {
             self::$instance = new Configuration();
         }
         return self::$instance;
     }
     public function get($key) {/* */}
     public function getAll() {/* */}
    }
    
    $singleton = Configuration::getInstance();


    7、封装条件语句

    例如:不好的程序

    if ($fsm->state === 'fetching' && is_empty($listNode)) {
      // ...
    }

    好的程序

    function shouldShowSpinner($fsm, $listNode) {
      return $fsm->state === 'fetching' && is_empty(listNode);
    }
    
    if (shouldShowSpinner($fsmInstance, $listNodeInstance)) {
      // ...
    }


    8、避免消极条件

    不好的程序

    function isDOMNodeNotPresent($node) {
      // ...
    }
    
    if (!isDOMNodeNotPresent($node)) {
      // ...
    }

    好的程序

    function isDOMNodePresent($node) {
      // ...
    }
    
    if (isDOMNodePresent($node)) {
      // ...
    }

    9、避免条件声明

    这看起来像一个不可能任务。当人们第一次听到这句话是都会这么说。 "没有一个if声明" 答案是你可以使用多态来达到许多case语句里的任务。第二个问题很常见, “那么为什么我要那么做?” 答案是前面我们学过的一个整洁代码原则:一个函数应当只做一件事。当你有类和函数有很多if声明,你自己知道你的函数做了不止一件事。记住,只做一件事。

    坏的程序:
    class Airplane {
      // ...
      public function getCruisingAltitude() {
        switch (this.type) {
          case '777':
            return $this->getMaxAltitude() - $this->getPassengerCount();
          case 'Air Force One':
            return $this->getMaxAltitude();
          case 'Cessna':
            return $this->getMaxAltitude() - $this->getFuelExpenditure();
        }
      }
    }

    好的程序

    class Airplane {
      // ...
    }
    
    class Boeing777 extends Airplane {
      // ...
      public function getCruisingAltitude() {
        return $this->getMaxAltitude() - $this->getPassengerCount();
      }
    }
    
    class AirForceOne extends Airplane {
      // ...
      public function getCruisingAltitude() {
        return $this->getMaxAltitude();
      }
    }
    
    class Cessna extends Airplane {
      // ...
      public function getCruisingAltitude() {
        return $this->getMaxAltitude() - $this->getFuelExpenditure();
      }
    }

    10、避免类型检查

    PHP是弱类型的,这意味着你的函数可以接收任何类型的参数。 有时候你为这自由所痛苦并且在你的函数渐渐尝试类型检查。有很多方法去避免这么做。第一种是考虑API的一致性。

    坏的程序

    function travelToTexas($vehicle) {
      if ($vehicle instanceof Bicycle) {
        $vehicle->peddle($this->currentLocation, new Location('texas'));
      } else if ($vehicle instanceof Car) {
        $vehicle->drive($this->currentLocation, new Location('texas'));
      }
    }

    好的程序

    function travelToTexas($vehicle) {
      $vehicle->move($this->currentLocation, new Location('texas'));
    }
    

    11、避免类型检查

    如果你正使用基本原始值比如字符串、整形和数组,你不能用多态,你仍然感觉需要类型检测,你应当考虑类型声明或者严格模式。 这给你了基于标准PHP语法的静态类型。 手动检查类型的问题是做好了需要好多的废话,好像为了安全就可以不顾损失可读性。保持你的PHP 整洁,写好测试,做好代码回顾。做不到就用PHP严格类型声明和严格模式来确保安全。


    坏的程序:

    function combine($val1, $val2) {
      if (is_numeric($val1) && is_numeric(val2)) {
        return val1 + val2;
      }
    
      throw new \Exception('Must be of type Number');
    }

    好的程序

    function combine(int $val1, int $val2) {
      return $val1 + $val2;
    }

    12、移除僵尸代码

    僵尸代码和重复代码一样坏。没有理由保留在你的代码库中。如果从来被调用过,见鬼去!在你的版本库里是如果你仍然需要他的话,因此这么做很安全。

    13、使用有意义可拼写的变量名

    坏的程序

    $ymdstr = $moment->format('y-m-d');

    好的程序

    $currentDate = $moment->format('y-m-d');

    14、同种类型的变量,使用相同的词汇

    坏的程序

    getUserInfo();
    getClientData();
    getCustomerRecord();

    好的程序

    getUser();

    15、使用易检索的名称

    我们会读比写要多的代码。通过是命名易搜索,让我们写出可读性和易搜索代码很重要。

    16、使用解释型变量

    17、避免心理映射

    明确比隐性好。


    18、不要添加不必要上下文

    如果你的class/object 名能告诉你什么,不要把它重复在你变量名里。

    19、使用参数默认值代替短路或条件语句。

    坏的程序

    function createMicrobrewery($name = null) {
      $breweryName = $name ?: 'Hipster Brew Co.';
      // ...
    }

    好的程序

    function createMicrobrewery($breweryName = 'Hipster Brew Co.') {
      // ...
    }

    20、函数参数最好少于2个

    限制函数参数个数极其重要因为它是你函数测试容易点。有超过3个可选参数参数导致一个爆炸式组合增长,你会有成吨独立参数情形要测试。

    无参数是理想情况。1个或2个都可以,最好避免3个。再多旧需要加固了。通常如果你的函数有超过两个参数,说明他多做了一些事。 在参数少的情况里,大多数时候一个高级别对象(数组)作为参数就足够应付。







    展开全文
  • 代码整洁 提高代码质量,为后期维护、升级奠定基础 第一章 编写可读的代码 关注细节 变量名命名 重视代码整洁 学会总结,回看之前写过的代码,多写代码 why bad code 时间不够、思路不对、难实现 稍后等于...

    代码整洁

    提高代码质量,为后期维护、升级奠定基础

    第一章

    1. 编写可读的代码

    2. 关注细节

    3. 变量名命名

    4. 重视代码整洁

    5. 学会总结,回看之前写过的代码,多写代码

      why bad code

      时间不够、思路不对、难实现

      稍后等于永不 LeBlanc

      Bjarne Stoystrup C++语言发明者
      代码逻辑直截了当
      减少依赖关系,便于维护
      依据某种分层战略完善错误处理代码
      性能调至最优

      简单代码规则 Ron Jeffries

      1. 能通过所有测试
      2. 没有重复代码
      3. 提现系统中的全部设计理念
      4. 包括尽量少的实体,比如类、方法、函数等

      总结 减少重复代码、提高表达力,提早构建简单抽象。

      修改变量名、拆分过长的函数、消除重复代码

    第二章

    1. 根据具体用处命名
    2. 避免使用有歧义的变量名
    3. 小心使用差别较小的名称
    4. 明确数据类型或者说是什么用处
    5. 命名尽量可读可搜索
    6. 把类和函数写的尽量小
    7. 用名词/名称短语来命名类
    8. 方法名应该是动词/动词短语
    9. 尽量用通用的一些名称来表示
    10. 相同单词用于相同的目的
    11. 使用计算机专业术语
    12. 添加变量名所在的有意义的语境
    13. 不管重构还是自己写都需要重视命名,增加代码可读性

    第三章 函数

    1. 函数需要短小
    2. 判断函数能否在拆出一个函数来
    3. 自顶向下读代码 向下规则
    4. 函数名称尽量具体、有描述性
    5. 函数参数尽可能少
    6. 函数参数较多时,应封装成类
    7. 全局变量和局部变量要区分好
    8. 函数只做一件事
    9. 抽离异常处理语句
    10. 消除重复
    11. 结构化编程 每一个代码块有一个入口和一个出口 只有一个return 循环没有continue 和break 不能有goto
    12. 可以先写出整体逻辑代码,不断优化
    13. 程序就像是故事

    第四章 注释

    1. 注释是你代码编写的失败
    2. 注释很少去维护
    3. 尽量少写注释
    4. 源头还会代码太糟糕
    5. 还是有一些必要的注释(法律信息、信息的注释、对意图的解释、出现某些特定的情况或者问题的注释)
    6. TODO 以后要写的东西,现在还没实现
    7. 保证注释的正确性
    8. 删除没有意义的注释
    9. 日志式注释应做相应的删除
    10. 不要的代码直接删掉
    11. 注释信息太多
    12. 注释和相关代码联系紧密
    13. 代码重构和注释重构

    第五章 格式

    1. 保持良好的代码格式,尽量使用相同的格式规则
    2. 格式会影响之后代码维护者
    3. 大致的结构和框架要搭好
    4. 空格和空行增加代码阅读性
    5. 关联紧密的代码应该相互靠近
    6. 不要把关系密切的概念放在不同的文件中
    7. 变量声明靠近使用位置(函数顶部)
    8. 实体变量在类的顶部声明
    9. 空格可以强调字符或运算符

    第6章 对象和数据结构

    1. 对象内部的结构要隐藏不应该暴露出去
    2. 以抽象的形态表述数据
    3. 过程式代码和面向对象
    4. 得墨忒耳定律
      每个单元对于其他的单元只能拥有有限的知识:只是与当前单元紧密联系的单元;
      每个单元只能和它的朋友交谈:不能和陌生单元交谈;
      只和自己直接的朋友交谈。
    5. 避免混杂的数据结构或对象
    6. Active Record 类似flask-sqlalchemy

    第7章 错误处理

    1. 必要时需要写异常处理代码
    2. 针对特定语句可能抛出异常的语句增加异常,结构清楚
    3. 避免传递null值
    4. 错误处理隔离看待,独立于主要逻辑之外,增加代码的可维护性

    第8章 边界

    第9章 单元测试

    1. 编写测试代码,用于测试
    2. tdd测试驱动开发,单元测试,编写生产代码之前要编写测试代码
    3. 编写整洁的测试代码,按照生产代码规范来编写
    4. 维护代码也是一个大工程,如果在编写测试代码是没有规范化。
    5. 测试可读性,要求简洁明了
    6. 用于测试而非生产环境
    7. 测试代码可以对代码的限制宽松一点,测试都需要断言断言断言
    8. 功能代码分解测试,越小越好
    9. 测试规则
      1. 快速
      2. 独立
      3. 可重复
      4. 自足验证
      5. 及时
    10. 提高测试代码的重视程度 非常重要

    第10章 类

    1. 类的封装,包括变量和函数的私有性
    2. 创建短小的类
    3. 只有一条修改的理由,只做一件事
    4. 类的内部实现内聚,变量被每个类中的方法所使用(尽量)
    5. 增加抽象类以满足多变量
    6. 结构要清晰,关系要理顺
    7. 调整代码之后还是需要做进一步的验证、测试
    展开全文
  • 代码整洁之道总结(一)

    千次阅读 2016-10-10 18:30:37
    代码逻辑直接了当,尽量减少依赖关系,使便于维护;依据某种分层战略完善错误处理代码,把性能调至最优,整洁代码只做好一件事。 2、简单代码重要性排序 a、能通过所有测试。 b、没有重复代码。 c、体现...
  • 过程式代码(使用数据结构的代码)便于在不改动既有数据结构的前提下添加新函数,面向对象便于在于不改动既有函数的前提下添加新类。所以,面向对象较难的事,对于过程式代码却较容易,反之亦然。 2、得墨忒耳律 ...
  • 代码整洁之道总结 1.switch语句 写出短小的switch很难,即便是只有两种条件的switch语句也要比单个代码块和函数大得多,写出制作一件事的switch也很难。当switch条件增多时,代码还会变长。它明显做了不止一件事,...
  • 代码整洁之道 最近,读了《代码整洁之道》这本书,这使我收获了很多,现总结如下:Ø 写代码的主要注意点:要分段(像写作文一样);要写有用的注释(为以后维护提供帮助的注释);方法类的命名要完整(不要写英文...
  • 代码整洁之道培训总结 1. 本次培训的总结 1.1培训目的 人类行为都是以目的为导向,培训也是。 这次培训目的很明确,就是让我们的代码更加简洁。为了实现这个目的,我们需要搞明白三个问题: 什么是整洁的代码...
  • 代码坏味道与启发--《代码整洁之道总结.pdf
  • 代码整洁之道》这本书的作者是外国某公司总裁,这本书我觉得主要分成两个部分,前半部分是适合新手,说的是在写代码过程中如何给变量或者函数取名以及代码和注释等等方面;后半部分是适合老手,说的是如何重构经过...
  • 一、整洁代码 混乱的代价:团队生产力下降,进度延缓,新人熟悉慢 华丽的新设计:新的设计需要赶超旧的设计,以至于完成时自身成为了旧的设计 简单规则: 1.能通过所有测试 2.没有重复代码 3.体现系统的全部设计...
  • 出处:https://www.cnblogs.com/xiaoyangjia/很早就阅读过《代码整洁之道》(英文版Clean Code),当时博主是个青涩的菜鸟,正在为团...
  • 函数要短小 注释要少 todo注释的使用:指要去做的事,包括未来要删除或者恢复某块代码

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,494
精华内容 2,197
关键字:

代码整洁之道总结