定义行为回调会导致您对回调模块的实现承担义务。在erlang中,模块只是函数容器,而不是类或接口。行为基于运行时模块名称解析Mod:fun()
. OTP 生成服务器 https://github.com/erlang/otp/blob/maint/lib/stdlib/src/gen_server.erl(检查一下)在传入后保留其回调模块名称:gen_server:start_link(CallbackModuleName, Args, Opts)
这是代码应用回调 https://github.com/erlang/otp/blob/maint/lib/stdlib/src/gen_server.erl#L303 init/1
:
init_it(Starter, Parent, Name0, Mod, Args, Options) ->
Name = name(Name0),
Debug = debug_options(Name, Options),
case catch Mod:init(Args) of
{ok, State} ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, State, Mod, infinity, Debug);
{ok, State, Timeout} ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, State, Mod, Timeout, Debug);
...
正在申请中init/1
传递的回调模块保存在Mod
参数,获取其最后一个值,执行您想要的操作并继续(或不继续,取决于最后一个值)。
假设我们有模块bla_impl
看起来像这样:
-module(bla_impl).
-behaviour(bla).
-export([init/1, start_link/0]).
start_link() -> bla:start_link(?MODULE). %% macro ?MODULE is resolved to bla_impl
init(Args) -> ... .
现在你需要在 bla 中说明你使用哪个模块:
-module(bla).
-export([start_link/1]).
start_link(Mod) -> Mod:init([]).
或者也许更好的解决方案是从配置中读取它:
-module(bla).
-export([start_link/0]).
start_link() ->
Mod = application:get_env(bla_app, callback_module),
Mod:init([]),
...
有很多方法可以做到这一点。
如您所见,这里没有魔法。即使没有,这也会起作用-behaviour(bla)
也没有指定回调-callback
。这只是编译器、工具和文档的信息。
来自erlang文档:行为 http://www.erlang.org/doc/design_principles/des_princ.html#id60128
顺便说一句。start_link
函数应该产生另一个进程并链接到它。
start_link(Mod) ->
spawn_link(Mod, init, [[]]).