《Clean Code》

整洁代码

文章目录

  • 一、命名
      • 1.1 变量
      • 1.2 函数
          • Rule 11.【推荐】先整体后细节
      • 1.3 类
  • 二、格式
  • 三、条件语句
  • 四、对象和数据结构

一、命名

以业务为导向命名[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行也封装成一个价格计算函数,使整个方法在同一抽象层级上。

Rule 4. 【推荐】向下规则

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

    public void test(){
        //xxx
        a();
        //yyy
    }

    private void a() {
        //
        b();
        //
    }

    private void b() {
        //
    }

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

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;
  }
  //...
}
  • 重构后的代码:提炼函数之后逻辑更加清晰

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.3 类

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

避免使用动词(Manager、Processor)

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

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

Rule 3. 【推荐】高内聚

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

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

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

二、格式

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

int result = temp - 2*a*b ; 

三、条件语句

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

if (condition) {
  ...
  return obj;
}

// 接着写else的业务逻辑代码;

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();
        }
    }
    

    当新增三角形这种数据结构时,需要:添加三角形类、需要在计算类中新增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;
        }
    }
    

    当新增三角形这种数据结构时,需要:仅仅需要添加三角形实现类,实现接口重写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;
      }
    }
    

    如果新增用户的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);
    }
    

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

你可能感兴趣的:(代码整洁之道,CleanCode)