• 《Clean Code》


    整洁代码

    一、命名

    以业务为导向命名[operateMaxSaleQtyLogs] > 以技术命名[operateMaxSaleQtyLogList] > 随意命名 [logList]

    1.1 变量

    Rule 1. 【推荐】变量名称最好:名词、短语、形容词

    Rule 2. 【推荐】不要在变量中包含多余的信息

    badCase:User userWithNameAndAge = newUser()

    Rule 3. 【推荐】避免误导

    生成的单号,不要使用数字0、1和字母o、O、l、L

    Rule 4. 【推荐】做有意义的区分:避免废话

    product和productData、productInfo意思无区别

    customer和customerData没区别

    name和nameStr没区别

    1.2 函数

    Rule 1. 【推荐】函数名称动词 + 名词

    Rule 2. 【推荐】函数长度

    最好能20行以内

    造成长函数的原因:

    • 把多个业务处理流程放在一个函数里实现;
    • 把不同层面的细节放到一个函数里实现。

    Rule 3. 【推荐】只做一件事

    每个函数一个抽象层级(锁库-较高抽象、查询算法或查档期-中间抽象、最大售卖量转换为字符串-低抽象)

    反例:一个方法里,前20行代码在进行很复杂的基本价格计算,然后调用一个折扣计算函数,再调用一个赠品计算函数。
    此时可将前20行也封装成一个价格计算函数,使整个方法在同一抽象层级上。
    
    • 1
    • 2

    Rule 4. 【推荐】向下规则

    每个函数后面,都紧跟着位于下一抽象层级的函数

        public void test(){
            //xxx
            a();
            //yyy
        }
    
        private void a() {
            //
            b();
            //
        }
    
        private void b() {
            //
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Rule 5. 【推荐】参数个数尽量小于3个

    1)如果多个参数同属于一个对象,直接传递对象。

    例外: 你不希望依赖整个对象,传播了类之间的依赖性。

    2)将多个参数合并为一个新创建的逻辑对象。

    例外: 多个参数之间毫无逻辑关联。

    3)将函数拆分成多个函数,让每个函数所需的参数减少。

    1. 尤其是查询数据库时,将常用的查询参数封装为model。在构造查询条件的时候,if判空model中属性是否为null,不为null,则criteria

    Rule 6. 【推荐】减少记忆参数顺序的负担

    AssertEquals(expected,actual)==》 assertExpectedEqualsActual(expected,actual)

    Rule 7. 【推荐】尽量减少重复的代码,抽取方法

    超过5行以上重复的代码,都可以考虑抽取公用的方法。

    Rule 8.【推荐】需要进行参数校验

    执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参数错误导致中间执行回退,或者错误,代价更大。

    class A {
      void hello(List list);
      void hello(ArrayList arrayList);
    }
    
    List arrayList = new ArrayList();
    a.hello(arrayList);//调用的是hello(List list),因为arrayList的定义类型是List
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Rule 9.【推荐】不要返回null

    如果你打算在方法中返回null,不如抛出异常,或者返回特例对象(Collections。emptylist()等)

    Rule 10.【推荐】入参不要传递null

    入参为null,也很容易npe

    Rule 11.【推荐】先整体后细节

    大部分人阅读代码的习惯是,先看此方法整体1、2、3做什么,然后再看1中具体细节

    • 重构前的代码
    public void invest(long userId, long financialProductId) {
      Calendar calendar = Calendar.getInstance();
      calendar.setTime(date);
      calendar.set(Calendar.DATE, (calendar.get(Calendar.DATE) + 1));
      if (calendar.get(Calendar.DAY_OF_MONTH) == 1) {
        return;
      }
      //...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 重构后的代码:提炼函数之后逻辑更加清晰
    
    public void invest(long userId, long financialProductId) {
      if (isLastDayOfMonth(new Date())) { //先整体再细节
        return;
      }
      //...
    }
    
    public boolean isLastDayOfMonth(Date date) {
      Calendar calendar = Calendar.getInstance();
      calendar.setTime(date);
      calendar.set(Calendar.DATE, (calendar.get(Calendar.DATE) + 1));
      if (calendar.get(Calendar.DAY_OF_MONTH) == 1) {
       return true;
      }
      return false;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    1.3 类

    Rule 1. 【推荐】命名使用名词、名词短语叠

    避免使用动词(Manager、Processor)

    Rule 2. 【推荐】字段未分组避免大类的产生、

    可以将一个大类n多个字段,按照商品基本信息、档期信息、库存信息、其它信息等几个模块分组。

    Rule 3. 【推荐】高内聚

    类应该只有少数实体变量、类中方法操作的变量越多,类就越内聚。

    大函数 -> 小函数(使用了大函数的4个变量)-> 将4个变量提升为类的实体变量 -> 丧失内聚(新增了4个只是为了少量函数而存在的实体变量)

    -> 将这些想要共享某些实体变量的函数 和 实体,抽离为一个新的小类,这样就完成了大函数 到 小函数的拆分,同时类也符合高内聚

    二、格式

    Rule 1. 【推荐】加减空格,乘除不加空格

    int result = temp - 2*a*b ; 
    
    • 1

    三、条件语句

    Rule 1. 【推荐】少用if-else方式,多用哨兵语句式以减少嵌套层次

    if (condition) {
      ...
      return obj;
    }
    
    // 接着写else的业务逻辑代码;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Rule 2.【推荐】减少使用取反的逻辑

    不使用取反的逻辑,有利于快速理解。且大部分情况,取反逻辑存在对应的正向逻辑写法。

    Rule 3.【推荐】能用while循环实现的代码,就不用do-while循环

    四、对象和数据结构

    Rule 1.【推荐】面向对象编程

    eg1: 计算图形的面积

    • 面向对象编程

      // 正方形
      @Data
      public class Square {
          private Double side;
      }
      
      // 圆形
      @Data
      public class Circle {
          private Double r;
      }
      
      // 计算类
      public class Calculate {
      
          public double area(Object shape) {
      
              if (shape instanceof Square) {
                  Square square = (Square) shape;
                  Double side = square.getSide();
                  return side * side;
              }
      
              if (shape instanceof Circle) {
                  Circle circle = (Circle) shape;
                  Double r = circle.getR();
                  return Math.PI * r * r;
              }
              throw new NoSuchElementException();
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31

      当新增三角形这种数据结构时,需要:添加三角形类、需要在计算类中新增if判断条件,违背了开闭原则

    • 面向对象编程

      // 接口定义方法:计算面积
      public interface Shape {
      
          /**
           * 计算图形的面积
           * @return 面积
           */
          public double area();
      }
      
      
      // 具体的正方形
      public class Square implements Shape{
          private Double side;
      
          @Override
          public double area() {
              return side * side;
          }
      }
      
      // 具体的圆形
      public class Circle implements Shape{
      
          private Double r;
      
          @Override
          public double area() {
              return Math.PI * r * r;
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31

      当新增三角形这种数据结构时,需要:仅仅需要添加三角形实现类,实现接口重写area方法即可,符合开闭原则

    eg2:计算不同等级用户对应的图书价格

    • 面向对象编程

      // 获取微信读书,不同书对于不同等级用户的价格
      public double getWeiXinReadBookPrice(final User user, final Book book) {
        double price = book.getPrice();
        switch (user.getLevel()) {
          case UserLevel.SILVER: //银
            return price * 0.9;
          case UserLevel.GOLD:  // 金
            return price * 0.8;
          default:
            return price;
        }
      }
      
      // 获取Kindle,不同书对于不同等级用户的价格
      public double getKindleBookPrice(final User user, final Book book) {
        double price = book.getPrice();
        switch (user.getLevel()) {
          case UserLevel.SILVER: //银
            return price * 0.95;
          case UserLevel.GOLD:   // 金
            return price * 0.85;
          default:
            return price;
        }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25

      如果新增用户的Level:Plantinum铂金用户,则对应的书的具体价格又不一样,两个方法都要新增case,违背了开闭原则

    • 面向对象编程

      // 定义用户等级接口
      interface UserLevel {
        double getWeiXinReadBookPrice(Book book);
        double getKindleBookPrice(Book book);
      }
      
      // 白银用户,使用微信读书 和 Kindle读书,对应的书价
      class SilverUserLevel implements UserLevel {
        @Override
        public double getWeiXinReadBookPrice(final Book book) {
          return book.getPrice() * 0.9;
        }
        
        @Override
        public double getKindleBookPrice(final Book book) {
          return epub.getPrice() * 0.85;
        }
      }
      
      // 黄金用户,使用微信读书 和 Kindle读书,对应的书价
      class GoldUserLevel implements UserLevel {
        @Override
        public double getWeiXinReadBookPrice(final Book book) {
          return book.getPrice() * 0.8;
        }
        
        @Override
        public double getKindleBookPrice(final Book book) {
          return epub.getPrice() * 0.85;
        }
      }
      
      
      // 调用的时候,就不要使用Switch
      public double getWeiXinReadBookPrice(final User user, final Book book) {
        UserLevel level = user.getUserLevel()
        return level.getBookPrice(book);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38

      如果新增用户的Level:Plantinum铂金用户,则直接新增铂金类,实现接口,重写两个方法即可,其它地方均无需改动,遵循开闭原则

  • 相关阅读:
    ubuntu 内核版本
    Linux守护进程
    Python逆向之 eval 函数解析,看着一篇就会了,案例掌房
    qt 判断文件是否存在
    保护您的Web应用:使用雷池(SafeLine)WAF的入门指南
    小车测速模块使用介绍
    面试题1:简单介绍,有哪几种方法可以将内联元素转换为块级元素?
    Redis系列16:聊聊布隆过滤器(原理篇)
    元宇宙之问:产业与资本为什么扎堆元宇宙
    AWTK MODBUS Client channel 模型
  • 原文地址:https://blog.csdn.net/tmax52HZ/article/details/132998592