Roles, Overloading and Advanced OOP
31.1 Roles
Roles provide horizontal reuse (like mixins/traits) with conflict detection at composition time. Prefer roles plus composition over deep inheritance.
package Role::Comparable;
use Moo::Role;
requires 'compare'; # consuming classes must implement compare
sub equals { my ($l, $r) = @_; $l->compare($r) == 0 }
sub less_than { my ($l, $r) = @_; $l->compare($r) < 0 }
package Role::Printable;
use Moo::Role;
requires 'to_string';
sub print_me { print shift->to_string, "\n" }
package Money;
use Moo;
with 'Role::Comparable', 'Role::Printable'; # compose roles
has cents => (is => 'ro', required => 1);
sub compare { $_[0]->cents <=> $_[1]->cents }
sub to_string { sprintf '$%.2f', $_[0]->cents / 100 }
package main;
my ($l, $r) = (Money->new(cents => 500), Money->new(cents => 750));
say $l->less_than($r) ? 'less' : 'not less';
say 'Money does Comparable' if $l->DOES('Role::Comparable');31.2 Operator Overloading
Overload handlers receive ($left, $right, $swapped). fallback => 1 lets Perl autogenerate related operators.
package Vector2D;
use v5.36;
use overload
'+' => \&add,
'-' => sub ($l, $r, $swap) { $swap ? $r->sub_vec($l) : $l->sub_vec($r) },
'*' => \&scale,
'==' => sub ($l, $r, @) { $l->{x} == $r->{x} && $l->{y} == $r->{y} },
'""' => \&to_string, # stringification
'bool'=> sub { 1 },
'<=>' => sub ($l, $r, $swap) { my $cmp = $l->len <=> (ref $r ? $r->len : $r); $swap ? -$cmp : $cmp };
sub new($class, $x, $y) { bless { x => $x, y => $y }, $class }
sub add($l, $r, @) { Vector2D->new($l->{x} + $r->{x}, $l->{y} + $r->{y}) }
sub sub_vec($l, $r) { Vector2D->new($l->{x} - $r->{x}, $l->{y} - $r->{y}) }
sub scale($v, $k, @) { Vector2D->new($v->{x} * $k, $v->{y} * $k) }
sub len($v) { sqrt($v->{x} ** 2 + $v->{y} ** 2) }
sub to_string($v, @) { "($v->{x}, $v->{y})" }
package main;
my $v = Vector2D->new(1, 2) + Vector2D->new(3, 4);
say "Result: $v"; # Result: (4, 6)
say $v * 2; # (8, 12)31.3 Design Patterns in Perl
Singleton: a state or file-scoped instance returned by instance().
Factory: a class method that loads and instantiates classes by name (use_module).
Strategy: code refs or role-based objects.
Observer: callbacks lists, or event emitters (Mojo::EventEmitter).
Dependency injection: constructor arguments, Bread::Board.
Plugins: Module::Pluggable finds plugin classes in a namespace.