在我们平常开发过程中,由于项目时间紧张,代码可以用就好,往往会忽视代码的质量问题。甚至有些复制粘贴过来,不加以整理规范。往往导致项目后期难以维护,更别说后续接手项目的人。所以啊,我们要编写出优雅的代码,方便你我他,岂不美哉?
下面分享一些我在开发中常用的编码中 小建议
,如有不妥,欢迎大家一起交流学习。
卫语句,就是把复杂的条件表达式拆分成多个条件表达式。比如 多个 if-elseif-else
嵌套, 可以拆分成多个 if
。如下面代码
-------------------- before -------------------- public void today() { if (isWeekend()) { if (isFee()) { System.out.println("study Android"); } else { System.out.println("play a game"); } } else { System.out.println("go to work"); } } -------------------- after (建议) -------------------- public void today() { // 提前过滤掉`特殊情况` if (!isWeekend()) { System.out.println("go to work"); return; // 提前return } //提前过滤掉`特殊情况` if (isFee()) { System.out.println("study Android"); return; // 提前return } // 更关注于 `核心业务`代码实现。 System.out.println("play a game"); } 复制代码
提前过滤掉 特殊
情况,更关注 核心
业务逻辑
我们平常开发的时候,应该编写 小而美
函数,避免 函数过长
。一般函数最好在15行以内( 建议
)
我们看看下面代码:
-------------------- before -------------------- if (age > 0 && age < 18){ System.out.println("小孩子"); } if (number.length() == 11){ System.out.println("符合手机号"); } -------------------- after (建议) -------------------- private static boolean isChild(int age) { return age > 0 && age < 18; } private static boolean isPhoneNumber(String number) { return number.length() == 11; } if (isChild(age)){ System.out.println("小孩子"); } if (isPhoneNumber(number)){ System.out.println("符合手机号"); } 复制代码
把判断语句抽取成一个个 小函数
, 这样代码更加清晰明了。
迪米特法则(Law of Demeter)又叫作最少知识原则(Least Knowledge Principle 简写LKP),就是说一个对象应当对其他对象有尽 可能少
的 了解
。例如 当一条语句中 一个对象出现两个 .
( student.getName().equals("张三")
) 就是代码坏味道的表现,如下代码所示。
-------------------- before -------------------- public class Student { private String name; public Student(String name) { this.name = name; } public String getName() { return name; } } public static void main(String[] args) { Student student = new Student("张三"); // 注意看这里, // 这里获取 student的name属性,在根据name属性进行判断 if (student.getName().equals("张三")) { System.out.println("我的好朋友是 " + student.getName()); } } -------------------- after (建议) -------------------- public class Student { ... 省略name代码 // 新增一个 判断是否是我的好朋友方法 public boolean isGoodFriend(){ return this.name.equals("张三"); } } public static void main(String[] args) { Student student = new Student("张三"); // 根据迪米特法则,把判断逻辑,抽取到 Student 内部,暴露出方法(isGoodFriend) if (student.isGoodFriend()){ System.out.println("我的好朋友是 " + student.getName()); } } 复制代码
IDEA/Android Studio 抽取方法快捷键: option + command + M
我们在平常开发中,会使用到 map
,但是在面向对象开发理念中,一个 map
的使用,往往就会错过了 Java Bean
。建议使用 Java Bean
更直观。如下代码:
public static void main(String[] args) { -------------------- before -------------------- Map<String, String> studentMap = new HashMap<>(); studentMap.put("张三", "男"); studentMap.put("小红", "女"); studentMap.put("李四", "男"); studentMap.forEach((name, sex) -> { System.out.println(name + " : " + sex); }); -------------------- after (建议) -------------------- List<Student> students = new ArrayList<>(); students.add(new Student("张三", "男")); students.add(new Student("小红", "女")); students.add(new Student("李四", "男")); for (Student student : students) { System.out.println(student.getName() + ":" + student.getSex()); } } 复制代码
笔者在编写这点时候,有所顾虑。肯定有小伙伴跳出来说, map
和 bean
不是一样吗?用 map
我还可以省去思考如何命名 Class
呢。但是从代码规范来说,这样代码设计不是更符合 Java 面向对象
的思想吗?
Java 8 API添加了一个新的抽象称为流 Stream
,可以让你以一种声明的方式处理数据。使得代码调用起来更加优雅~
直接来看代码:
public static void main(String[] args) { List<Student> students = new ArrayList<>(); students.add(new Student("张三", "男")); students.add(new Student("李四", "男")); students.add(new Student("小红", "女")); students.add(new Student("小花", "女")); students.add(new Student("小红", "女")); -------------------- before -------------------- //统计男生个数 //传统的 for each 循环遍历 long boyCount = 0; for (Student student : students) { if (student.isBoy()) { boyCount++; } } System.out.println("男生个数 = " + boyCount); -------------------- after (建议) -------------------- //统计男生个数 //stream 流遍历 long count = students.stream() .filter(Student::isBoy) // 等同于.filter(student -> student.isBoy()) .count(); System.out.println("男生个数 = " + boyCount); } 复制代码
相比与 传统的 For
循环,更推荐大家使用 stream
遍历。 stream
流的链式调用,还有许多骚操作,如 sorted
, map
, collect
等操作符,可以省去不必要 if-else
, count
等判断逻辑。
Java 三大特性之一, 多态
,相信大家都不会陌生,多态的好处就是根据对象不同类型采取不同的的行为。我们常常在编写 switch
语句的时候,如果改用多态,可以把每个分支,抽取到一个子类内的覆写函数中,这就更加灵活。
我们有这样一个需求,编写一个简单计算器方法,我们先来看一小段代码:
-------------------- before -------------------- public static int getResult(int numberA, int numberB, String operate) { int result = 0; switch (operate) { case "+": result = numberA + numberB; break; case "-": result = numberA - numberB; break; case "*": result = numberA * numberB; break; case "/": result = numberA / numberB; break; } return result; } -------------------- after (建议) -------------------- abstract class Operate { abstract int compute(int numberA, int numberB); } class AddOperate extends Operate { @Override int compute(int numberA, int numberB) { // TODO 在这里处理相关逻辑 return numberA + numberB; } } ... SubOperate, MulOperate, DivOperate 也和 AddOperate一样这里就不一一贴出 public static int getResult(int numberA, int numberB, String operate) { int result = 0; switch (operate) { case "+": result = new AddOperate().compute(numberA, numberB); break; case "-": result = new SubOperate().compute(numberA, numberB); break; case "*": result = new MulOperate().compute(numberA, numberB); break; case "/": result = new DivOperate().compute(numberA, numberB); break; } return result; } 复制代码
有小伙伴可能会说,你这不是更复杂了吗?
对比起单纯的 switch
,我们可以这样理解:
加法
的逻辑, 我们只需要修改对应 AddOperate
类就可以了。避免直接修改 getResult
方法
但是这里会存在一些问题,如果我们新增一个 平方根
, 平方
等计算方式, 就需要修改 switch
里面的逻辑,新增一个条件分支。下面我们再来看看更进一步的优化。
通过上面例子,我们可以进一步优化,通过 反射
生成对应的 Class
,然后在调用 compute
方法。如下代码:
public static <T extends Operate> int getResult(int numberA, int numberB, Class<T> clz) { int result = 0; try { return clz.newInstance().compute(numberA, numberB); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); return result; } } 复制代码
根据传入 class
参数,然后生成对应 Opearte
处理类,
对比多态方式,我们这里采用反射,使得代码耦合度大大降低,如果在增加 平方根
, 平方
等计算方式。我们只需要 新增一个 class
继承 Opearte
即可, getResult
不用做任何修改。
需要注意的是,不是所有 switch
语句都需要这样替换, 在面对简单的 switch
语句,就不必要了,
避免 过度设计
的嫌疑。如下代码:
public String getResult(int typeCode) { String type = ""; switch (typeCode) { case 0: type = "加法"; break; case 1: type = "减法"; break; case 2: type = "乘法"; break; case 3: type = "触发"; break; } return type; } 复制代码