天天看點

Erlang關鍵字之behaviour

使用 Erlang 程式設計的人都知道 OTP,而基于OTP 架構建立程序的時候,常用的有四大 behaviour: 

  • supervisor
  • gen_server
  • gen_fsm
  • gen_event

1.behaviour的定義以及用途?

定義:在 erlang 的編譯器中,behaviour 的作用是用來定義一個規約。定義好這個規約之後,任何遵守這個規約的子產品,必須按照規約中的要求,使用 -export([]). 導出對應的函數,導出完這些函數後,這些導出函數的調用将由 behaviour 統一支配。behaviour 隻不過是實作代碼組織的一種手段而已,behaviour 就是把代碼分成 通用部分 ,以及每一個回調子產品需要去做的 特殊部分 。這樣就好了解了,于是 使用 -behaviour() 定義的子產品都是這個 behaviour 所要求實作的回調子產品 。 

2.是否能自定義behaviour?

這是可以的,如果大家代碼讀得多了,就應該發現,很多優秀的開源項目中就自定義了 behaviour。例如經典的 rabbitmq 中,就将 erlang 自帶的 gen_server 進行了改進,寫了一個 gen_server2 的 behaviour,因為 gen_server 中的消息隊列是一個普通消息隊列不能滿足需要,改進後的 gen_server2 使用了帶優先級的消息隊列。當然了,在 erlang 本身的底層代碼裡面,也有寫過很多 behaviour,不細說了,總之 erlang 可以自定義 behaviour 。 

3.如何定義一個behaviour?

要定義一個 behaviour,首先你要建立一個子產品,它必須導出 behaviour_info/1 這個函數(注意 behaviour 是 帶 u 的),函數定義如下:

behaviour_info(callbacks) ->
    [{foo, 0}, {bar, 1}, {baz, 2}];

behavior_info(_) ->
    undefined.
           

傳入 callbacks 參數,必須傳回一個包含導出函數和參數個數的清單。 

這些導出函數,就是這些回調子產品所特有的部分,而通用部分,則寫在 behaviour 中。比如在 gen_server 中,當一個程序收到一個消息,在 behaviour 行為模型中,它會處理大部分通用的邏輯,例如,如果是 call 消息,它會根據 handle_call 函數的傳回值對 From 程序發送傳回消息,同時,處理完消息後,它會繼續循環進行消息處理,不多說,亮代碼: 

handle_msg({'$gen_call', From, Msg}, GS2State = #gs2_state { mod = Mod,  
                                                             state = State,  
                                                             name = Name,  
                                                             debug = Debug }) ->  
    case catch Mod:handle_call(Msg, From, State) of %%大家注意了,這裡就是調用了子產品中的handle_call()函數,并且擷取它的傳回值  
        {reply, Reply, NState} ->  
            Debug1 = common_reply(Name, From, Reply, NState, Debug),  
            loop(GS2State #gs2_state { state = NState,%%在這裡,處理完一條消息之後,繼續轉入到循環中去  
                                       time  = infinity,  
                                       debug = Debug1 });  

        {reply, Reply, NState, Time1} ->  
            Debug1 = common_reply(Name, From, Reply, NState, Debug),  
            loop(GS2State #gs2_state { state = NState,  
                                       time  = Time1,  
                                       debug = Debug1});  
        {noreply, NState} ->  
            Debug1 = common_debug(Debug, fun print_event/3, Name,  
                                  {noreply, NState}),  
            loop(GS2State #gs2_state {state = NState,  
                                      time  = infinity,  
                                      debug = Debug1});  
        {noreply, NState, Time1} ->  
            Debug1 = common_debug(Debug, fun print_event/3, Name,  
                                  {noreply, NState}),  
            loop(GS2State #gs2_state {state = NState,  
                                      time  = Time1,  
                                      debug = Debug1});  
        {stop, Reason, Reply, NState} ->  
            {'EXIT', R} =  
                (catch terminate(Reason, Msg,  
                                 GS2State #gs2_state { state = NState })),  
            reply(Name, From, Reply, NState, Debug),  
            exit(R);  
        Other ->  
            handle_common_reply(Other, Msg, GS2State)  

    end;
           

4.怎麼使用behaviour?

使用的時候,需要在子產品開頭 使用: 

-behaviour(behaviour_name).
           

同時需要定義并且使用 -export([]) 導出這個 behaviour 所要求的導出函數。 

注意了, 一個子產品是可以有多個 -behaviour(behaviour_name). 的 ,也就是說,這個子產品的一部分函數可以既是 A behaviour 的回調子產品,又是 B behaviour 的回調子產品。 

5.何時使用behaviour?

  最後這個話題就比較廣了,這是一個沒有絕對意義正确的問題,在 erlang OTP 中,那幾個主要程序的實作都是使用 behaviour 封裝了基本的操作。 

      對于 behaviour 和程序的關系,我曾經有過這樣的疑惑,隻有在建立程序的時候才能用 behaviour 嗎?最後得到的結論是:沒有關系! 因為在 erlang 的設計理念當中,子產品與程序,這是兩個概念,而 behaviour 與子產品的組織相關,是以它與程序沒有必然聯系。 

原文位址:https://blog.csdn.net/zhangzhizhen1988/article/details/8499871?spm=a2c6h.12873639.0.0.6fc23877YVhE8t