Category: Perl6 Keywords: role Perl6
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
- 在 class 中定义 shake 来覆盖。或者该 shake 方法通过指定不同调用不同 role 的方法。
- 用 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::A12 和 Perl6::Bible::S12.