Object-Oriented Perl (Classic)

Perl Intermediate/Advanced — Object-Oriented Perl (Classic)
Perl Intermediate/Advanced
Course 2 · Chapter 3 · Object-Oriented Perl (Classic)

🏛️ Object-Oriented Perl (Classic)

This is the "old school" style of Perl OOP — predating the modern Moo/Moose approach Chapter 4 covers, and still genuinely common in legacy code you're likely to encounter in real work. There's no dedicated class keyword here: a "class" is just a package combined with a blessed reference, built entirely out of tools already covered — packages, references (Course 1, Chapter 9), and subroutines (Course 1, Chapter 7).

📦 Packages — Perl's Namespace Mechanism

package Animal; # ... subroutines defined here belong to the Animal namespace ... 1; # a .pm file must end with a true value — this is standard, if easy to forget

package Animal; declares a namespace — every subroutine defined after it, until the next package statement (or end of file), belongs to Animal. In real code, one package typically lives in its own .pm file named to match (Animal.pm), loaded elsewhere with use Animal; (Chapter 5 covers modules in depth).

🎯 bless — Turning a Reference into an Object

package Animal; sub new { my $class = shift; my $name = shift; my $self = { name => $name }; # an ordinary anonymous hashref (Course 1, Chapter 9) bless $self, $class; # marks that hashref as belonging to $class return $self; }

bless takes an ordinary reference (a hashref, almost always) and a class name, and marks that reference as an instance of that class — nothing more mysterious than that. new is not a language keyword — it's just a subroutine name, chosen entirely by convention. Nothing stops you naming a constructor something else, but every real Perl codebase calls it new, and deviating would only confuse readers.

📞 Methods — Just Subroutines with an Implicit First Argument

sub speak { my $self = shift; # the object itself — always arrives as the first element of @_ return "$self->{name} makes a sound"; } my $animal = Animal->new("Generic Animal"); say $animal->speak(); # Generic Animal makes a sound

$animal->speak() is really just syntax sugar for speak($animal) — Course 1, Chapter 7's @_-based argument passing, unchanged. The object arrives as the very first element of @_, which is why my $self = shift; is the near-universal first line of every method — exactly the same shift from Course 1, Chapter 3's array operations, now doing double duty as "unpack the invocant."

🧬 @ISA — Manual Inheritance

package Dog; our @ISA = ('Animal'); # Dog inherits from Animal sub speak { # overrides Animal's speak() my $self = shift; return "$self->{name} says Woof!"; }

@ISA ("is a") is a package variable listing a package's parent(s). When a method is called, Perl searches the current package first, then walks @ISA looking for a match — Dog defining its own speak means that version wins, but Dog still inherits Animal's new automatically, since Dog never defines its own.

💡 use parent Is the Cleaner Modern Spelling

Directly assigning @ISA works, but use parent -norequire, 'Animal'; is the more common modern form — it does the same thing, with slightly better error-checking behavior. Both are worth recognizing; @ISA itself is what's actually happening underneath either spelling.

⬆️ Calling the Parent's Method — SUPER::

package Puppy; our @ISA = ('Dog'); sub speak { my $self = shift; my $base = $self->SUPER::speak(); # calls Dog's speak(), not Puppy's own return "$base (but higher-pitched)"; }

SUPER::method() explicitly calls the parent class's version of a method rather than the current package's — useful for extending, rather than fully replacing, inherited behavior.

🔧 A Worked Example — Animal/Dog/Cat Hierarchy

package Animal; sub new { my ($class, $name) = @_; return bless { name => $name }, $class; } sub speak { my $self = shift; return "$self->{name} makes a sound"; } package Dog; our @ISA = ('Animal'); sub speak { my $self = shift; return "$self->{name} says Woof!"; } package Cat; our @ISA = ('Animal'); sub speak { my $self = shift; return "$self->{name} says Meow!"; } package main; my @animals = (Dog->new("Rex"), Cat->new("Whiskers")); foreach my $animal (@animals) { say $animal->speak(); # each object uses its OWN speak() automatically — polymorphism }

The foreach loop never checks "is this a Dog or a Cat" — calling ->speak() automatically runs whichever version belongs to that object's actual class. This is the identical polymorphism concept from the site's own Python course (py2-2's Dog/Cat example) — same idea entirely, expressed through completely different mechanics: Python's built-in class system versus Perl's manual package-plus-bless construction.

⚠ Forgetting to shift the Class Name First

A constructor's first argument is always the class name — Dog->new("Rex") passes @_ = ('Dog', 'Rex'), not just ('Rex'). Forgetting my $class = shift; as the very first line, before unpacking anything else, means every later argument is off by one — a classic, confusing bug where the constructor silently receives the wrong values with no error at all.

Classic Perl OOP vs. Python Classes

Python's class keyword, automatic self parameter, and __init__ constructor are all built directly into the language. Classic Perl OOP has none of that: self (called $self here) is an ordinary variable you manually shift off @_ yourself, new is a plain subroutine name rather than a reserved constructor keyword, and "class" simply means "package plus bless." Nothing is hidden or automatic — which is precisely why Chapter 4's Moo/Moose covers a considerably more ergonomic modern alternative, while this chapter's manual version remains essential for reading the vast amount of existing Perl code still built this way.

package

Declares a namespace — the closest thing classic Perl has to a "class."

bless

Marks an ordinary reference as belonging to a class.

Methods

$obj->method() is sugar for method($obj) — always shift first.

@ISA / SUPER::

Manual inheritance; SUPER:: explicitly calls the parent's version.

💻 Coding Challenges

Challenge 1: A Basic Class

Write a package Book with a new($title) constructor and a describe method returning "Title: $title". Create an instance and call describe on it.

→ Solution

Challenge 2: Inheritance with @ISA

Write a package Shape with an area method returning 0, then a subclass Square (using @ISA) with its own constructor storing a side and its own area method computing side * side.

→ Solution

Challenge 3: Extend with SUPER::

Given the chapter's Animal/Dog classes, write a LoudDog subclass (inheriting from Dog) whose own speak calls SUPER::speak and appends " (LOUDLY)" to the result.

→ Solution

🎯 What's Next

Next chapter: Modern OOP with Moo/Moose — a lighter, more ergonomic alternative to hand-rolled OOP, with attributes and roles.