角色是属性和方法的集合; 但是,与类不同,角色仅用于描述对象行为的一部分; 这就是为什么一般来说,角色应该在类和对象中混合使用。通常,类用于管理对象,而角色用于管理对象内的行为和代码重用。
声明 Roles 就像声明 Class 一样, 在 role 中声明属性和方法就像在 Perl 6 的类中声明属性和方法那样。
角色使用关键字 role
放在所声明的角色名称前面。角色使用 does
关键字 mixed in, does
关键字放在角色名之前。
constant dt = Date.today.DateTime;
role CheckingIn {
has DateTime $.start = dt.later(hours => 9);
has DateTime $.end = $!start.later(hours => 12);
has Bool $.saturday = False;
method hello() { "Good Moring!" }
}
角色 CheckingIn
声明了三个属性: s.start
、$.end
、$.saturday
和一个方法 hello
。does
会把角色中的属性和方法混到类里面, 就像这些属性和方法一开始就存在于类中一样。
class JJL does CheckingIn {
has Str $.name;
method working() {
"{$!name}: has worked {self.end.hour - self.start.hour} hours."
}
method Str {
self.hello();
}
}
我们初始化类 JJL
的时候, 传递自有的属性和混合而来的属性:
my $jjl = JJL.new(
:name('jjy')
:start(DateTime.new(year => 2019, month => 4, day => 19, hour => 9)),
:end(DateTime.new(year => 2019, month => 4, day => 19, hour => 20)),
:saturday
);
say $jjl.working();
类只须声明某些特定的属性, 例如 JJL
中的 $.name
。角色中的属性可以有默认值, 角色中的方法可以有默认实现, 也可以没有默认实现。
role CheckingIn {
method hello() { ... }
}
上面的 …
是 yadayada 运算符, 表示不实现该方法, 只是占位符。JJL 类可以实现/覆盖角色中的方法:
class JJL does CheckingIn {
method hello() { "implementation or overrite" }
}
在 Class 中不能再声明和 Role 中同名的属性或方法!
Attribute '$!end' already exists in the class 'JJL', but a role also wishes to compose it at t.p6:11
类可以继承多个角色, 如果所继承的角色中有同名的属性/方法, 那么继承后就会发生冲突:
role Salary {
has Bool $.saturday = False;
has $.dollar;
method hello() { "Thans for your working!" }
}
我们声明了一个新的角色 Salary
, 其中 $.saturday
属性和 hello()
方法与角色 CheckingIn
中的同名, 我们同时继承这俩个角色:
class Employee does CheckingIn does Salary {
has Str $.name;
has Int $.age;
method Str { self.hello() }
}
这会导致属性冲突:
Attribute '$!saturday' conflicts in role composition
我们注释掉 Salary 中的 $!saturday
, 依然存在冲突
Method 'hello' must be resolved by class Employee because it exists in multiple roles (Salary, CheckingIn)
我们再去掉 Salary 中的 hello()
方法, 冲突就消失了。所以避免冲突的方法是, 混合角色的时候注意同名属性和方法, 但这通常无法避免。