目錄
前言
在我們平常開發過程中,由於項目時間緊張,代碼可以用就好,往往會忽視代碼的質量問題。甚至有些複製粘貼過來,不加以整理規範。往往導致項目後期難以維護,更別說後續接手項目的人。所以啊,我們要編寫出優雅的代碼,方便你我他,豈不美哉?
下面分享一些我在開發中常用的編碼中小建議
,如有不妥,歡迎大家一起交流學習。
衛語句
衛語句,就是把複雜的條件表達式拆分成多個條件表達式。比如 多個 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 (StringUtils.isNotBlank(student.getName()) && student.getName().equals("張三")) {
System.out.println("我的好朋友是 " + student.getName());
}
}
-------------------- after (建議) --------------------
public class Student {
... 省略name代碼
// 新增一個 判斷是否是我的好朋友方法
public boolean isGoodFriend(){
return StringUtils.isNotBlank(this.name) && 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
,但是在面向對象開發理念中,一個 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 面向對象
的思想嗎?
Stream
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;
}
}
public static void main(String[] args) {
// 調用的時候直接傳遞 class 即可
System.out.println(getResult(1, 2, SumOpearte.class));
}
根據傳入 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;
}
鏈接:https://juejin.im/post/5dafbc02e51d4524a0060bdd