程式的副作用Side Effects是指當一個表示式(expression),函式(function)或方法(method)會改變外面物件的狀態或內容時,這個函式就被稱為有副作用。
例如Collections.shuffle()
方法會改變List
物件的內容順序,所以有side effect。
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
System.out.println(list); // [1, 2, 3, 4, 5]
Collections.shuffle(list); // side effect
System.out.println(list); // maybe [3, 1, 5, 4, 2]
在看以下的例子,addCash()
因為會改變外部People
物件的cash
值,也就是改變了外部物件的狀態,所以有side effect。
public class Main {
public static void main(String[] args) {
People people = new People(1000);
addCash(people); // side effect
System.out.println(people.getCash()); // 2000
}
private static void addCash(People people) {
people.setCash(people.getCash() + 1000);
}
}
class People {
int cash;
public People(int cash) {
this.cash = cash;
}
public int getCash() {
return cash;
}
public void setCash(int cash) {
this.cash = cash;
}
}
而沒有side effect的函式,就是指不會改變外面物件狀態的函式,其只是單純地做計算,並將結果返回出去。
例如下面的calculateTotalCash()
僅是將每個People
物件的cash
做加總並回傳出去,並沒有影響外面People
物件的狀態,所以沒有side effect。
public class Main {
public static void main(String[] args) {
People p1 = new People(1000);
People p2 = new People(2000);
People p3 = new People(3000);
int totalCash = calculateTotalCash(p1, p2, p3); // no side effect
System.out.println(p1.getCash()); // 2000
System.out.println(p2.getCash()); // 2000
System.out.println(p3.getCash()); // 2000
System.out.println(totalCash); // 2000
}
private static int calculateTotalCash(People... peoples) {
return Arrays.stream(peoples).map(p -> p.getCash()).reduce(0, Integer::sum);
}
}
class People {
int cash;
public People(int cash) {
this.cash = cash;
}
public int getCash() {
return cash;
}
public void setCash(int cash) {
this.cash = cash;
}
}
一個函式給予相同的輸入永遠得到相同的輸出,沒有副作用,沒有IO存取,這樣的函式就稱為Pure Function(純函式)。
可以的話盡量不要撰寫有side effect的方法,因為比較難除錯,例如輸出結果不符預期,因為有副作用的方法默默地改變外面某個物件的狀態,要層層深入才能找到被更改的地方。不過實際工作上side effect方法是不可避免的,例如存取料庫,讀寫檔案,呼叫函式庫,呼叫外部API等方法都有side effect。
參考:
沒有留言:
張貼留言