和Parameterized Module一样的态度:把extend当作Erlang语言特性,当作代码复用的一种手段,不扯OOP,可能更好理解: ) 
 本文回答Erlang Inheritance 怎么用?如果是Parameterized Module呢? 什么时候使用? 

How To Use

  Erlang继承通过关键字extends实现,下面看一个简单的例子:

-module(alpha).
-export([c/2, b/1, a/0]).

a() ->
{this_is_module,?MODULE}.

b(S) ->
{this_is_module,?MODULE,S}.

c(S,T)->
{this_is_module,?MODULE,S,T}.
-module(beta).

-compile(export_all).

-extends(alpha).

show() ->
{hello_world,?MODULE,?BASE_MODULE}.
a() ->
{hello,?MODULE}.
这是一个五脏俱全的小例子,注意 ① beta模块添加-extends(alpha)继承了alpha模块, ② 同时beta模块里面定义了一个与alpha模块相同签名的方法a/0, ③ beta模块里面还有一个比较新鲜的宏?BASE_MODULE
Erlang R15B (erts-5.9) [source] [64-bit] [smp:2:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.9 (abort with ^G)
1> beta:show().
{hello_world,beta,alpha} %?BASE_MODULE是一个预定义的宏,表示父模块2> beta:b(12). %调用beta模块并没有定义的b/1方法{this_is_module,alpha,12}
3> beta:c(23,34). %调用beta模块没有定义的c/2方法{this_is_module,alpha,23,34}
4> beta:a(). %调用beta和alpha模块都定义了的a/0的方法
{hello,beta}
5> beta:module_info().
[{exports,[{show,0},{a,0},{module_info,0},{module_info,1}]},
{imports,[]},
{attributes,[{vsn,[97314629038901962574531905090625388152]},
{extends,[alpha]}]}, %注意这里
{compile,[{options,[error_summary]},
{version,"4.8"},
{time,{2012,2,17,4,2,12}},
{source,"/ligaoren/src/beta.erl"}]}]
6> beta:k().
** exception error: undefined function alpha:k/0 %异常信息亮了
上面是编译之后的执行结果,关键位置我已经添加了注释,梳理一下:
1.?BASE_MODULE是一个预定义的宏,表示父模块,本例中是alpha
2.使用了继承的模块,方法执行首先会在当前模块里面寻找对应的方法,如果找到就执行;如果在当前模块没有找到,就在其父模块寻找,如果父模块没有继承自其它模块就抛出异常;注意抛出异常的时候会是父模块不包含该方法,所以我说上面例子的最后一个调用beta:k().异常信息是亮点;如果alpha还继承自其它模块,方法调用会沿着继承链继续寻找,直到找到执行或者找不到抛出异常.
3.当前模块的继承信息可以在module_info里面看到:{extends,[alpha]}]}

支持多继承么?
   不支持多继承,当尝试在beta代码中指定再指定一个继承-extends(alpha2).的时候,就会提示extends属性节redefined.

支持继承链么? 
支持,我们增加beta2模块把 alpha ← beta 的继承链修改为:alpha ← beta ← beta2 是可以顺利执行

Inheritance & Parameterized Modules

昨天我们提到过一种比较特殊的Parameterized Module http://www.cnblogs.com/me-sa/archive/2012/02/16/Erlang0037.html
这就有问题了:普通模块可以继承Parameterized Module么?Parameterized Module可以继承Parameterized Module么?
-module(a,[X,Y]).
-compile(export_all).
a() ->
{this_is_module_a,X,Y}.
b(Z) ->
{this_is_module_a,X,Y,Z}.
c(Z) ->
{this_is_module_a,Z}.

%%模块设计目的:测试一下普通模块是不是可以继承Parameterized Module
-module(b).
-compile(export_all).
-extends(a).
b() ->
{this_is_module_b}.

%%模块设计目的:测试一下Parameterized Module是否可以继承另外一个Parameterized Module
-module(b2,[X,Y,Z]).
-compile(export_all).
-extends(a).

b2() ->
{this_is_module_b2,X,Y,Z}.

%%模块设计目的:测试Parameterized Module在参数和父模块参数个数相同时的情况
-module(b3,[X,Y]).
-compile(export_all).
-extends(a).

b() ->
{this_is_module_b3,X,Y}.
上面的模块顶部都标注了设计意图,我们直接看执行结果:
Erlang R15B (erts-5.9) [source] [64-bit] [smp:2:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.9 (abort with ^G)
1> b:b().
{this_is_module_b}
2> b:a().
** exception error: undefined function a:a/0
3> B = b:new(1,2).
{a,1,2}
4> B:a().
{this_is_module_a,1,2}
5>
5> B2= b2:new(1,2,3).
{b2,1,2,3}
6> B2:b2().
{this_is_module_b2,1,2,3}
7> B2:a().
** exception error: no function clause matching a:a({b2,1,2,3}) (a.erl, line 5)
8> B3 = b2:new(1,2).
{a,1,2}
9> B3:a().
{this_is_module_a,1,2}
10>
10> B4= b3:new(3,4).
{b3,3,4}
11> B4:b().
{this_is_module_b3,3,4}
12> B4:a().
{this_is_module_a,3,4}
13>
13> c(b3,[to_core]).
** Warning: No object file created - nothing loaded **
ok
14>
做到最后一个实验,我们测试的是一个非常巧合的情况,即:Parameterized Module继承了另外一个Parameterized Module,而且参数个数相同!
实际的结果 B4= b3:new(3,4).的时候首先在当前模块寻找new/2创建了模块实例:{b3,3,4},当调用方法B4:a().的时候,首先在自己模块里面寻找,没有找到就会沿着继承链继续寻找,那么,在a模块里面为什么可以执行成功呢?我们曾经讨论过模块实例实际上的结构是{module,State},这里很明显module是不同的({b3,3,4}模块名是b3,不是a)! 这就要编译出来a模块的Core Erlang来一探究竟了,下面是代码片段:
'a'/1 =
%% Line 5
fun (_cor0) ->
case _cor0 of
<THIS = {_cor2,X,Y}> when 'true' ->
%% Line 6
{'this_is_module_a',X,Y}
( <_cor1> when 'true' ->
( primop 'match_fail'
({'function_clause',_cor1})
-| [{'function_name',{'a',1}}] )
-| ['compiler_generated'] )
end
 <THIS = {_cor2,X,Y}>关键就在这里,这里并没有对模块名作匹配验证而仅仅提取了变量X,Y值,所以调用会成功!

When To Use

  显然继承能够实现代码复用和模块行为的复制,但是问题也在这里,由于代码分布在多个模块而且子模块覆盖父模块的方法并没有什么警告;排错,重构的难度都会增加;和Parametrized Module一样,可以用但是要控制使用范围.