31 May 2005
This post may be outdated due to it was written on 2005. The links may be broken. The code may be not working anymore. Leave comments if needed.

Intro.

role 是类代码的复用。有点类似模块可以将子程序导出到某一程序或另一模块,一个 role 能将它的属性和方法导出到另一个类。注意,它与继承不同。
很多语言都有这个功能:比如 Ruby 有 mixins, Java 有 interfaces, 和某些版本的 Smalltalk 有 traits. Perl6 的 role 比它们略好一点。

easy example

class Dog {
    does bark;
    ...
}
class Wolf does bark {
    ...
}
role bark {
    method barking {
        say 'Wooo!';
    }
}
my $dog = Dog.new();
$dog.barking(); # Wooo!
my $wolf = Wolf.new();
$wolf.barking(); # Wooo!
这就是 role, 一种没有什么继承关系的代码复用。:)
如果 role 里定义了私有方法,注意此方法不是对 role 是私有的,而是对使用的类。
role 里可以调用 role 也可以继承 class.最简单的讲 role 和 class 就是关键字不同。

mixins

如果在一个类后面加 does role 的话,那是在编译时就加进去了。
而在一个对象后面加 does role 的话,那是在运行时才加进去的。
# 上面的例子我们不在 class 后面或里面加  does bark
if $pet ~~ Dog|Wolf { $pet does bark; $pet.barking(); }

方法冲突

假设我们有 class Dog, role Pet 和 role Sentry. role 中都定义了方法 shake.
  • 如果 class 中定义了 shake, 那么隐藏 role 中定义的 shake.
  • 如果是 role Pet 继承 class Dog 的话(role Pet is Dog),那么该 role 隐藏 class 里定义的 shake.
  • class 定义 shake 后,隐藏的 role 中的 shake 可以通过 ./Sentry::shake() 或 ./Pet::shake() 调用。
  • 第二种情况是 class 中没有定义 shake, 那么又分为两种。编译时(在 class 后面加的)会报错,而运行时(在 $object 后面加的)会用后一个自动覆盖。
    # 假设 Dog 里没有定义 shake
    my $animal is Dog does Pet does Sentry;
    # 这里后面的 Sentry 里的 shake 会隐藏 Pet 的 shake
所以一般而言,碰到冲突的解决方案有:
  1. 在 class 中定义 shake 来覆盖。或者该 shake 方法通过指定不同调用不同 role 的方法。
  2. 用 multi 通过类型来避免冲突

property 和 but

如果你需要一个 role, 而这个 role 只有一个与它名字一样的属性的话,Perl6 中提供了关键字 property 来简化操作。
比如下面的代码就可以简化为一行:
my role answer {
    has int $.answer is rw;
}
 # 用 property 简化
my int property answer;
but 与 does 不同的是 but 在一个副本上作变动。
$a = 0 but answer(42);
# 等同于
$tmp = 0;  # $tmp 为 0 的一个副本
$tmp does answer; # answer 是前面定义的一个属性 role
$tmp.answer = 42;
$a = $tmp;
# 或者我们可以怎么写:
(($anonymous = 0) does answer).answer = 42;
$a = $anonymous;
这么定义后 $a 本身还是为 0, 只是它有一个 answer 属性。由于是 is rw 的,所以可以这么设置:
$a.answer = 44;
but 最大的用途是后面跟一个 Enum 枚举类型。Enum 稍后再说,或请参考 Perl6::Bible::A12Perl6::Bible::S12.


blog comments powered by Disqus