Modern OOP with Moo/Moose
✨ Modern OOP with Moo/Moose
bless/@ISA approach still common in legacy code. This chapter shows what most new Perl OOP code actually reaches for today: Moo and Moose, both of which eliminate nearly all of the boilerplate — hand-written accessors, manual constructors, manual @ISA assignment — the previous chapter required by hand.
⚖️ Moose vs. Moo — Which One?
| Moose | Moo | |
|---|---|---|
| Feature set | The original, full-featured, built on a complete meta-object protocol | A lightweight, largely Moose-compatible subset |
| Startup cost | Heavier — more dependencies, slower to load | Minimal dependencies, fast startup |
| Typical use | When you need Moose's deeper introspection/meta-programming features | The common default for most modern code |
This chapter uses Moo for its examples, since it's the more commonly reached-for modern default — the attribute/inheritance syntax shown below is nearly identical in Moose.
🏷️ Defining Attributes
has replaces every hand-written accessor from Chapter 3's $self->{name} pattern — declaring an attribute automatically generates a method of the same name. is => 'ro' gives you $animal->name (read-only); is => 'rw' additionally lets you call $animal->age(29) to update it. No manual hashref key access anywhere in your own code.
🏗️ The Automatic Constructor
Moo generates new for you — no manually shift-ing the class name and building a hashref by hand, per Chapter 3's constructor pattern. Attributes accept two more useful options:
This is the exact same root cause as Course 1, Chapter 2's mutable-default-argument gotcha, resurfacing in a new form: default => [] would build one arrayref when the attribute is declared, shared by every instance that doesn't supply its own — modifying it on one object would leak into every other. Wrapping it in sub { [] } defers building the default until each individual object is actually constructed, giving every instance its own independent, fresh reference.
🧬 Inheritance with extends
extends 'Animal'; is the entire inheritance declaration — considerably cleaner than manually assigning @ISA, and Dog still inherits Animal's attributes (name, its generated accessor, its constructor logic) automatically.
🎭 Roles — Composition Over Inheritance
A role (with 'Loud';) is genuinely different from inheritance — it's a bundle of methods composed directly into a class, rather than inherited from a parent up a chain. This sidesteps a lot of the awkwardness multiple inheritance can create (Course 3's Vim course, and the site's own OOP-focused chapters elsewhere, cover this same composition-over-inheritance instinct from other angles) — a class can compose several unrelated roles together without any of the ambiguity a shared parent class hierarchy might introduce.
🔧 A Worked Example — Rewriting Chapter 3's Animal Hierarchy
Compare this directly to Chapter 3's hand-rolled version: no manual new, no manual hashref-building, no manual @ISA assignment, no hand-written accessor for name — every piece that was pure boilerplate before is now generated by use Moo;, has, and extends, leaving only the logic that's genuinely specific to each class.
Moo/Moose Attributes vs. Python's @dataclass
The site's own Python Advanced course closes its capstone with @dataclass — auto-generating __init__, __repr__, and __eq__ from type-hinted attributes. Moo's has aims at genuinely the same problem (eliminate boilerplate for a data-holding class) but goes further by default: accessors, a full constructor with required/default handling, and — via extends/with — inheritance and composition, all from the same declarative style. Different language, same underlying instinct: declare what an object holds, let the framework generate the mechanical code around it.
has
Declares an attribute; is => 'ro'/'rw' controls getter/setter generation.
Automatic new
No manual constructor — required/default handle validation and defaults.
extends
Clean inheritance declaration, replacing manual @ISA assignment.
Roles (with)
Compose methods into a class directly — genuinely different from inheritance.
💻 Coding Challenges
Challenge 1: A Moo Class
Write a Moo class Book with a required, read-only title attribute and a read-write read attribute defaulting to false. Create an instance without setting read, then update it to true using the generated setter.
Challenge 2: Fix the Mutable Default
Given a buggy has 'tags' => (is => 'rw', default => []);, rewrite it correctly per this chapter's warn-box, and explain in one sentence why the original version is dangerous.
Challenge 3: A Role
Write a Moo role Describable with a method summary that returns "This is a " . ref($self). Compose it into a class Widget using with, and call summary on an instance.
🎯 What's Next
Next chapter: Modules & CPAN — use/require, writing your own module, and the CPAN ecosystem.