AdSense

網頁

2019/5/4

Java 如何撰寫callback方法 - 使用命令模式(Command Pattern)

Callback方法,是指以參數型態傳入另一個方法中的方法。也就是在被傳入Callback方法的方法邏輯執行完後才要執行的方法。這解釋看過就好,看範例才會比較清楚。

Callback方法/函式在事件驅動(Event Driven)為主的語言JavaScript中用很多,可以參考JavaScript 什麼是Callback函式 (Callback Function)?

例如前端常用JavaScript透過Ajax來與後端的WebService進行非同步的溝通,就需要callback函式來處理response回來後的邏輯。


因為Java為物件導向程式語言,方法本身並無法做為一個物件做為方法的引數,所以在Java程式中要以類別及介面的方法來設計Callback方法,相對於以函式程式設計(Functional Programming)的JavaScript使用上不是那麼直覺。不過Java 8以後引入了Lambda語法的特性,使Java在實作匿名類別上也可以達到類似的撰寫方式。

而在Java中設計Callback方法可以利用命令模式(Command Pattern)或觀察者模式(Observer Pattern)來達成,本篇以Command Pattern來實作。


Callback又分為一般的同步(Synchronous)程式的Callback及多執行緒的非同步(Asynchronous)程式的Callback。


Synchronous Callback 同步回呼


public class Main {

    public static void main(String[] args) throws Exception {
        Caller caller = new Caller();
        
        Callback callback = new Callback() {

            @Override
            public void callback(String name) {
                System.out.println(name + " registered successfully!!\n");
            }};
            
        caller.register("Matt", callback);
        
        // Java lambda expression
        caller.register("John", (name) -> System.out.println(name + " registered successfully!!"));
    }
}

interface Callback {
    
    public void callback(String name);
    
}

class Caller {

    public void register(String name, Callback callback) throws Exception {
        System.out.println(name + " resitering...");
        Thread.sleep(2000);
        callback.callback(name);
    }
}

印出結果如下

Matt resitering...
Matt registered successfully!!

John resitering...
John registered successfully!!

範例中的Callback介面其實就是Command Pattern中的Command,用來封裝執行命令時的資訊(也就是name)及,而Caller類別則為Invoker調用者,負責調用Command的執行方法也就是Callback.callback(),而Main.main()方法則為Client。


Asynchronous Callback 非同步回呼


通常在Java提到非同步是指多執行緒程式。

public class Main {

    public static void main(String[] args) throws Exception {
        Caller caller = new Caller();
        
        Callback callback = new Callback() {

            @Override
            public void callback(String name) {
                System.out.println(name + " registered successfully!!");
            }
        };
            
        for (int i = 0; i < 10; i++) {
            caller.register("User" + i, callback);
        }
    }
}

interface Callback {
    
    public void callback(String name);
    
}

class Caller {

    public void register(String name, Callback callback) throws Exception {
        new Thread(new Runnable() {
            public void run() {
                System.out.println(name + " resitering...");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (callback != null) {
                    callback.callback(name);
                }
            }
        }).start();
    }
}

"可能"印出結果如下,因為執行緒的先後是我們無法控制的。

User2 resitering...
User3 resitering...
User6 resitering...
User7 resitering...
User0 resitering...
User1 resitering...
User4 resitering...
User5 resitering...
User8 resitering...
User9 resitering...
User0 registered successfully!!
User6 registered successfully!!
User9 registered successfully!!
User7 registered successfully!!
User8 registered successfully!!
User3 registered successfully!!
User5 registered successfully!!
User4 registered successfully!!
User1 registered successfully!!
User2 registered successfully!!


參考:

沒有留言:

AdSense