From time to time I find myself needing to explain what OO programming is. I've written that Objects are Experts, but as usual, there's a deeper lesson to be learned here.

Imagine that you've hired a new barista (espresso bartender) and you're teaching him how to make a latté. You explain

how to pull the grounds and start the espresso brewing

how to steam the milk and get a good foam

pouring the milk into a cup

pouring the espresso through the milk (and maybe making a nifty pattern on top)

You explain this over and over. That's procedural programming. Finally, one day you can walk in and just ask for latté. You don't have to tell your new barista how to do it, you just ask. Congratulations: your barista is now an object.

The code might look like this:

my $barista = Barista->new; my $drink = $barista->prepare('latté');

From the outside world, you've encapsulated a lot of knowledge and this snippet is easier to read than listing all of the steps.

my $grounds = pull_grounds($container); my $espresso = brew($grounds); my $milk = steam_milk( until => $temp, using => 'hands' ); my $latte = make_latte( $milk, $espresso );

Note that bit about " using => $hands ". Well, how does the barista know what the temperature is? Many experienced baristas will use their hands, but there's a problem with that. If you're not feeling well or you just put lotion on your hands, your temperature sensitivity is often off and you'll tend to not steam the milk to a hot enough temperature. Thus, an expert barista will usually use a thermometer to steam their milk and make sure it's correct. Now you have to update your procedural code:

my $grounds = pull_grounds($container); my $espresso = brew($grounds); my $milk = steam_milk( until => $temp, using => 'thermometer' ); my $latte = make_latte( $milk, $espresso );

If you had used objects, you would encapsulate this expert knowledge in your object and the calling code would not change:

my $barista = Barista->new; my $drink = $barista->prepare('latté');

That's not a big deal in this toy example, but in the real world, this is why objects are so popular: you put your expert knowledge in one place rather than spreading it out in procedural code. In my experience, as procedural code bases get larger, then tend to have less conceptual organization and these bits of expert knowledge get scattered throughout the code and often get out of synch, but with well designed OO systems, it's not only easier to keep the expert knowledge in one place, it's also easier to understand where this expert knowledge should be.

And that brings me to methods (and subroutines, while we're at it). I like to think of them as "little objects". You pass in all the state you need, they do their stuff and return the result. That's why I would write this:

my $milk = steam_milk( until => $temp, using => 'thermometer' );

Instead of this:

my $milk; do { $milk = steam_milk(); } until ( temperature($milk) >= $limit );

That brings me to a tiny refactoring I did today. I'm writing a new data import system for $client and I had the following code:

sub run { my $self = shift; while ( my $row = $self->_next ) { $self->_process($row); } } sub _process { my ( $self, $row ) = @_; $self->_extract_components($row); # a list of methods that rely on this data }

The _next() method was delegated to my parser:

has '_parser' => ( is => 'ro', isa => Parser, lazy => 1, builder => '_build_parser', handles => { num_lines => 'num_lines', _next => 'next', }, );

What's important is that the _extract_components() method extracts relevant data from the $row and we must always do this when we fetch the next row and set up the importer for processing the data. Some of the methods in _process() could be called from other places, meaning we had a problem: you must never, ever forget to call _extract_components() after _next() or else you would be processing leftover data from a previous line. Oops.

To fix that, you need to encapsulate the responsibility of setting up those components into your _next() method. My code now looks like this:

sub run { my $self = shift; while ( $self->_next ) { $self->_process_row; } } sub _next { my $self = shift; my $row = $self->_parser->next or return; $self->_extract_compents($row); return $self; } sub _process_row { my ( $self ) = @_; # _extract_components no longer needs to be called here

By no longer delegating _next() directly to the parser, but by calling it explicitly internally and having the _next() method call $parser->next itself, it can extract the components for us and ensure that we will never forget to extract those components.

So objects should encapsulate everything they're responsible for, but so should methods. When you see tightly coupled behaviors that must fire correctly but aren't grouped together in a method, look to see if you can simplify your code by grouping them into a method responsible for for those behaviors (this won't always be appropriate, of course).

When you follow this pattern, you may start to notice a pleasant side-effect. Compare this:

sub run { my $self = shift; while ( my $row = $self->_next ) { $self->_process($row); } }

To this:

sub run { my $self = shift; while ( $self->_next ) { $self->_process_row; } }

Notice how we're not passing around arguments any more? You've heard all the time that you should minimize the number of arguments you pass to subroutines and methods to subroutines. Argument lists in systems can get unwieldy if you're not careful and since my methods must all rely on data that's been properly extracted, they can ask the object for the data rather than rely on arguments that come from who knows where. This is the same reason we do this:

$person->name($name);

Instead of:

$person->{name} = $name;

Your code will be cleaner and your data more reliable.