Inheritance is Bad: Code Reuse Part III

0. Overview

  1. Inheritance is Bad: Code Reuse Part I
  2. Inheritance is Bad: Code Reuse Part II
  3. Inheritance is Bad: Code Reuse Part III

1. Introduction

There exists a legend named the Gordian Knot. It was a knot that nobody was able to untie. An oracle predicted that the one who will be able to untie the knot would become the king of Asia. A lot of people tried to untie it, but nobody was successful. It seemed like an impossible task to do. But 333 BC they came a man, when he tried to untie it he drew his sword, sliced the knot and untied it that way. That man was Alexander the Great, and became later the King of Asia.

In the first part i talked about inheritance and explained why inheritance is at so many levels broken to archive a good code re-usability. I introduced roles and explained how roles can be a lot better for code re-usability and a much cleaner design. I build an example in part II to show roles in action. And we still faced some problems. Code re-usability still seems not to be as good as wanted. We still needed to copy & paste some code. If you need the ability to change behaviour at runtime there is not an easy solution for that. You still can run in naming problems, because all properties/methods in a role needs to be unique in order to easily reuse and assemble then in a class. Now we want to resolve the remaining problems.

Probably, you now expect some awesome super feature. Something shiny new. Probably a new library? A Moose Extension? How can all that be easy possible? And if it is possible, it has to be very complex, not easy, right? The Answer is no. It is extremely simple. All we need are just classes, nothing more. Now you probably will not believe me. Just classes? No inheritance or multiple inheritance? No Roles? Just Classes? Yep, just classes, nothing more. You don't believe me? It's okay, i will show you.

2. Roles?

Before we start you probably now ask why did i created two long parts and explained roles with so much detail. If "Just Classes" should be the solution then everything before should not be important at all. The Answer is, no not all. The Parts before are really important to understand this chapter. The important part of the previous articles was not that we used roles. The important part was to understand how to "Design" everything. For example it is not important that Position is a role. It is important to understand that Position is a behaviour or a piece of code that should not be part directly in a class, the important part to understand was that Position needs to be a separated piece of code that we are able to reuse. And the exact same is true for Destroyable.

What you should do when you designing something is exactly that what we have done so far in Part I + II. But instead of roles, you create classes. Now you probably don't see why that should solve any problem we still have open. So lets start with something easy. With refactoring Position.

3. Position

In the previous Part i mentioned that i still don't liked that we duplicated Position and created PositionRW, but there seemed no other solution to do it otherwise. All other solutions looked even more worse. So now lets rewrite Position from a role to a class and lets what actually changed. Another thing is that instead of Position i name it Vector2, because a 2D/3D Vector is just the way how you usually save a position in a game. On Top of it i add methods to add/subtract vectors and let them return their magnitude (length). And im creating an immutable Vector2 so we already can implement the not changing position for our Building. The reason i add those extra methods is that it should be more reasonable to understand why we not want to copy Vector2 to a Vector2RW or something else dirty ways like we did with PositionRW.

package Vector2;
use Moose;

has 'x' => (
    is       => 'ro',
    isa      => 'Int',
    required => 1,
);

has 'y' => (
    is       => 'ro',
    isa      => 'Int',
    required => 1,
);

# Vector2 add(Vector2 vector)
sub add {
    my ( $self, $vector ) = @_;
    return Vector2->new(
        x => $self->x + $vector->x,
        y => $self->y + $vector->y,
    );
}

# Vector2 subtract(Vector2 vector)
sub subtract {
    my ( $self, $vector ) = @_;
    return Vector2->new(
        x => $self->x - $vector->x,
        y => $self->y - $vector->y,
    );
}

# float magnitude()
sub magnitude {
    my ( $self ) = @_;
    return sqrt($self->x ** 2 + $self->y ** 2)
}

Now we converted our Position role to a class and named it Vector2. We also added some methods. Now how does our Building class change?

package Building;
use Moose;
use Vector2;

with 'Destroyable';

has 'name' => (
    is       => 'rw',
    isa      => 'Str',
    required => 1,
);

has 'position' => (
    is       => 'ro',
    isa      => 'Vector2',
    required => 1,
);

sub info {
    my ( $obj ) = @_;

    my $alive = $obj->is_alive ? "true" : "false";
    return sprintf("[Building: Name=%s Position=%d/%d Life=%d/%d Alive=%s]\n", $obj->name, $obj->position->x, $obj->position->y, $obj->current_life, $obj->max_life, $alive);
}

Instead of a "with 'Position'" statement we added a propertie with the name "position" that is a type of our Vector2 class. Now think of it, what changed so far? Did we solve one of our problems already? The first problem that we solved so far is our naming problems. When you write a class you don't have to think about which methods other classes uses. In our example we could name our methods just add(), subtract() or magnitude(). But what would you have done if our Vector2 was still a role? Just naming methods like add or subtract would probably not be a good idea. You would probably name them add_position() or something else. But the main point here is that your class can't collide with another class. How you name your propertie in your Building class is up to you. And the methods from Vector2 don't get injected in our Building class. Nothing can collide.

That also eliminate another problem. If you build your classes that way you usually don't end with with classes that have hundreds of methods. You have your position that is a Vector2. And absolutly everything related to Vector2 is part of an Vector2 object. Makes sense, or? And that is in my opinion an important point. Even something simple like an Vector2 usually have a lot of methods. You often want methods like normalizing a Vector, calculating the distance between two vectors, doing a linear interpolation between two vectors and so on. You could sure add everything also to a Vector2 Role, but it will automatically always fill your class with a lot of garbage.

But let's rethink, one main point that i wanted to eliminate was the creation of PositionRW. How do we now do it without copy & pasting? Well, we already have done it. Our Unit class now looks something like that.

package Unit;
use Moose;

with 'Destroyable';

has 'name' => (
    is       => 'rw',
    isa      => 'Str',
    required => 1,
);

has 'position' => (
    is       => 'rw',
    isa      => 'Vector2',
    required => 1,
);

# void move_by(Vector2 vector)
sub move_by {
    my ( $self, $vector ) = @_;
    $self->position($self->position->add($vector));
    return;
}

sub info {
    my ( $obj ) = @_;

    my $alive = $obj->is_alive ? "true" : "false";
    return sprintf("[Unit: Name=%s Position=%d/%d Life=%d/%d Alive=%s]\n", $obj->name, $obj->position->x, $obj->position->y, $obj->current_life, $obj->max_life, $alive);
}

Now you probably feel cheated? But thats it. We already have a working Vector2. Nobody said that the object itself needs to be a mutable object. For our design it is absolutely fine to work with immutable objects. And if you want to allow that an object can move. No problem, instead of a "ro" propertie you just use a "rw" propertie. Job done.

We don't need to copy & paste code or some other ugly way. Do you remember in the second part that i mentioned that our role lock away our code and makes re-usability even more worse? Well that is exactly it. If you design it as a role you only can inject it in a class. But the code itself will become a part of the class. In this way we have an object of a class and pass it around. In my opinion that is really what "object orientation" is about. Having objects to work with. Not a bunch of properties and methods that get injected like you do it in procedural code. Here is another example of re-usability that now is easily solved.

Now our Unit can move, but in which direction does our Unit move? Now you probably want to save the "target position" in your class. How do you do it with our class? Well it is very easy. Just add another Vector2 propertie.

has 'target' => (
    is       => 'rw',
    isa      => 'Vector2',
    required => 0,
);

What have we done? Well we have reused Vector2. We now have two instances of it, one in position, another one in target in one class. Lets assume Vector2 would be a Role. How do you use two instances of a role in one class? With a class that is easy. But think of it. How do you have it done with a role? Creating another role? Copying Vector2 lets say to Target and rename addposition to addtarget and so on, and then inject Target in your class? Wouldn't that be just stupid?

Now you probably say. Okay everything is fine. What you have shown works with Position or a Vector. But that's not gonna work with everything, and we still had some problems left. But yes, it really works with everything. The reason for that is that you already think of behaviours. The thing with behaviours is that every behaviour is usable on its own, because you already separated it to a single logic unit. And you always can convert roles like that just to a class.

Destroyable class

So let's look at our Destroyable Role. We just convert it to a class. Its really simple.

package Destroyable;
use Moose;

has 'max' => (
    is       => 'ro',
    isa      => 'Int',
    required => 1,
);

has 'value' => (
    is       => 'ro',
    isa      => 'Int',
    writer   => '_set_value',
    required => 1,
);

has 'is_alive' => (
    is      => 'ro',
    isa     => 'Bool',
    lazy    => 1,
    builder => '_build_is_alive',
    writer  => '_set_is_alive',
);

sub _build_is_alive {
    my ( $self ) = @_;
    return $self->value > 0;
}

sub add {
    my ( $self, $amount ) = @_;
    my $max = $self->max;
    my $val = $self->value;
    if ( $val + $amount <= $max ) {
        $self->_set_value($val + $amount);
    }
    else {
        $self->_set_value($max);
    }
    return;
}

sub subtract {
    my ( $self, $amount ) = @_;
    my $min = 0;
    my $val = $self->value;
    if ( $val - $amount >= $min ) {
        $self->_set_value($val - $amount);
    }
    else {
        $self->_set_value(0);
        $self->_set_is_alive(0);
    }
    return;
}

There are not many changes to the Role. Instead of Moose::Role we just use Moose. I added a "require" statement to the properties, a builder for isalive. And i changed the name of some properties and methods. Instead of "maxlife" now it is just "max". "currentlife" => "value". Instead of "addlife" and "subtract_life" it is now just "add" and "subtract". This changes are now possible and good, because we have an class and later an object. There is no need to prefix/postfix values. But besides some naming changes it is nearly the same as the role. Now lets lock at our Building class how it will change.

package Building;
use Moose;
use Vector2;
use Destroyable;

has 'name' => (
    is       => 'rw',
    isa      => 'Str',
    required => 1,
);

has 'position' => (
    is       => 'ro',
    isa      => 'Vector2',
    required => 1,
);

has 'life' => (
    is       => 'ro',
    isa      => 'Destroyable',
    required => 1,
);

sub info {
    my ( $self ) = @_;

    my $life  = $self->life;
    my $alive = $life->is_alive ? "true" : "false";
    return sprintf("[Building: Name=%s Position=%d/%d Life=%d/%d Alive=%s]\n", $self->name, $self->position->x, $self->position->y, $life->value, $life->max, $alive);
}

We also can see here that we don't need anything special. Instead of injecting the Destroyable Role, we now have an attribute "life" that should be a Destroyable object. Now lets see some usage of our Building class.

my $hq = Building->new(
    name     => 'Headquarter',
    position => Vector2->new(x => 10, y => 10),
    life     => Destroyable->new(
        max   => 100,
        value => 100,
    ),
);

print $hq->info;
$hq->life->subtract(70);
print $hq->info;
$hq->life->subtract(70);
print $hq->info;
print $hq->position->magnitude, "\n";

Now our code itself doesn't change much. Instead of passing "maxlife", "currentlife" to our object, we now pass an object of Destroyable to our class. Instead of calling "$hq->subtract_life()" we now use "$hq->life->subtract(70)". In my opinion the later is much more cleaner. It is clear that you have an object that represents the life of our hq, and we can just call methods on our life to work with it. And once again you get better code re-usability because you can easily reuse Destroyable. Let's say every building also have some plasma shield that can absorb up to 50 damage. Once again it is easy. Just add an "shield" propertie of type Destroyable to your Building.

has 'shield' => (
    is       => 'ro',
    isa      => 'Destroyable',
    required => 1,
);

And your construction of $hp will change to this.

my $hq = Building->new(
    name     => 'Headquarter',
    position => Vector2->new(x => 10, y => 10),
    life     => Destroyable->new(
        max   => 100,
        value => 100,
    ),
    shield => Destroyable->new(
        max   => 50,
        value => 50,
    ),
);

Delegation

In my opinion something like "$hq->life->subtract(70)" is a clean interface. We directly see on the Top Level which objects we have, and we can operate on them. But for whatever reason some people want a "subtract_life()" method instead of calling "life->subtract()". If you also like to pollute your classes with a lot of garbage you also can do that easily with Delegation. For example you can change the Attributes in the Building class this way.

has 'life' => (
    is       => 'ro',
    isa      => 'Destroyable',
    required => 1,
    handles  => {
        'add_life'      => 'add',
        'subtract_life' => 'subtract',
    }
);

has 'shield' => (
    is       => 'ro',
    isa      => 'Destroyable',
    required => 1,
    handles  => {
        'add_shield'      => 'add',
        'subtract_shield' => 'subtract',
    }
);

The good part is as you can see here. You also can easily create "subtractlife" and "subtractshield" methods, without creating two roles. So you can still have your objects with hundreds of methods or properties without all that object chaining if that is something what you want. You can read more of it in Moose::Manual::Delegation.

Multiple Implementations

At the moment we had rewritten Destroyable to a class. But one problem what we didn't face so far is what i named "Multiple Implementation". What we wanted to do is to have multiple Destroyable implementations with different features. At best we want to separate every implementation and put it in their own file. But the important part was not separation alone. In our example we could just create a DestroyableResistances role if that was everything what we wanted. The important part was that we also can switch between the different implementations at runtime.

If you think about it. Switching the implementation is very easy. With our design all we have is just an object of class. If you just want to switch the implementation the only thing we need is to put another object in it. Or allow Moose another type. At the moment our definition for life in Building looks something like this.

has 'life' => (
    is       => 'ro',
    isa      => 'Destroyable',
    required => 1,
);

The first thing we need to change is make it "rw" to allow changing. The next is to change the "isa". We could for example change the isa to something like "Destroyable|DestroyableResistances". With a definition like this Moose will accept both classes. But that is still not a good way. The reason is we don't want to hardcode all our implementations in it. If you have 10 implementations and use in a lot of classes that is really a bad way. For example we also have our "shield" propertie und you need to add DestroyableResistances to it. And if you have other classes like Enemy, Unit, Dragon and so on that also needs a type or subtype of Destroyable you always have to name all classes that implements your behaviour, or change all entries whenever you add a new implementation. That is not a good way. We can fix that problem also easily, but at the moment for the sake of easiness we will change the isa value to "Object". Now Moose just ensure we got some Object. Later we will come back to this again and we will fix this. So our new life now should look something like that.

has 'life' => (
    is       => 'rw',
    isa      => 'Object',
    required => 1,
);

Lets assume we already had written our DestroyableResistances class. How can we now switch the implementation at runtime? Well it is really easy. We just create an object and assign it to life.

$hq->life(DestroyableResistances->new(...));

At some point that is so easy, it even sounds stupid to write about it, because it so obvious.

DestroyableResistances class

Now understanding how you can switch the implementation at runtime with easiness. Let's just write our DestroyableResistances implementation. The important part here is that we need the exact same interface. Only then we are able to switch objects without problems. Now lets look at our Destroyable class. Our Destroyable class had "max", "value" and "is_alive" as properties. And two methods named "add" and "subtract". When we now write our DestroyableResistances class we also need the exact same interface, to be able to switsch implementation without problems. But all that doesn't mean we can't have additional properties or methods. At least we write another implementation. In order to have a different behaviour we often have additional properties and the construction itself differs. But after that is done the class itself needs to behave the same. And the same means it needs the same properties, methods needs to take the same arguments and needs to return the same types. For example if our add/subtract would return a number, all other implementation also have to do it. But of course, the result itself can differ. And because it is another implementation it is also what we probably expect.

What we now want is a Destroyable implementation where it is possible to configure some Resistances. In the second part i talked about a dragon with fire damage and so on, but implementing this would mean we also have to implement types of damage, and we have to change some code to allow that. So for the sake of easiness i just implement a "global" resistance. Something like: We have 50% resistances. Now we only got half the damage. The amount of resistances should be changeable. To make the calculation easy our resistance is a number between 0 and 1. So 50% resistance is 0.5, 70% should be 0.3 and so on. So lets write our class.

package DestroyableResistances;
use Moose;

has 'max' => (
    is       => 'ro',
    isa      => 'Int',
    required => 1,
);

has 'value' => (
    is       => 'ro',
    isa      => 'Int',
    writer   => '_set_value',
    required => 1,
);

has 'is_alive' => (
    is      => 'ro',
    isa     => 'Bool',
    lazy    => 1,
    builder => '_build_is_alive',
    writer  => '_set_is_alive',
);

# Resistances as a value between 0 and 1
has 'resistances' => (
    is       => 'rw',
    isa      => 'Num',
    required => 1,
);

sub _build_is_alive {
    my ( $self ) = @_;
    return $self->value > 0;
}

sub add {
    my ( $self, $amount ) = @_;
    my $max = $self->max;
    my $val = $self->value;
    if ( $val + $amount <= $max ) {
        $self->_set_value($val + $amount);
    }
    else {
        $self->_set_value($max);
    }
    return;
}

sub subtract {
    my ( $self, $amount ) = @_;
    $amount = $amount * $self->resistances;
    my $min = 0;
    my $val = $self->value;
    if ( $val - $amount >= $min ) {
        $self->_set_value($val - $amount);
    }
    else {
        $self->_set_value(0);
        $self->_set_is_alive(0);
    }
    return;
}

What we now can do is something like this

my $hq = Building->new(
    name     => 'Headquarter',
    position => Vector2->new(x => 10, y => 10),
    life     => Destroyable->new(
        max   => 100,
        value => 100,
    )
);

print $hq->info;
$hq->life->subtract(50);
print $hq->info;

$hq->life(
    DestroyableResistances->new(
        max         => $hq->life->max,
        value       => $hq->life->value,
        resistances => 0.5,
    )
);

$hq->life->subtract(50);
print $hq->info;

The output of our application

[Building: Name=Headquarter Position=10/10 Life=100/100 Alive=true]
[Building: Name=Headquarter Position=10/10 Life=50/100 Alive=true]
[Building: Name=Headquarter Position=10/10 Life=25/100 Alive=true]

What we have seen so far is a second implementation of a Destroyable with resistances. At first our $hq starts with a simple Destroyable. We do 50 damage against it and our life sunk to 50/100. At runtime we changed the implementation to DestroyableResistances with 50% resistances. Another 50 damage now just reduced our life from 50/100 to 25/100. What we expected. The only proplem when you look at Destroyable and DestroyableResistances is, we have Code Duplication once again.

Refactoring Destroyable & DestroyableResistances

When you look at our classes then once again we see a lot of Code Duplication. Didn't i tell that with this way we can eliminate Code Duplication? Sure, you can, but that doesn't mean you automatically by magic get it. If you have Code Duplication you have to look at your code and try to abstract the Duplication. Now lets do it.

At first, what is Code Duplication anyway, and how do we abstract it? At first we have a specific interface that we always need. So the existence of some specific properties or methods itself is not Code Duplication. They have to be there to full fill our interface. But if you have classes that always needs the same properties over and over again or you have properties that together build a "unit" then you should try to abstract it away. In our Destroyable class you already see that king. Look at the attribute "max" and "value". Which purpose do have "max" alone? If you think about it, max is the limit for "value". Sure you can have "value" alone but in this case we need max to specify the maximum possible value for "value". So we should try to abstract that.

Another way to find what you need to abstract away is if you look at the code of your methods. Which code exists in both classes and analyse what they do. If you look at both classes, well there are almost identical. In the add() method we add a value to "value", and then we try to ensure that our "max" is not reached. In "subtract" we do the same, but ensure our minimum value here 0 is not reached. So what we do is to ensure our "value" is always in a range. Why not abstract it away? Let's create a MinMaxValue class.

package MinMaxValue;
use Moose;

# We want:
# MinMaxValue->new(Num max, Num value)
# MinMaxValue->new(Num max, Num value, Num min)
around BUILDARGS => sub {
    my $orig  = shift;
    my $class = shift;

    if ( @_ == 2 || @_ == 3 ) {
        my $max   = shift;
        my $value = shift;
        my $min   = shift // 0;

        return $class->$orig(max => $max, value => $value, min => $min);
    }
    else {
        return $class->$orig(@_);
    }
};

has 'max' => (
    is       => 'rw',
    isa      => 'Num',
    required => 1,
);

has 'min' => (
    is       => 'rw',
    isa      => 'Num',
    default  => 0,
    required => 0,
);

has 'value' => (
    is       => 'rw',
    isa      => 'Num',
    required => 1,
    trigger  => \&_check_min_max,
    reader   => 'get',
    writer   => 'set',
);

sub add {
    my ( $self, $amount ) = @_;
    $self->set($self->get + $amount);
    $self->_check_min_max;
    return;
}

sub subtract {
    my ( $self, $amount ) = @_;
    $self->set($self->get - $amount);
    $self->_check_min_max;
    return;
}

sub _check_min_max {
    my ( $self ) = @_;
    if ( $self->get > $self->max ) {
        $self->set($self->max);
    }
    if ( $self->get < $self->min ) {
        $self->set($self->min);
    }
    return;
}

Our Destroyable class now looks like this

package Destroyable;
use Moose;
use MinMaxValue;

# We want:
# Destroyable->new(max => 100, value => 100)
around BUILDARGS => sub {
    my $orig  = shift;
    my $class = shift;
    my %args  = @_;

    $args{value} = MinMaxValue->new($args{max}, $args{value}, 0);

    $class->$orig(%args);
};

has 'value' => (
    is       => 'ro',
    isa      => 'MinMaxValue',
    required => 1,
);

has 'is_alive' => (
    is       => 'ro',
    isa      => 'Bool',
    init_arg => undef,
    lazy     => 1,
    builder  => '_build_is_alive',
    writer   => '_set_is_alive',
);

sub _build_is_alive {
    my ( $self ) = @_;
    return $self->value > 0;
}

sub add {
    my ( $self, $amount ) = @_;
    $self->value->add($amount);
    $self->_check_alive;
    return;
}

sub subtract {
    my ( $self, $amount ) = @_;
    $self->value->subtract($amount);
    $self->_check_alive;
    return;
}

sub _check_alive {
    my ( $self ) = @_;
    if ( $self->value <= $self->value->min ) {
        $self->is_alive(0);
    }
}

And our DestroyableResistances like this

package DestroyableResistances;
use Moose;
use MinMaxValue;

# We want:
# Destroyable->new(max => 100, value => 100, resistances => 0.5)
around BUILDARGS => sub {
    my $orig  = shift;
    my $class = shift;
    my %args  = @_;

    $args{value}       = MinMaxValue->new($args{max}, $args{value}, 0);
    $args{resistances} = MinMaxValue->new(1, $args{resistances}, 0);

    $class->$orig(%args);
};

has 'value' => (
    is       => 'ro',
    isa      => 'MinMaxValue',
    required => 1,
);

has 'resistances' => (
    is       => 'ro',
    isa      => 'MinMaxValue',
    required => 1,
);

has 'is_alive' => (
    is       => 'ro',
    isa      => 'Bool',
    init_arg => undef,
    lazy     => 1,
    builder  => '_build_is_alive',
    writer   => '_set_is_alive',
);

sub _build_is_alive {
    my ( $self ) = @_;
    return $self->value > 0;
}

sub add {
    my ( $self, $amount ) = @_;
    $self->value->add($amount);
    $self->_check_alive;
    return;
}

sub subtract {
    my ( $self, $amount ) = @_;
    $self->value->subtract($amount * $self->resistances->get);
    $self->_check_alive;
    return;
}

sub _check_alive {
    my ( $self ) = @_;
    if ( $self->value <= $self->value->min ) {
        $self->is_alive(0);
    }
}

Now we reduced some Code Duplication. Instead of always checking if our value rechead the maximum or minimum we now have a separated MinMaxValue that does that. So we don't need this code in our methods anymore. But our Destroyable class also needs to check if our object is alive. The only thing we added in Destroyable is just a check when our MinMaxValue hits 0. If it hits zero is_alive will be set to a false value. If you look at it our Destroyable consumes a MinMaxValue and just add some extra functionality to it. At some way, it is like inheritance. With inheritance you would probably create a class named Destroyable inherit from MinMaxValue and you would add your specific code.

Now this example probably looks a little bit more complicated. Instead of inherit from it, we embed another class/object. The benefit is we don't run in any problems that we usaly got with inheritance (Look at Part I if you forgot). You can embed as many objects like you want. You don't run in naming problems and a lot of other stuff.

But now let's focus on DestroyableResistances. Through abstracting and creating MinMaxValue you already see that we also re-used this class for our resistances and we already benefit of it of abstracting it away. But if you still look at aour DestroyableResistances it still feels a little bit like Duplicated Code. Can we further abstract something away? Yes, sure. Destroyable already was a class that uses our MinMaxValue and added something to it. But now look at our DestroyableResistances. Our Resistances works the same way like Destroyable, the only thing DestroyableResistances does is to add another MinMaxValue to it, and change the subtracting. So, we do we not embed Destroyable in DestroyableResistances? Now let's name it DestroyableResistances2.

package DestroyableResistances2;
use Moose;
use Destroyable;
use MinMaxValue;

# We want:
# Destroyable->new(max => 100, value => 100, resistances => 0.5)
around BUILDARGS => sub {
    my $orig  = shift;
    my $class = shift;
    my %args  = @_;

    # Creating _destroyable
    my $max   = delete $args{max};
    my $value = delete $args{value};
    $args{_destroyable} = Destroyable->new(max => $max, value => $value);

    # Creating resistances
    $args{resistances} = MinMaxValue->new(1, $args{resistances}, 0);

    $class->$orig(%args);
};

has '_destroyable' => (
    is       => 'ro',
    isa      => 'Destroyable',
    required => 1,
    handles  => [qw(value is_alive add)],
);

has 'resistances' => (
    is       => 'ro',
    isa      => 'MinMaxValue',
    required => 1,
);

sub subtract {
    my ( $self, $amount ) = @_;
    $amount = $amount * $self->resistances->get;
    $self->_destroyable->subtract($amount);
    return;
}

At first, in BUILDARGS we change the constructor in a way that it accepts the same signature like DestroyableResistances. It will build the needed Destroyable class for us. If you didn't pay attention i already did some similar changes in Destroyable or the MinMaxValue class.

But now our class is really short. We just needed to add a resistances propertie. We suplied our subtract method that should be changed. The rest of the needed methods will just be delegates to our Destroyable object.

Now lets quickly check if both implementations really do the same work.

my $hqr1 = Building->new(
    name     => 'Headquarter1',
    position => Vector2->new(x => 10, y => 10),
    life     => DestroyableResistances->new(
        max         => 100,
        value       => 100,
        resistances => 0.5,
    ),
);

my $hqr2 = Building->new(
    name     => 'Headquarter2',
    position => Vector2->new(x => 10, y => 10),
    life     => DestroyableResistances2->new(
        max         => 100,
        value       => 100,
        resistances => 0.5,
    ),
);

print $hqr1->info;
print $hqr2->info;

$hqr1->life->subtract(50);
$hqr2->life->subtract(50);

if ( $hqr1->life->value->get == $hqr2->life->value->get ) {
    print "The same\n";
}
else {
    print "Not the same\n";
}

print $hqr1->info;
print $hqr2->info;

We will get the following output

[Building: Name=Headquarter1 Position=10/10 Life=100/100 Alive=true]
[Building: Name=Headquarter2 Position=10/10 Life=100/100 Alive=true]
The same
[Building: Name=Headquarter1 Position=10/10 Life=75/100 Alive=true]
[Building: Name=Headquarter2 Position=10/10 Life=75/100 Alive=true]

Shortly, yes we are done.

Conclusion

What you have seen so far is nothing new. The technique itself is called "Composition". Probably you sometimes heard the statement "Composition over Inheritance". In my opinion, Composition is the most powerful/important way how you can/should program in a object oriented language. It is much more powerful then inheritance. It is a shame that so many books, tutorials and so on just focus on inheritance, sometimes you read a side-node in books that there exists Composition (and Aggregation) but then you get hundreds of sides just explaining how inheritance work.

But okay, historically there exists a small reason for it. The whole last part what i often called "Multiple Implementations" is only really possible with Interfaces. Well not directly, you also can use multiple inheritance to simulate interfaces, like you also can simulate roles with multiple inheritance. So "Interfaces" was one attempt to force users in languages like Java or C# more to the pattern what i described here. More to Composition. The problem was that still everyone just talked about inheritance, because these languages still supported single-inheritance. The end of the story is that most people don't knew what's the point of an interfaces because an interface itself seems to do absolutely nothing at all.

But in fact, interfaces are probably one of the most important "new" features in object oriented programming. Well not directly in Perl, because Perl has no type system, and no methods signature or return types and so on. And variables can hold any type, so in Perl you don't really need them. But the important part here is the concept that interfaces tried to introduce. Roles also tries to introduce a concept to achieve better code re-usability. In fact there are a lot better than inheritance, and if you never used them, you should use them. But roles are still not as useful as composition + interfaces. In reality roles are more a rip-off of interfaces. Roles try to make interfaces usable, without understanding what interfaces really are.

At the moment i highly recommend to read more about Composition and interfaces and try to understand them. If you already have used roles, try to convert them to classes. If you see Code Duplication like explained before, try to abstract it away in another re-usable class. Often you get a much higher re-usablity you didn't even expect. For example, did you ever expected to use two instances of Destroyable in a single class? In Part II you could inject the Destroyable role into a class, but with a class you just can have easily two properties of Destroyable. And if you forgot, i just added a "shield" to our Building. How did you have done it with a role? Probably creating a "Shield" role, even if it would just do the exact same like Destroyable?

Roles also can't "embed" other roles. But with a class that's not a problem. A MinMaxValue class used in a Destroyable class. A Destroyable class embedded in a DestroyableResistances class and so on. With composition you really start to create classes with a very high abstraction. And often because you separate a lot of code, your code really gets easy/small. You nearly ever see big classes. And you often implement new features really fast because you already have a lot of reusable classes.

If you now have questions then feel free to ask, at the moment i'm not planning a fourth part. But if i'm doing a fourth Part it will just be some more information about Interfaces and explaining why they are so important, and probably answering some question if they take a little bit longer to explain.

Leave a comment

About Sid Burn

user-pic I blog about Perl.