Курс языка программирования Raku / Приложение 🆕 / Типы и текстовые машины / Объекты и классы / Exercises / Фигуры с общей ролью
Решение: Фигуры с общей ролью
Вот один из возможных вариантов решения задачи.
Код
role Shape {
method area { ... }
}
class Circle does Shape {
has $.radius;
method area { 3.14159 * $.radius ** 2 }
}
class Square does Shape {
has $.side;
method area { $.side ** 2 }
}
sub describe(Shape $shape) {
say "{$shape.^name}: {$shape.area}";
}
for Circle.new(radius => 2), Square.new(side => 3) -> $shape {
describe($shape);
}🦋 Исходный код можно найти в файле shapes-role.raku.
Вывод
Circle: 12.56636
Square: 9Комментарии
Строго говоря, вызов
$shape.areaсработал бы и без всякой роли. Raku разрешает методы по имени в момент вызова, поэтому, пока у каждого объекта случайно есть методarea, цикл работает, — это известно как утиная типизация. Что же тогда даёт нам здесь роль? Две гарантии.Роль — это тип, который можно потребовать.
describeобъявлена какsub describe(Shape $shape), поэтому она принимает только объекты, исполняющиеShape, и отвергает всё прочее ещё до выполнения тела. Передайте ей обычное число — и программа откажется компилироваться:describe(42); # Calling describe(Int) will never work with declared signature (Shape $shape)
Без роли такого типа просто нет: подпрограмма с утиной типизацией приняла бы любой аргумент и рухнула бы лишь позже, глубоко внутри, добравшись до
.area.Роль — это договор. Объявление
areaзаглушкой (method area { ... }) обязывает каждый класс, исполняющий роль, предоставить собственныйarea. Забудьте о нём — и ошибка не сможет остаться незамеченной:class Triangle does Shape { has $.base; has $.height; } # Method 'area' must be implemented by Triangle because it is required by roles: Shape. my $t = Triangle.new(base => 3, height => 4); say $t.area; # Stub code executed
Rakudo сообщает о недостающем методе сразу при композиции класса — программа отказывается компилироваться ещё до того, как появится хоть один объект
Triangle. И даже в реализации, которая приняла бы такое определение класса, ошибка всплывёт самое позднее при использовании метода: унаследованная от роли заглушка{ ... }— это настоящий код, который умирает сStub code executed, как только кто-нибудь её вызовет.$shape.^nameспрашивает у объекта имя его класса, поэтому одна и та жеdescribeверно подписывает каждый результат, не зная типа заранее.