網頁

2019/6/1

Java 副作用Side Effects

程式的副作用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。


參考:

沒有留言:

張貼留言