精华内容
下载资源
问答
  • 编写高质量代码之java

    2019-03-01 11:27:15
    编写高质量代码之java
  • 编写高质量代码之Java

    2018-06-07 16:42:14
    编写高质量代码之Java 编写高质量代码之Java 编写高质量代码之Java
  • 在通往“Python技术殿堂”的路上,本书将为你编写健壮、优雅、高质量的Python代码指点迷津!内容全部由Python编码的最佳实践组成,从基本原则、惯用法、语法、库、设计模式、内部机制、开发工具和性能优化8个方面...
  • 编写高质量代码改善C#程序的157个建议(带目录含源代码-完整清晰)
  • 《编写高质量代码:改善javascript程序的188个建议》是web前端工程师进阶修炼的必读之作,将为你通往“javascript技术殿堂”指点迷津!内容全部由编写高质量的javascript代码的最佳实践组成,从基本语法、应用架构、...
  • 编写高质量代码++改善C++程序的150个建议_(高清).pdf
  • 编写高质量代码 - Java

    千次阅读 2018-05-11 14:49:30
    编写高质量代码:改善Java程序的151个建议 持续更新、Java、笔记 1.使用 long 类型时注意使用 大写L // 错误的 long i = 1l; // 正确的 long i = 1L; 2.三元表达式类型务必一致   三元操作符是if...

    参考:编写高质量代码:改善Java程序的151个建议

    持续更新Java笔记


    1.使用 long 类型时注意使用 大写L

    // 错误的
    long i = 1l;
    // 正确的
    long i = 1L;

    2.三元表达式类型务必一致

      三元操作符是if-else的简化写法,在项目中使用它的地方很多,也非常好用,但是好用 又简单的东西并不表示就可以随便用,我们来看看下面这段代码:

    public static void main(String[] args) { 
            int i = 80;
            String s = String.valueOf(i<100 ? 90 : 100);
            String si = String.valueOf(i<100 ? 90 : 100.0);
            System.out.println("两者是否相等:"+s.equals(si));
    }

      分析一下这段程序:i是80,那它当然小于100,两者的返回值肯定都是90,再转成 String类型,其值也绝对相等,毋庸置疑的。恩,分析得有点道理,但是变量s中三元操作符的第二个操作数是100,而si的第二个操作数是100.0,难道没有影响吗?不可能有影响吧,三元操作符的条件都为真了,只返回第一个值嘛,与第二个值有一毛钱的关系吗?貌似有道理。

      果真如此吗?我们通过结果来验证一下,运行结果是:“两者是否相等:false”,什么? 不相等,Why?

      问题就出在了 100和100.0这两个数字上,在变量s中,三元操作符中的第一个操作数 (90)和第二个操作数(100)都是int类型,类型相同,返回的结果也就是int类型的90,而变量si的情况就有点不同了,第一个操作数是90 (int类型),第二个操作数却是100.0,而这是个浮点数,也就是说两个操作数的类型不一致,可三元操作符必须要返回一个数据,而且类型要确定,不可能条件为真时返回int类型,条件为假时返回float类型,编译器是不允许如此的,所以它就会进行类型转换了,int型转换为浮点数90.0,也就是说三元操作符的返回值是浮点数90.0,那这当然与整型的90不相等了。这里可能会有疑惑了:为什么是整型转为浮点,而不是浮点转为整型呢?这就涉及三元操作符类型的转换规则:
      若两个操作数不可转换,则不做转换,返回值为Object类型。
      若两个操作数是明确类型的表达式(比如变量),則按照正常的二进制数字来转换,int类型转换为long类型,long类型转换为float类型等。
      若两个操作数中有一个是数字S,另外一个是表达式,且其类型标示为T,那么,若数字S在T的范围内,则转换为T类型;若S超出了T类型的范围,则T转换为S类型。
      若两个操作数都是直接量数字(Literal、字面量),则返回值类型为范围较大者。

      知道是什么原因了,相应的解决办法也就有了 :保证三元操作符中的两个操作数类型一致,即可减少可能错误的发生。


    3.避免 instanceof 非预期结果

      instanceof是一个简单的二元操作符,它是用来判断一个对象是否是一个类实例的,其 操作类似于 >=、==,非常简单,我们来看段程序,代码如下:

    public static void main(String[] args) {
            // String对象是否是Object的实例 
            boolean b1 = "Sting" instanceof Object;
            // String对象是否是String的实例
            boolean b2 = new String() instanceof String;
            // Object对象是否是String的实例
            boolean b3 = new Object() instanceof String;
            // 拆箱类型是否是装箱矣型的实例
            boolean b4 = 'A' instanceof Character;
                // 空对象是否是String的实例
            boolean b5 = null instanceof String;
            // 类负转換后的空对象是否是String的实例 
            boolean b6 = (String) null instanceof String;
            //Date对象是否是String的实例
            boolean b7 = new Date() instanceof String;
            boolean b8 = new GenericClass<String>().isDateInstance("");
    }
    
    class GenericClass<T> {
            // 判断是否是Date类型
            public boolean isDateInstance(T t) {
                    return t instanceof  Date;
            }
    }

      就这么一段程序,instanceof的所有应用场景都出现了,同时问题也产生了:这段程序中哪些语句会编译通不过?我们一个一个地来解说。

      “Sting” instanceof Object
      返回值是true,这很正常,”String”是一个字符串,字符串又继承了 Object,那当然是返回true了。

      new String() instanceof String
      返回值是true,没有任何问题,一个类的对象当然是它的实例了。

      new Object() instanceof String
      返回值是false,Object是父类,其对象当然不是String类的实例了。要注意的是,这句话其实完全可以编译通过,只要instanceof关键字的左右两个操作数有继承或实现关系,就可以编译通过。

      ‘A’ instanceof Character
      这句话可能有读者会猜错,事实上它编译不通过,为什么呢?因为是一个char类型,也就是一个基本类型,不是一个对象,instanceof只能用于对象的判断,不能用于基本类型的判断。

      null instanceof String
      返回值是false,这是instanceof特有的规则:若左操作数是null,结果就直接返回 false,不再运算右操作数是什么类。这对我们的程序非常有利,在使用instanceof操作符时, 不用关心被判断的类(也就是左操作数)是否为null,这与我们经常用到的equals、toString方法不同。

      (String)null instanceof String
      返回值是false,不要看这里有个强制类型转换就认为结果是true,不是的,null是一个万用类型,也可以说它没类型,即使做类型转换还是个null。

      new Date() instanceof String
      编译通不过,因为Date类和String没有继承或实现关系,所以在编译时直接就报错了,instanceof操作符的左右操作数必须有继承或实现关系,否则编译会失败。

      new GenericClass().isDateInstance(“”)
      编译通不过?非也,编译通过了,返回值是false, T是个String类型,与Date之间没有继承或实现关系,为什么”t instanceof Date”会编译通过呢?那是因为Java的泛型是为编码服务的,在编译成字节码时,T已经是Object类型了,传递的实参是String类型,也就是说T的表面类型是Object,实际类型是String,那t instanceof Date这句话就等价于 Object instance of Date了,所以返回 false 就很正常了。


    4.奇偶判断,用偶判断,不要用奇判断

      判断一个数是奇数还是偶数是小学里学的基本知识,能够被2整除的整数是偶数,不能被2整除的是奇数,这规则简单又明了,还有什么好考虑的?好,我们来看一个例f,代码 如下:

    public static void main(String[] args) {
            //接收键盘输入参数
            Scanner input = new Scanner(System.in);
            System.out.print("请输入多个数字判断奇偶:");
            while(input.hasNextlnt()) {
                    int i = input.nextInt();
                    String str  = i + "->" + (i % 2 == 1 ? "奇数" : "偶数");
                    System.out.println(str);
            }
    }

      输入多个数字,然后判断每个数字的奇偶性,不能被2整除就是奇数,其他的都是偶数,完全是根据奇偶数的定义编写的程序,我们来看看打印的结果:

    请输入多个数字利断奇偶:1 2 0 -1 -2

    1 -> 奇数
    2 -> 偶数
    0 -> 偶数
    -1 -> 偶数
    -2 -> 偶数

      前三个还很靠谱,第四个参数怎么可能会是偶数呢,这Java也太差劲了,如此简单的计算也会错!別忙着下结论,我们先来了解一下Java中的取余(%标示符)算法,模拟代码如下:

    // 橫拟取余计算,dividend被除数,divisor除数
    public static int remainder(int dividend, int divisor) {
            return dividend - dividend / divisor * divisor;
    }

      看到这段程序,相信大家都会心地笑了,原来Java是这么处理取余计算的呀。根据上面的模拟取余可知,当输入-1的时候,运算结果是-1,当然不等于1 了,所以它就被判定为偶数了,也就是说是我们的判断失误了。问题明白了,修正也很简单,改为判断是否是偶数即可,代码如下:

    i % 2 == 0 ? "偶数" : "奇数";

    5.不要让类型默默转换

    看以下代码

    public static final int LIGHT_SPEED = 30 * 10000 * 1000;
    
    public static void main(String[] args) {
            long dis = LIGHT_SPEED * 60 * 8;
            System.out.println(dis);
    }

    结果:

    -2028888064

      这是应为Java是先运算然后再进行类型转换的,具体地说就是因为disc的三个运算参数都是int类型,三者相乘的结果虽然也是int类型,但是已经超过了int的最大值,所以其值就是负值了(为什么是负值?因为过界了就会从头开始),再转换成long型,结果还是负值。

    • 问题知道了,解决问题也很简单,只需一个小小的L
    long dis = LIGHT_SPEED * 60L * 8;

    在实际开发中,更通用的做法,是主动声明类型转化

    long dis = 1L * LIGHT_SPEED * 60 * 8;

    意思是:嗨,我已经是长整型了,你们都跟着我一起转为长整型吧


    6.边界,边界,还是边界

      某商家生产的电子产品非常畅销,需要提前30天预订才能抢到手,同时它还规定了一 个会员可拥有的最多产品数量,目的是防止囤积压货肆意加价。会员的预定过程是这样的: 先登录官方网站,选择产品型号,然后设置需要预订的数量,提交,符合规则即提示下单成功,不符合规则提示下单失败。后台的处理逻辑模拟如下:

    // 一个会员拥有产品的最多数量
    public final static int LIMIT = 2000;
    public static void main(String[] args) {
    
            // 会员当前拥有的产品数量 
            int cur = 1000;
            Scanner input = new Scanner(System.in);
            System.out.print("请输入需要预定的数量:");
            while (input.hasNextInt()) {
                    int order = input.nextInt();
                    // 当前拥有的与准备订购的产品数量之和 
                    if (order > 0 && order + cur <= LIMIT) {
                            System.out .println ("你已经成功定的" + order + "个产品!");
                    } else {
                            System.out.print("超出限额,预定失败!");
                    }
            }
    }

      这是一个简易的订单处理程序,其中cur代表的是会员已经拥有的产品数LIMIT是一个会员最多拥有的产品数置(现实中这两个参数当然是从数据库中获得的,不过这里是一 个模拟程序),如果当前预订数置与拥有数之和超过了最大数量,则预订失败,否则下单成功。业务逻辑很简单,同时在Web界面上对订单数量做了严格的校验,比如不能是负值、 不能超过最大数量等,但是人算不如天算,运行不到两小时数据库中就出现了异常数据:某会员拥有产品的数址与预订数量之和远远大于限额。怎么会这样?程序逻辑上不可能有问题呀,这是如何产生的呢?我们来模拟一下,第一次输入:

      请褕入需要预定的致量:800
      你己经成功预定的800个产品!

      这完全满足条件,没有任何问题,继续输入:

      请输入需要预定的数量:2147483647
      你已径成功預定的2147483647个产品!

      看到没,这个数字远远超过了 2000的限额,但是竞然预订成功了,真是神奇!

      看着2147483647这个数字很眼熟?那就对了,它是int类型的最大值,没错,有人输入 了一个最大值,使校验条件失效了,Why?我们来看程序,order的值是2147483647,那再加上1000就超出int的范围了,其结果是-21W482649,那当然是小于正数2000了!一句话可归结其原因:数字越界使检验条件失效

      在单元测试中,有一项测试叫做边界测试(也有叫做临界测试),如果一个方法接收的是int类型的参数,那以下三个值是必测的:0、正最大、负最小,其中正最大和负最小是边界值,如果这三个值都没有问题,方法才是比较安全可靠的。我们的例子就是因为缺少边界测试,致使生产系统产生了严重的偏差。

      也许你要疑惑了,Web界面既然已经做了严格的校验,为什么还能输入2147483647这么大的数字呢?是否说明Web校验不严格?错了,不是这样的,Web校验都是在页面上通过JavaScript实现的,只能限制普通用户(这里的普通用户是指不懂HTML、不懂HTTP、不懂Java的简单使用者),而对干高手,这些校验基本上就是摆设,HTTP是明文传输的,将其拦截几次,分析一下数据结构,然后再写一个模拟器,一切前端校验就都成了浮云!想往后台提交个什么数据那还不是信手拈来?


    7.不要让四舍五入亏了一方

      本建议还是来重温一个小学数学问题:四舍五入。四舍五入是一种近似精确的计算方法,在Java 5之前,我们一般是通过使用Math.round来获得指定精度的整数或小数的,这种方法使用非常广泛,代码如下:

    public static void main(String[] args) {  
            System.out.println("10.5近似值:" + Math.round(10.5));  
            System.out.println("-10.5近似值:"+ Math.round(-10.5));  
    } 

      输出结果为:

    10.5近似值:11
    -10.5近似值:-10

      这是四舍五入的经典案例,也是初级面试官很乐意选择的考题,绝对值相同的两个数字,近似值为什么就不同了呢?这是由Math.round采用的舍入规则所决定的(采用的是正无穷方向舍入规则,后面会讲解)。我们知道四舍五入是有误差的:其误差值是舍入位的一半。我们以舍入运用最频繁的银行利息计算为例来阐述该问题。

      我们知道银行的盈利渠道主要是利息差,从储户手里收拢资金,然后放贷出去,其间的利息差额便是所获得的利润。对一个银行来说,对付给储户的利息的计算非常频繁,人民银行规定每个季度末月的20日为银行结息日,一年有4次的结息日。

      场景介绍完毕,我们回过头来看四舍五入,小于5的数字被舍去,大于等于5的数字进位后舍去,由于所有位上的数字都是自然计算出来的,按照概率计算可知,被舍入的数字均匀分布在0到9之间,下面以10笔存款利息计算作为模型,以银行家的身份来思考这个算法。

      四舍。舍弃的数值:0.000、0.001、0.002、0.003、0.004,因为是舍弃的,对银行家来说,就不用付款给储户了,那每舍弃一个数字就会赚取相应的金额:0.000、0.001、0.002、0.003、0.004。

      五入。进位的数值:0.005、0.006、0.007、0.008、0.009,因为是进位,对银行家来说,每进一位就会多付款给储户,也就是亏损了,那亏损部分就是其对应的10进制补数:0.005、0.004、0.003、0.002、0.001。

      因为舍弃和进位的数字是在0到9之间均匀分布的,所以对于银行家来说,每10笔存款的利息因采用四舍五入而获得的盈利是:

    0.000+0.001+0.002+0.003+0.004-0.005-0.004-0.003-0.002-0.001 = -0.005

      也就是说,每10笔的利息计算中就损失0.005元,即每笔利息计算损失0.0005元,这对一家有5千万储户的银行来说(对国内的银行来说,5千万是个很小的数字),每年仅仅因为四舍五入的误差而损失的金额是:

    public static void main(String[] args) {  
            // 银行账户数量,5千万  
            int accountNum = 5000 * 10000;  
            //按照人行的规定,每个季度末月的20日为银行结息日  
            double cost = 0.0005 * accountNum * 4 ;
            System.out.println("银行每年损失的金额:" + cost);  
    }

      输出的结果是:“银行每年损失的金额:100000.0”。即,每年因为一个算法误差就损失了10万元,事实上以上的假设条件都是非常保守的,实际情况可能损失得更多。那各位可能要说了,银行还要放贷呀,放出去这笔计算误差不就抵消掉了吗?不会抵销,银行的贷款数量是非常有限的,其数量级根本没有办法和存款相比。

      这个算法误差是由美国银行家发现的(那可是私人银行,钱是自己的,白白损失了可不行),并且对此提出了一个修正算法,叫做银行家舍入(Banker’s Round)的近似算法,其规则如下:
      舍去位的数值小于5时,直接舍去;
      舍去位的数值大于等于6时,进位后舍去;
      当舍去位的数值等于5时,分两种情况:5后面还有其他数字(非0),则进位后舍去;若5后面是0(即5是最后一个数字),则根据5前一位数的奇偶性来判断是否需要进位,奇数进位,偶数舍去。

    以上规则汇总成一句话:四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。

    • 我们举例说明,取2位精度:
    round(10.5551) = 10.56
    round(10.555) = 10.56
    round(10.545) = 10.54

      要在Java 5以上的版本中使用银行家的舍入法则非常简单,直接使用RoundingMode类提供的Round模式即可,示例代码如下:

    public static void main(String[] args) {  
            // 存款  
            BigDecimal d = new BigDecimal(888888);  
            // 月利率,乘3计算季利率
            BigDecimal r = new BigDecimal(0.001875 * 3);
            //计算利息
            BigDecimal i = d.multiply(r).setScale(2, RoundingMode.HALF_EVEN); 
            System.out.println("季利息是:" + i);  
    }

      在上面的例子中,我们使用了BigDecimal类,并且采用setScale方法设置了精度,同时传递了一个RoundingMode.HALF_EVEN参数表示使用银行家舍入法则进行近似计算,BigDecimal和RoundingMode是一个绝配,想要采用什么舍入模式使用RoundingMode设置即可。目前Java支持以下七种舍入方式:
      ROUND_UP:远离零方向舍入。 向远离0的方向舍入,也就是说,向绝对值最大的方向舍入,只要舍弃位非0即进位。
      ROUND_DOWN:趋向零方向舍入。 向0方向靠拢,也就是说,向绝对值最小的方向输入,注意:所有的位都舍弃,不存在进位情况。
      ROUND_CEILING:向正无穷方向舍入。 向正最大方向靠拢,如果是正数,舍入行为类似于ROUND_UP;如果为负数,则舍入行为类似于ROUND_DOWN。注意:Math.round方法使用的即为此模式。
      ROUND_FLOOR:向负无穷方向舍入。 向负无穷方向靠拢,如果是正数,则舍入行为类似于ROUND_DOWN;如果是负数,则舍入行为类似于ROUND_UP。
      HALF_UP:最近数字舍入(5进)。这就是我们最最经典的四舍五入模式。
      HALF_DOWN:最近数字舍入(5舍)。 在四舍五入中,5是进位的,而在HALF_DOWN中却是舍弃不进位。
      HALF_EVEN:银行家算法。

      在普通的项目中舍入模式不会有太多影响,可以直接使用Math.round方法,但在大量与货币数字交互的项目中,一定要选择好近似的计算模式,尽量减少因算法不同而造成的损失。

      注意 根据不同的场景,慎重选择不同的舍入模式,以提高项目的精准度,减少算法损失。


    8.在接口中不要存在实现代码

      看到这样的标题读者可能会纳闷:接口中有实现代码?这怎么可能呢?确实,接口中可以声明常量,声明抽象方法,也可以继承父接口,但就是不能有具体实现,因为接口是一种契约(Contract),是一种框架性协议,这表明它的实现类都是同一种类型,或者是具备相似特征的一个集合体。对于一般程序,接口确实没有任何实现,但是在那些特殊的程序中就例外了,阅读如下代码:

    public static void main(String[] args) {  
            // 调用接口的实现  
            B.s.doSomething();  
    }
    
    // 在接口中存在实现代码 
    interface B { 
            public static final S s = new S() {  
                    public void doSomething() {  
                            System.out.println("我在接口中实现了");  
                    }  
            }; 
    } 
    
    // 被实现的接口 
    interface S {  
            public void doSomething(); 
    } 

      仔细看main方法,注意那个B接口。它调用了接口常量,在没有任何显式实现类的情况下,它竟然打印出了结果,那B接口中的s常量(接口是S)是在什么地方被实现的呢?答案是在B接口中。

      在B接口中声明了一个静态常量s,其值是一个匿名内部类(Anonymous Inner Class)的实例对象,就是该匿名内部类(当然,可以不用匿名,直接在接口中实现内部类也是允许的)实现了S接口。你看,在接口中存在着实现代码吧!

      这确实很好,很强大,但是在一般的项目中,此类代码是严禁出现的,原因很简单:这是一种不好的编码习惯,接口是用来干什么的?接口是一个契约,不仅仅约束着实现者,同时也是一个保证,保证提供的服务(常量、方法)是稳定、可靠的,如果把实现代码写到接口中,那接口就绑定了可能变化的因素,这就会导致实现不再稳定和可靠,是随时都可能被抛弃、被更改、被重构的。所以,接口中虽然可以有实现,但应避免使用。

      注意 接口中不能存在实现代码


    9.避免在构造函数中初始化其他类

      构造函数是一个类初始化必须执行的代码,它决定着类的初始化效率,如果构造函数比较复杂,而且还关联了其他类,则可能产生意想不到的问题,我们来看如下代码:

    public class Client {  
            public static void main(String[] args) {  
                    Son s = new Son();  s.doSomething();  
            } 
    } 
    // 父类 
    class Father {  
            Father() {  
                    new Other();  
            } 
    }
    // 子类 
    class Son extends Father {  
            public void doSomething() {  
                    System.out.println("Hi,show me something");  
            } 
    } 
    // 相关类
    class Other {  
            public Other() {  
                    new Son();  
            } 
    } 

      这段代码并不复杂,只是在构造函数中初始化了其他类,想想看这段代码的运行结果是什么?是打印”Hi, show me something“吗?

      答案是这段代码不能运行,报StackOverflowError异常,栈(Stack)内存溢出。这是因为声明s变量时,调用了Son的无参构造函数,JVM又默认调用了父类Father的无参构造函数,接着Father类又初始化了Other类,而Other类又调用了Son类,于是一个死循环就诞生了,直到栈内存被消耗完毕为止。

      可能有读者会觉得这样的场景不可能在开发中出现,那我们来思考这样的场景:Father是由框架提供的,Son类是我们自己编写的扩展代码,而Other类则是框架要求的拦截类(Interceptor类或者Handle类或者Hook方法),再来看看该问题,这种场景不可能出现吗?

      那有人可能要说了,这种问题只要系统一运行就会发现,不可能对项目产生影响。

      那是因为我们在这里展示的代码比较简单,很容易一眼洞穿,一个项目中的构造函数可不止一两个,类之间的关系也不会这么简单的,要想瞥一眼就能明白是否有缺陷这对所有人员来说都是不可能完成的任务,解决此类问题的最好办法就是:不要在构造函数中声明初始化其他类,养成良好的习惯。


    10.让工具类不可实例化

      Java项目中使用的工具类非常多,比如JDK自己的工具类java.lang.Math、java.util.Collections等都是我们经常用到的。工具类的方法和属性都是静态的,不需要生成实例即可访问,而且JDK也做了很好的处理,由于不希望被初始化,于是就设置构造函数为private访问权限,表示除了类本身外,谁都不能产生一个实例,我们来看一下java.lang.Math代码:

    public final class Math {  
            /**  
             * Don't let anyone instantiate this class.  
             */  
            private Math() {} 
    } 

      之所以要将”Don't let anyone instantiate this class.“留下来,是因为Math的构造函数设置为private了:我就是一个工具类,我只想要其他类通过类名来访问,我不想你通过实例对象访问。这在平台型或框架型项目中已经足够了。但是如果已经告诉你不能这么做了,你还要生成一个Math实例来访问静态方法和属性(Java的反射是如此的发达,修改个构造函数的访问权限易如反掌),那我就不保证正确性了,隐藏问题随时都有可能爆发!那我们在项目开发中有没有更好的限制办法呢?有,即不仅仅设置成private访问权限,还抛异常,代码如下:

    public class UtilsClass {  
            private UtilsClass() {  
                    throw new Error("不要实例化我!");  
            }
    } 

      如此做才能保证一个工具类不会实例化,并且保证所有的访问都是通过类名来进行的。需要注意一点的是,此工具类最好不要做继承的打算,因为如果子类可以实例化的话,那就要调用父类的构造函数,可是父类没有可以被访问的构造函数,于是问题就会出现。

      注意 如果一个类不允许实例化,就要保证“平常”渠道都不能实例化它。


    11.

    展开全文
  • 高质量代码的三大要素: 可读性、可维护性和可变更性!! 做好代码规范、提高代码质量,能显著增强代码的可读性、可维护性和可变更性。努力提高代码的读写可维护性,是做好代码规范的必要非充分条件。代码规范和...

    高质量代码的三大要素:

    可读性、可维护性和可变更性!!

    做好代码规范、提高代码质量,能显著增强代码的可读性、可维护性和可变更性。努力提高代码的读写可维护性,是做好代码规范的必要非充分条件。代码规范和架构设计是软件的灵魂所在,代码质量偏低,就像是人失去了三魂七魄中的一魄,就会丧失活力,影响正常运行,增加软件交付后维护成本,出现推迟完成、超出预算、特性缺失等现象。

    任何语言都需要强调编码风格的一致性。只要是团队开发,每个人都以相同的方式编写代码就是至关重要的。这样大家才能方便地互相看懂和维护对方的代码。

    实际上,每家公司都会有一份(或多份)代码规范,因此提高代码的读写可维护性的关键在于是否能落实公司的相关文档,公司的技术总监、项目经理或相关代码审查机关是否具有应有的执行力。如果不能落实,那么即便代码规范画得再美,具体的代码也会丑到崩溃。

    代码规范

    如果不想为以后挖坑,做好代码规范是程序员和团队负责人、项目经理的必修课。如何保证当前项目开发过程中压力正常,而不是在后期面对过多的压力、以至于噩梦缠身?最简单的办法就是照看好你的代码,也就是落实好公司的代码规范工作。每天为此付出一丁点的努力,便可以避免代码腐烂,并保证代码产品日后不至于变得难以理解(可读性)和维护(可维护性)。

    1. 代码的可读性

    代码的可读性是指代码让人容易阅读、跟踪和理解的程度。提高代码的可读性可以为代码阅读者节约时间(避免阅读时浪费过多无谓的时间)和精力(Debug、扩展功能或是性能优化的前提条件是你要读懂这段代码)。以下是摘选的可供参考的策略:

    • 编码风格一致
    • 代码清晰表达意图
      • 写别人看得懂的单词,如果选用英语,那么避免日语、法语和汉语拼音等,尽量使用语义化的命名组合;
      • PIE 原则:意图清楚而且表达明确地编程;
      • 能够让人快速看懂(最低限度的要求是自己一个月后能快速读懂);
    • 恰到好处的注释
      • 不能太多或太少,注释的形式根据代码具体的情况有不同;
      • 避免用注释包裹代码;
      • 尽量留下简明扼要的注释;
    • 评估取舍(不要编写大段的代码)
      • 避免写一些现在不需要、将来也不太可能需要的功能:
        • 不完美主义:不多写代码(如会话存储拆分);
      • 避免做没有太大价值的优化工作;
      • 区分任务的轻重缓急:
        • 头疼医头也医脚:先容忍失败,再解决问题(如节点关闭逻辑);
        • 不头疼不医头:量化分析(如参数调整回滚等);
      • 综合考虑一下性能、便利性、生产力、成本和上市时间……
    • 简单就是美,避免简单的功能写出复杂的代码;
      • 保持简单的代码远比写出复杂代码要难得多,但这是值得的;
      • 不编写讨巧的代码;
      • 避免无谓的条件嵌套和过度封装;
      • 第一眼看上去就能知道其用处的代码,才是简单而美的代码
      • 坚持操作方法的原子性,而后使用组合模式实现业务逻辑;
      • 避免大段代码,要写高内聚、低耦合的代码;
         

    2. 代码的可维护性

    软件可维护性是指理解、改正、改动、改进软件的难易程度。通常影响软件可维护性的因素有可理解性、可测试性和可修改性。笔者这里将其分为两大类:编写时可维护性和运行时可维护性。

    • 编写时可维护性
      • 编写时可维护性是指在程序或系统上线后爆出 BUG,开发团队能够及时扑灭这个 BUG 且不会爆出其他 BUG。保持方法的原子性,提高代码内聚,能使某处修改的影响降到最低,这样某处方法出现 BUG,也不太会影响到其他模块的正常运作。编写时可维护性还包括了代码的可测试性。
    • 运行时可维护性
      • 运行时的可维护性是指在系统运行过程中(或无需再次编码发布、只需系统重启一次)修改系统的某项配置并使其生效,且不影响现在正在进行的业务和用户的操作。这要求软件工程师不能把代码写死。例如配置文件、数据库连接字符串、资源文件、日志等。

    3. 代码的可写性

    代码的可写性包括代码的可变更性,代码的可变更性是软件理论的核心。

    代码的可写性是建立在代码的可维护性上的,而代码的可写性与可维护性又都建立在代码的可读性上。如果代码难以阅读,那么 BUG 的修正将变得难以入手,新功能的添加就更是无从入手了。

     

    现在,如果有人问你“如何提高代码质量”,你知道如何回答了吗?

    • 不要把代码写死;
    • 预测可能发生的变化;
    • 通过提高代码的复用性提高代码的可维护性(解释见上文);

     

    更多精彩,请关注我的"今日头条号":Java云笔记
    随时随地,让你拥有最新,最便捷的掌上云服务

     

    展开全文
  • 第1章 javascript语言基础 / 1 第2章 字符串、正则表达式和数组 / 53 第3章 函数式编程 / 91 第4章 面向对象编程 / 133 第5章 dom编程 / 195 第6章 客户端编程 / 226 第7章 数据交互和存储 / 254 ...
  • 什么才是好代码、高质量代码

    千次阅读 2019-03-06 16:14:24
    虽然刚毕业的同学对于代码质量都很用心,但最终呈现出来的质量仍然没能达到“十分优秀”的程度。 究其原因,主要是不了解好的代码“应该”是什么样的。 什么是好代码代码的第一步是理解什么是好代码。在准备boot...

    阿里云幸运券

    虽然刚毕业的同学对于代码质量都很用心,但最终呈现出来的质量仍然没能达到“十分优秀”的程度。 究其原因,主要是不了解好的代码“应该”是什么样的。
    什么是好代码
    写代码的第一步是理解什么是好代码。在准备bootcamp的课程的时候,我就为这个问题犯了难,我尝试着用一些精确的定义区分出“优等品”、“良品”、“不良品”;但是在总结的过程中,关于“什么是好代码”的描述却大多没有可操作性

    1.好代码的定义
    随便从网上搜索了一下“优雅的代码”,找到了下面这样的定义:

    Bjarne Stroustrup,C++之父:

    逻辑应该是清晰的,bug难以隐藏;

    依赖最少,易于维护;

    错误处理完全根据一个明确的策略;

    性能接近最佳化,避免代码混乱和无原则的优化;

    整洁的代码只做一件事。

    Grady Booch,《面向对象分析与设计》作者:

    整洁的代码是简单、直接的;

    整洁的代码,读起来像是一篇写得很好的散文;

    整洁的代码永远不会掩盖设计者的意图,而是具有少量的抽象和清晰的控制行。

    Michael Feathers,《修改代码的艺术》作者:

    整洁的代码看起来总是像很在乎代码质量的人写的;

    没有明显的需要改善的地方;

    代码的作者似乎考虑到了所有的事情。

    看起来似乎说的都很有道理,可是实际评判的时候却难以参考,尤其是对于新人来说,如何理解“简单的、直接的代码”或者“没有明显的需要改善的地方”?

    而实践过程中,很多同学也确实面对这种问题:对自己的代码总是处在一种心里不踏实的状态,或者是自己觉得很好了,但是却被其他人认为很烂,甚至有几次我和新同学因为代码质量的标准一连讨论好几天,却谁也说服不了谁:我们都坚持自己对于好代码的标准才是正确的。

    在经历了无数次code review之后,我觉得这张图似乎总结的更好一些:

    代码质量的评价标准某种意义上有点类似于文学作品,比如对小说的质量的评价主要来自于它的读者,由个体主观评价形成一个相对客观的评价。并不是依靠字数,或者作者使用了哪些修辞手法之类的看似完全客观但实际没有什么意义的评价手段。

    但代码和小说还有些不一样,它实际存在两个读者:计算机和程序员。就像上篇文章里说的,即使所有程序员都看不懂这段代码,它也是可以被计算机理解并运行的。

    所以对于代码质量的定义我需要于从两个维度分析:主观的,被人类理解的部分;还有客观的,在计算机里运行的状况。

    既然存在主观部分,那么就会存在个体差异,对于同一段代码评价会因为看代码的人的水平不同而得出不一样的结论,这也是大多数新人面对的问题:他们没有一个可以执行的评价标准,所以写出来的代码质量也很难提高。

    有些介绍代码质量的文章讲述的都是倾向或者原则,虽然说的很对,但是实际指导作用不大。所以在这篇文章里我希望尽可能把评价代码的标准用(我自认为)与实际水平无关的评价方式表示出来。

    2.可读的代码
    在权衡很久之后,我决定把可读性的优先级排在前面:一个程序员更希望接手一个有bug但是看的懂的工程,还是一个没bug但是看不懂的工程?如果是后者,可以直接关掉这个网页,去做些对你来说更有意义的事情。

    2.1.逐字翻译
    在很多跟代码质量有关的书里都强调了一个观点:程序首先是给人看的,其次才是能被机器执行,我也比较认同这个观点。在评价一段代码能不能让人看懂的时候,我习惯让作者把这段代码逐字翻译成中文,试着组成句子,之后把中文句子读给另一个人没有看过这段代码的人听,如果另一个人能听懂,那么这段代码的可读性基本就合格了。

    用这种判断方式的原因很简单:其他人在理解一段代码的时候就是这么做的。阅读代码的人会一个词一个词的阅读,推断这句话的意思,如果仅靠句子无法理解,那么就需要联系上下文理解这句代码,如果简单的联系上下文也理解不了,可能还要掌握更多其它部分的细节来帮助推断。大部分情况下,理解一句代码在做什么需要联系的上下文越多,意味着代码的质量越差。

    逐字翻译的好处是能让作者能轻易的发现那些只有自己知道的、没有体现在代码里的假设和可读性陷阱。无法从字面意义上翻译出原本意思的代码大多都是烂代码,比如“ms代表messageService“,或者“ms.proc()是发消息“,或者“tmp代表当前的文件”。

    2.2.遵循约定
    约定包括代码和文档如何组织,注释如何编写,编码风格的约定等等,这对于代码未来的维护很重要。对于遵循何种约定没有一个强制的标准,不过我更倾向于遵守更多人的约定。

    与开源项目保持风格一致一般来说比较靠谱,其次也可以遵守公司内部的编码风格。但是如果公司内部的编码风格和当前开源项目的风格冲突比较严重,往往代表着这个公司的技术倾向于封闭,或者已经有些跟不上节奏了。

    但是无论如何,遵守一个约定总比自己创造出一些规则要好很多,这降低了理解、沟通和维护的成本。如果一个项目自己创造出了一些奇怪的规则,可能意味着作者看过的代码不够多。

    一个工程是否遵循了约定往往需要代码阅读者有一定经验,或者需要借助checkstyle这样的静态检查工具。如果感觉无处下手,那么大部分情况下跟着google做应该不会有什么大问题:可以参考google code style,其中一部分有对应的中文版。

    另外,没有必要纠结于遵循了约定到底有什么收益,就好像走路是靠左好还是靠右好一样,即使得出了结论也没有什么意义,大部分约定只要遵守就可以了。

    2.3.文档和注释
    文档和注释是程序很重要的部分,他们是理解一个工程或项目的途径之一。两者在某些场景下定位会有些重合或者交叉(比如javadoc实际可以算是文档)。

    对于文档的标准很简单,能找到、能读懂就可以了,一般来说我比较关心这几类文档:

    对于项目的介绍,包括项目功能、作者、目录结构等,读者应该能3分钟内大致理解这个工程是做什么的。

    针对新人的QuickStart,读者按照文档说明应该能在1小时内完成代码构建和简单使用。

    针对使用者的详细说明文档,比如接口定义、参数含义、设计等,读者能通过文档了解这些功能(或接口)的使用方法。

    有一部分注释实际是文档,比如之前提到的javadoc。这样能把源码和注释放在一起,对于读者更清晰,也能简化不少文档的维护的工作。

    还有一类注释并不作为文档的一部分,比如函数内部的注释,这类注释的职责是说明一些代码本身无法表达的作者在编码时的思考,比如“为什么这里没有做XXX”,或者“这里要注意XXX问题”。

    一般来说我首先会关心注释的数量:函数内部注释的数量应该不会有很多,也不会完全没有,个人的经验值是滚动几屏幕看到一两处左右比较正常。过多的话可能意味着代码本身的可读性有问题,而如果一点都没有可能意味着有些隐藏的逻辑没有说明,需要考虑适当的增加一点注释了。

    其次也需要考虑注释的质量:在代码可读性合格的基础上,注释应该提供比代码更多的信息。文档和注释并不是越多越好,它们可能会导致维护成本增加。关于这部分的讨论可以参考简洁部分的内容。

    2.4.推荐阅读
    《代码整洁之道》

    3.可发布的代码
    新人的代码有一个比较典型的特征,由于缺少维护项目的经验,写的代码总会有很多考虑不到的地方。比如说测试的时候似乎没什么异常,项目发布之后才发现有很多意料之外的状况;而出了问题之后不知道从哪下手排查,或者仅能让系统处于一个并不稳定的状态,依靠一些巧合勉强运行。

    3.1.处理异常
    新手程序员普遍没有处理异常的意识,但代码的实际运行环境中充满了异常:服务器会死机,网络会超时,用户会胡乱操作,不怀好意的人会恶意攻击你的系统。

    我对一段代码异常处理能力的第一印象来自于单元测试的覆盖率。大部分异常难以在开发或者测试环境里复现,即使有专业的测试团队也很难在集成测试环境中模拟所有的异常情况。

    而单元测试可以比较简单的模拟各种异常情况,如果一个模块的单元测试覆盖率连50%都不到,很难想象这些代码考虑了异常情况下的处理,即使考虑了,这些异常处理的分支都没有被验证过,怎么指望实际运行环境中出现问题时表现良好呢?

    3.2.处理并发
    我收到的很多简历里都写着:精通并发编程/熟悉多线程机制,诸如此类,跟他们聊的时候也说的头头是道,什么锁啊互斥啊线程池啊同步啊信号量啊一堆一堆的名词滔滔不绝。而给应聘者一个实际场景,让应聘者写一段很简单的并发编程的小程序,能写好的却不多。

    实际上并发编程也确实很难,如果说写好同步代码的难度为5,那么并发编程的难度可以达到100。这并不是危言耸听,很多看似稳定的程序,在面对并发场景的时候仍然可能出现问题:比如最近我们就碰到了一个linux kernel在调用某个系统函数时由于同步问题而出现crash的情况。

    而是否高质量的实现并发编程的关键并不是是否应用了某种同步策略,而是看代码中是否保护了共享资源:

    局部变量之外的内存访问都有并发风险(比如访问对象的属性,访问静态变量等)

    访问共享资源也会有并发风险(比如缓存、数据库等)。

    被调用方如果不是声明为线程安全的,那么很有可能存在并发问题(比如java的hashmap)。

    所有依赖时序的操作,即使每一步操作都是线程安全的,还是存在并发问题(比如先删除一条记录,然后把记录数减一)。

    前三种情况能够比较简单的通过代码本身分辨出来,只要简单培养一下自己对于共享资源调用的敏感度就可以了。

    但是对于最后一种情况,往往很难简单的通过看代码的方式看出来,甚至出现并发问题的两处调用并不是在同一个程序里(比如两个系统同时读写一个数据库,或者并发的调用了一个程序的不同模块等)。但是,只要是代码里出现了不加锁的,访问共享资源的“先做A,再做B”之类的逻辑,可能就需要提高警惕了。

    3.3.优化性能
    性能是评价程序员能力的一个重要指标,很多程序员也对程序的性能津津乐道。但程序的性能很难直接通过代码看出来,往往要借助于一些性能测试工具,或者在实际环境中执行才能有结果。

    如果仅从代码的角度考虑,有两个评价执行效率的办法:

    算法的时间复杂度,时间复杂度高的程序运行效率必然会低。

    单步操作耗时,单步耗时高的操作尽量少做,比如访问数据库,访问io等。

    而实际工作中,也会见到一些程序员过于热衷优化效率,相对的会带来程序易读性的降低、复杂度提高、或者增加工期等等。对于这类情况,简单的办法是让作者说出这段程序的瓶颈在哪里,为什么会有这个瓶颈,以及优化带来的收益。

    当然,无论是优化不足还是优化过度,判断性能指标最好的办法是用数据说话,而不是单纯看代码,性能测试这部分内容有些超出这篇文章的范围,就不详细展开了。

    腾讯云代金券

    3.4.日志
    日志代表了程序在出现问题时排查的难易程度,经(jing)验(chang)丰(cai)富(keng)的程序员大概都会遇到过这个场景:排查问题时就少一句日志,查不到某个变量的值不知道是什么,导致死活分析不出来问题到底出在哪。

    对于日志的评价标准有三个:

    日志是否足够,所有异常、外部调用都需要有日志,而一条调用链路上的入口、出口和路径关键点上也需要有日志。

    日志的表达是否清晰,包括是否能读懂,风格是否统一等。这个的评价标准跟代码的可读性一样,不重复了。

    日志是否包含了足够的信息,这里包括了调用的上下文、外部的返回值,用于查询的关键字等,便于分析信息。

    对于线上系统来说,一般可以通过调整日志级别来控制日志的数量,所以打印日志的代码只要不对阅读造成障碍,基本上都是可以接受的。

    3.5.扩展阅读
    《Release It!: Design and Deploy Production-Ready Software》(不要看中文版,翻译的实在是太烂了)

    Numbers Everyone Should Know

    4.可维护的代码
    相对于前两类代码来说,可维护的代码评价标准更模糊一些,因为它要对应的是未来的情况,一般新人很难想象现在的一些做法会对未来造成什么影响。不过根据我的经验,一般来说,只要反复的提问两个问题就可以了:

    他离职了怎么办?

    他没这么做怎么办?

    4.1.避免重复
    几乎所有程序员都知道要避免拷代码,但是拷代码这个现象还是不可避免的成为了程序可维护性的杀手。

    代码重复分为两种:模块内重复和模块间重复。无论何种重复,都在一定程度上说明了程序员的水平有问题,模块内重复的问题更大一些,如果在同一个文件里都能出现大片重复的代码,那表示他什么不可思议的代码都有可能写出来。

    对于重复的判断并不需要反复阅读代码,一般来说现代的IDE都提供了检查重复代码的工具,只需点几下鼠标就可以了。

    除了代码重复之外,很多热衷于维护代码质量的程序员新人很容易出现另一类重复:信息重复。

    我见过一些新人喜欢在每行代码前面写一句注释,比如:

    成员列表的长度>
    0
    并且<
    200
    
    
    
     
    if
    (memberList.size() > 
    0
     && memberList.size() < 
    200
    ) {
    
    
    
         
    // 返回当前成员列表
    
    
    
         
    return
     memberList;
    
    
    
     }
    
    看起来似乎很好懂,但是几年之后,这段代码就变成了:
    
     
    // 成员列表的长度>0并且<200
    
    
    
     
    if
    (memberList.size() > 
    0
     && memberList.size() < 
    200
    ```| (tmp.isOpen() && flag)) {
    
    
    
         
    // 返回当前成员列表
    
    
    
         
    return
     memberList;
    
    
    
     }
    
    再之后可能会改成这样:
    
     
    // edit by axb 2015.07.30
    
    
    
     
    // 成员列表的长度>0并且<200
    
    
    
     
    //if(memberList.size() > 0 && memberList.size() < 200```| (tmp.isOpen() && flag)) {
    
    
    
     
    //     返回当前成员列表
    
    
    
     
    //    return memberList;
    
    
    
     
    //}
    
    
    
     
    if
    (tmp.isOpen() && flag) {
    
    
    
         
    return
     memberList;
    
    
    
     }
    

    随着项目的演进,无用的信息会越积越多,最终甚至让人无法分辨哪些信息是有效的,哪些是无效的。

    如果在项目中发现好几个东西都在做同一件事情,比如通过注释描述代码在做什么,或者依靠注释替代版本管理的功能,那么这些代码也不能称为好代码。

    4.2.模块划分
    模块内高内聚与模块间低耦合是大部分设计遵循的标准,通过合理的模块划分能够把复杂的功能拆分为更易于维护的更小的功能点。

    一般来说可以从代码长度上初步评价一个模块划分的是否合理,一个类的长度大于2000行,或者一个函数的长度大于两屏幕都是比较危险的信号。

    另一个能够体现模块划分水平的地方是依赖。如果一个模块依赖特别多,甚至出现了循环依赖,那么也可以反映出作者对模块的规划比较差,今后在维护这个工程的时候很有可能出现牵一发而动全身的情况。

    一般来说有不少工具能提供依赖分析,比如IDEA中提供的Dependencies Analysis功能,学会这些工具的使用对于评价代码质量会有很大的帮助。

    值得一提的是,绝大部分情况下,不恰当的模块划分也会伴随着极低的单元测试覆盖率:复杂模块的单元测试非常难写的,甚至是不可能完成的任务。所以直接查看单元测试覆盖率也是一个比较靠谱的评价方式。

    4.3.简洁与抽象
    只要提到代码质量,必然会提到简洁、优雅之类的形容词。简洁这个词实际涵盖了很多东西,代码避免重复是简洁、设计足够抽象是简洁,一切对于提高可维护性的尝试实际都是在试图做减法。

    编程经验不足的程序员往往不能意识到简洁的重要性,乐于捣鼓一些复杂的玩意并乐此不疲。但复杂是代码可维护性的天敌,也是程序员能力的一道门槛。

    跨过门槛的程序员应该有能力控制逐渐增长的复杂度,总结和抽象出事物的本质,并体现到自己设计和编码中。一个程序的生命周期也是在由简入繁到化繁为简中不断迭代的过程。

    对于这部分我难以总结出简单易行的评价标准,它更像是一种思维方式,除了要理解、还需要练习。多看、多想、多交流,很多时候可以简化的东西会大大超出原先的预计。

    4.4.推荐阅读
    《重构-改善既有代码的设计》

    《设计模式-可复用面向对象软件的基础》

    《Software Architecture Patterns-Understanding Common Architecture Patterns and When to Use Them》

    结语
    这篇文章主要介绍了一些评价代码质量优劣的手段,这些手段中,有些比较客观,有些主观性更强。之前也说过,对代码质量的评价是一件主观的事情,这篇文章里虽然列举了很多评价手段。但是实际上,很多我认为没有问题的代码也会被其他人吐槽,所以这篇文章只能算是初稿,更多内容还需要今后继续补充和完善。

    虽然每个人对于代码质量评价的倾向都不一样,但是总体来说评价代码质量的能力可以被比作程序员的“品味”,评价的准确度会随着自身经验的增加而增长。在这个过程中,需要随时保持思考、学习和批判的精神。

    原文链接

    https://mp.weixin.qq.com/s?__biz=MzIwMzY1OTU1NQ%3D%3D&mid=2247485597&idx=1&sn=fbe292f3acfb629d1eb92983f4af97b8&chksm=96cd48d1a1bac1c7905785ea6e827b10ae107e024b55ad2576467571ec01ef57eba3c7546f9a&mpshare=1&scene=23&srcid=%23rd

    服务推荐

    展开全文
  • 编写高质量代码改善C#程序的157个建议
  • 编写高质量代码--Web前端开发修炼之道(含电子书及源代码) 编写高质量代码--Web前端开发修炼之道(含电子书及源代码) 编写高质量代码--Web前端开发修炼之道(含电子书及源代码) 编写高质量代码--Web前端开发修炼...
  • 编写高质量代码-电子版.pdf编写高质量代码-电子版.pdf编写高质量代码-电子版.pdf编写高质量代码-电子版.pdf
  • 编写高质量代码 改善Python程序的91个建议第1章 引论建议1:理解Pythonic概念建议2:编写Pythonic代码建议3:理解Python与C语言的不同之处建议4:在代码中适当添加注释建议5:通过适当添加空行使代码布局更为优雅、...

    编写高质量代码 改善Python程序的91个建议

    第1章 引论

    建议1:理解Pythonic概念

    建议2:编写Pythonic代码

    建议3:理解Python与C语言的不同之处

    建议4:在代码中适当添加注释

    建议5:通过适当添加空行使代码布局更为优雅、合理

    建议6:编写函数的4个原则

    建议7:将常量集中到一个文件

    第2章 编程惯用法

    建议8:利用assert语句来发现问题

    建议9:数据交换值的时候不推荐使用中间变量

    建议10:充分利用Lazy evaluation的特性

    建议11:理解枚举替代实现的缺陷

    建议12:不推荐使用type来进行类型检查

    建议13:尽量转换为浮点类型后再做除法

    建议14:警惕eval()的安全漏洞

    建议15:使用enumerate()获取序列迭代的索引和值

    建议16:分清=与is的适用场景

    建议17:考虑兼容性,尽可能使用Unicode

    建议18:构建合理的包层次来管理module

    第3章 基础语法

    建议19:有节制地使用from…import语句

    建议20:优先使用absolute import来导入模块

    建议21:i+=1 不等于 ++i

    建议22:使用with自动关闭资源

    建议23:使用else子句简化循环(异常处理)

    建议24:遵循异常处理的几点基本原则

    建议25:避免finally中可能发生的陷阱

    建议26:深人理解None,正确判断对象是否为空

    建议27:连接字符串应优先使用join而不是+

    建议28:格式化字符串时尽量使用.format方式而不是%

    建议29:区别对待可变对象和不可变对象

    建议30:[]、()和{}: 一致的容器初始化形式

    建议31:记住函数传参既不是传值也不是传引用

    建议32:警惕默认参数潜在的问题

    建议33:慎用变长参数

    建议34:深入理解str()和repr()的区别

    建议35:分清staticmethod和classmethod的适用场景

    第4章 库

    建议36:掌握字符串的基本用法

    建议37:按需选择sort()或者sorted()

    建议38:使用copy模块深拷贝对象

    建议39:使用Counter进行计数统计

    建议40:深入掌握ConfigParser

    建议41:使用argparse处理命令行参数

    建议42:使用pandas处理大型CSV文件

    建议43:一般情况使用ElementTree解析XML

    建议44:理解模块pickle优劣

    建议45:序列化的另一个不错的选择—JSON

    建议46:使用traceback获取栈信息

    建议47:使用logging记录日志信息

    建议48:使用threading模块编写多线程程序

    建议49:使用Queue使多线程编程更安全

    第5章 设计模式

    建议50:利用模块实现单例模式

    建议51:用mixin模式让程序更加灵活

    建议52:用发布订阅模式实现松耦合

    建议53:用状态模式美化代码

    第6章 内部机制

    建议54:理解build-in objects

    建议55:init() 不是构造方法

    建议56:理解名字查找机制

    建议57:为什么需要self参数

    建议58:理解MRO与多继承

    建议59:理解描述符机制

    建议60:区别getattr()和getattribute()方法

    建议61:使用更为安全的property

    建议62:掌握metaclass

    建议63:熟悉Python对象协议

    建议64:利用操作符重载实现中缀语法

    建议65:熟悉Python的迭代器协议

    建议66:熟悉Python的生成器

    建议67:基于生成器的协程及greenlet

    建议68:理解GIL的局限性

    建议69:对象的管理与垃圾回收

    第7章 使用工具辅助项目开发

    建议70:从PyPI安装包

    建议71:使用pip和yolk安装、管理包

    建议72:做paster创建包

    建议73:理解单元测试概念

    建议74:为包编写单元测试

    建议75:利用测试驱动开发提高代码的可测性

    建议76:使用Pylint检查代码风格

    建议77:进行高效的代码审查

    建议78:将包发布到PyPI

    第8章 性能剖析与优化

    建议79:了解代码优化的基本原则

    建议80:借助性能优化工具

    建议81:利用cProfile定位性能瓶颈

    建议82:使用memory_profiler和objgrash剖析内存使用

    建议83:努力降低算法复杂度

    建议84:掌握循环优化的基本技巧

    建议85:使用生成器提高效率

    建议86:使用不同的数据结构优化性能

    建议87:充分利用set的优势

    建议88:使用meltiprocessing克服GIL的缺陷

    建议89:使用线程池提高效率

    建议90:使用C/C++模块扩展提高性能

    建议91:使用Cython编写扩展模块

    展开全文
  • 给python开发人员提供的编写高质量代码的91个建议,很有用
  • C语言因为既具有高级语言特性,又具有汇编语言...作为一位C程序员,如何才能编写出健壮、优雅、高质量代码?本书作者结合自己在该领域十几年的钻研和实践,从以下几个大的方面梳理了经常困扰“入门者”的几大类问题:
  • 编写高质量代码-web前端开发修炼之道-源代码.pdf
  • C/C++之写出高质量代码

    千次阅读 2017-04-06 22:54:14
    感觉自己以前写的代码都是一些垃圾,真的low,根本不知道一些基本的编程规范,然后看了一些大神写的代码,清晰易懂,而且还很漂亮,给人一种赏心悦目的感觉,写一手高质量代码,是一个程序员最基本的素养,因此,...
  • 《编写高质量代码-Web前端开发修炼之道》配套的源代码
  • 高质量的Java代码提出了151条极为宝贵的建议。对于每一个问题,不仅以建议的方式从 正反两面蛤出了被实践证明为十分优秀的解决方案和非常精糕的解决方案,而且还分析了 问题产生的根源,犹如醍醐灌顶,让人...
  • html+js+css, 编写高质量代码-web前端开发修炼之道-源代码 本书的核心内容是围绕Web 前端开发的三大技术要素——HTML、CSS 和JavaScript 来深入地 探讨编写高质量的HTML 代码、CSS 代码和JavaScript 代码的方法、...
  • ????公众号:Python图书馆 (ID:python_library) 整理:????兆锋 获取图书:下载链接 热 文 推 荐 ????278页PDF:《Python数据分析基础》...推荐5个Excel自动化办公免费学习资源~(数据分析、Python、VBA等) ...Pa...
  • 代码整洁之道》这本书提出了一个观点:代码质量与其整洁度成正比,干净的代码,既在质量上可靠,也为后期维护、升级奠定了良好基础。书中介绍的规则均来自作者多年的实践经验,涵盖从命名到重构的多个编程方面,虽...
  • 如何写出高质量代码

    千次阅读 2017-08-04 00:10:25
    与其写出一段漏洞百出的代码,倒不如仔细分析再写出鲁棒的代码代码的鲁棒性鲁棒是英文 Robust 的英译,有时也翻译成健壮性。所谓的鲁棒性是指程序能够判断输入是否合乎规范要求,对不和要求的输入予以合理的处理。...
  • 编写高质量代码的50条黄金守则

    千次阅读 2020-08-14 23:01:56
    该系列文章由比特飞原创发布,计划用半年时间写完全50篇文章,为大家提供编写高质量代码的一般准则。 目录 编写高质量代码的50条黄金守则-Day 01(首选隐式类型转换) 编写高质量代码的50条黄金守则-Day 02(首选...
  • 三、高质量代码的命名法则 要点一:要名副其实 名副其实说起来貌似很简单,但真正做起来,似乎没那么容易。选个好名字要花一些时间,但其实选好名字之后省下来的时间,要比之前选名字时花掉的时间多得多。 ...
  • 你知道怎么写高质量代码吗?不要一上来就开始写代码,想清楚再动手,下面分享10个写代码的小技巧教你写出高质量代码: 1、重构思维模式 不要一上来就开始写代码,要掌握尽量多的重构方法,重构思维方式,...
  • 如何写出高质量代码

    千次阅读 2017-02-18 16:52:59
    从3方面确保代码的完整性在写代码之前,要考虑单元测试。如果能够设计全面的单元测试用例并在代码中体现出来,那么写出的代码自然也就是完整正确的了。通常我们从功能测试、边界测试和负面测试三方面来设计测试用例...
  • 《编写高质量代码:改善JavaScript程序的188个建议》是Web前端工程师进阶修炼的必读之作,将为你通往“JavaScript技术殿堂”指点迷津!内容全部由编写高质量的JavaScript代码的最佳实践组成,从基本语法、应用架构、...
  • Effective Python编写高质量python代码的59个有效方法百度网盘地址

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 390,735
精华内容 156,294
关键字:

高质量代码