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.