Erlang Generic Event Handling

2009-03-28
當我們編程去處理事件的時候,只需將事件作為一個信息發送給負責處理事件的進程:
RegProcName ! {event, E}

E就係一個事件(可以係任何Erlang數据類型),RegProcName即處理進程的注冊名。事件發送出去後,發送方就毋需關心該事件會被如何處理了~~

現在我們看看處理事件的進程(事件處理器 - event handler),看個例子:
-module(event_handler).
-export([make/1, add_handler/2, event/2]).

%創建一個名為Name的新事件處理器
%其接收到事件後,會執行函數no_op/1
make(Name) ->
register(Name, spawn(fun() -> my_handler(fun no_op/1) end)).

add_handler(Name, Fun) -> Name ! {add, Fun}.

%產生一個事件
event(Name, X) -> Name ! {event, X}.

my_handler(Fun) ->
receive
{add, Fun1} ->
my_handler(Fun1);
{event, Any} ->
(catch Fun(Any)),
my_handler(Fun)
end.

no_op(_) -> void.

上例的事件處理器有如下功能:
event_handler:make(Name):創建一個名為Name的事件處理器;
event_handler:event(Name, X):向名為Name的事件處理器發送事件X;
event_handler:add_handler(Name, Fun):將事件處理器的處理函數(最初係一個什么都不幹的函數no_op/1)替換為Fun,替換後當接收到事件時,就會調用新的處理函數Fun來處理該事件。

運行試試效果如何:
1> event_handler:make(errors).
true
2> event_handler:event(errors, hi).
{event, hi}

Well,沒咩特別事發生,因為現在的處理函數什么都不幹~~我們接著寫個回調模塊(callback module)來設立一個會做實際工作的事件處理函數:
-module(motor_controller).
-export([add_event_handler/0]).

add_event_handler() ->
event_handler:add_handler(errors, fun controller/1).

controller(too_hot) -> io:format("Turn off the motor!n");
controller(X) -> io:format("~w ignored event: ~p~n", [?MODULE, X]).

上述代碼編譯之後就會添加一個事件處理函數了:
3> c(motor_controller).
{ok, motor_controller}
4> motor_controller:add_event_handler().
{add, #Fun}

現在向事件處理器發送信息,將由函數motor_controller:controller/1來處理:
5> event_handler:event(errors, cool).
motor_controller ignored event: cool
{event, cool}
6> event_handler:event(errors, too_hot).
Turn off the motor
{event, too_hot}

好了,上面寫了一大堆,到底都做了些什么呢?首先我們提供了一個接收事件的事件處理器(這里係一個注冊名為errors的進程),然后定義了如何發送事件給該處理器(發送方毋需知道處理器會對事件作什么處理),最后我們看到怎么在運行過程中動態的更新事件處理函數。

譯自《Programming Erlang》

M-OSCAR | Powered by Blogger | Entries (RSS) | Comments (RSS) | Designed by MB Web Design | XML Coded By Cahayabiru.com