Skip to content

Latest commit

 

History

History
151 lines (109 loc) · 3.69 KB

第十章-角色.adoc

File metadata and controls

151 lines (109 loc) · 3.69 KB

角色是属性和方法的集合; 但是,与类不同,角色仅用于描述对象行为的一部分; 这就是为什么一般来说,角色应该在类和对象中混合使用。通常,类用于管理对象,而角色用于管理对象内的行为和代码重用。

角色声明

声明 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 和一个方法 hellodoes 会把角色中的属性和方法混到类里面, 就像这些属性和方法一开始就存在于类中一样。

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() 方法, 冲突就消失了。所以避免冲突的方法是, 混合角色的时候注意同名属性和方法, 但这通常无法避免。

匿名角色

实例化角色

如果你实例化角色, 那么角色会变成类:

role Point {
    has $.x;
    has $.y;
    method abs { sqrt($.x * $.x + $.y * $.y) }
}

my $p = Point.new(x => 6, y => 8);
say $p ~~ Point;
say $p.abs;

输出:

True
10

实例化角色 Point 之后, $p 变成了 Point 类了。