The beauty of runtime traits

Ovid on 2006-09-04T14:53:34

The problem: let's say we need "platform" specific information for "regular" and "premium" customers. These customers have servers with us and they get to pick their operating system. The "platform" information depends upon which of the two types of customers and which of three types of operating systems we are using (this isn't quite what the problem is but you get the point). What I need to be able to do is this:

my $platform = Company::Platform->new;
print $platform->base_dir;
print $platform->lib_path;
my $dbh = $platform->dbh;
# ... and so on

But how do I represent this? There are several strategies, all of which really aren't too much fun. One way is to use some broken multiple-inheritance implementation. Another way, assuming we use Company::Platform::Premium and Company::Platform::Regular classes is to set up delegation for the various OS dependent classes we need. However, as it turns out, the OS stuff also is potentially dependent on whether it's a regular or premium customer (and vice versa) and this makes delegation a bit harder.

Or I can use traits (that's a nice little review by chromatic).

package Customer::Platform;

use strict;
use warnings;
use Class::Trait;

sub new {
    my $class = shift;
    # Whee! I can specify behavior when the constructor is called!
    Class::Trait->apply( $class, $class->_get_os_trait );
    bless {}, $class;
}

{
    my %trait_for = (
        MSWin32 => 'Customer::Platform::Trait::OS::Win32',
        freebsd => 'Customer::Platform::Trait::OS::FreeBSD',
        linux   => 'Customer::Platform::Trait::OS::Linux',
    );

    sub _get_os_trait {
        my $class = shift;
        my $os    = $class->_get_operating_system;
        return $trait_for{$os} or $class->_croak("No OS trait found for $os");
    }
}

sub _get_operating_system {$^O}    # testing hook

Now all of my OS-dependent behaviors are flattened into the class and delegation isn't necessary. Later, I might need to refactor the "premium" and "regular" customer behaviors into their own traits and everything which needs this behavior/knowledge can use it with ease. Problem solved.