Archive of September 2006

September 3

Simulating Class Clusters in PHP

Class clusters are a nice feature of ultra dynamic languages (like Objective-C). This is one way of loosely simulating that kind of functionality in PHP.

Introduction

The basic idea here is a kind of advanced polymorphism where the object itself decides what type of object it should be and therefore how it should behave. Where this differs from your normal situation of implementing differences through subclassing, is that even when you construct the object, you don't really know what you are dealing with (and of course you don't care).

Disclaimer: This is one way of implementing this kind of thing, not the only way.

This will all be in PHP5. If you need to do this in PHP4, I would suggest looking at object aggregation. Because it is PHP5, we will be using the __call magic method to do all our heavy lifting.

The Code

We have a Circle class. This Circle class is an extremely trivial example and really doesn't benefit from being implemented as a class cluster, but it gets the idea across.

[php] class Circle { private $real_circle; private $radius;

    public function __construct($radius_in_meters)
    {
        if ($radius_in_meters < 50)
        {
            $this->real_circle = new SmallRealCircle($this);
        }
        else
        {
            $this->real_circle = new BigRealCircle($this);
        }
    }

    public function __call($method, $arguments)
    {
        // Really should use is_callable() to see if this method is valid
        // and throw an exception if it isn't.

        return call_user_func_array(array(&$this->real_circle, $method), $arguments);
    }

    public function printRadius()
    {
        print "In: " . get_class($this) . " - getRadius()";
        print "Radius: {$this->radius}";
    }
}

abstract class RealCircle
{
    protected $source; // This will be the object who created us.

    public function __construct(Circle $source)
    {
        $this->source = $source;
    }

    abstract public function howBigAreYou(); 
}

class SmallRealCircle extends RealCircle
{
    public function howBigAreYou()
    {
        print "In: " . get_class($this) . " - howBigAreYou()";
        return 'I am not that big.';
    }
}

class BigRealCircle extends RealCircle
{
    public function howBigAreYou()
    {
        print "In: " . get_class($this) . " - howBigAreYou()";
        return 'I am pretty big.';
    }
}

[/php]

Like I said, not the most inspiring concept to ever be expressed in Code. Some usage....

[php] $circle_1 = new Circle(20); // Should be small circle. $circle_2 = new Circle(100); // Should be a big circle.

$circle_1->printRadius();
// Output: 'In: Circle - printRadius()'
// Output: 'Radius: 20'

$circle_2->printRadius();
// Output: 'In: Circle - printRadius()'
// Output: 'Radius: 100'

[/php]

Still nothing interesting. What it does illustrate though is that the __call magic function doesn't stop ordinary methods from being called. Lets see some cluster like behaviour...

[php]
$circle_1 = new Circle(20); // Should be small circle. $circle_2 = new Circle(100); // Should be a big circle.

$circle_1->howBigAreYou();
// Output: 'In: SmallRealCircle - howBigAreYou()'
// Output: 'I am not that big.'

$circle_2->howBigAreYou();
// Output: 'In: BigRealCircle - howBigAreYou()'
// Output: 'I am pretty big.'

[/php]

So thats the basis of it. What you should be able to get out of it is that we essentially have one class that can manage its behaviour internally dynamically in a way that doesn't involve constant if-ing.

A Use Case

We didn't gain a lot from the above example, but there are cases where this technique is useful. In an application I am currently working on, we use one database table to model all people within the enterprise. This makes somethings easier but makes implementing certain behaviours difficult to do cleanly. Without using this clustering technique, I would have had to constantly code fork depending on the type of person that I am dealing with. In fact, this is how I did it at the start. This soon became unmanageable.

How I solved this was to create a hidden class similar in concept to the RealCircle class above and have a different subclass of that class for each person type that required unique behaviour. I called these classes representors. This arrangement worked extremely well for my particular situation. It offered a clean and easily updateable code base.

Why not subclass?

One reason to use this approach over subclassing is that you may not know what you are dealing with at construction time. In my case I had to create the person object before I had any idea what kind of person it would be.

Another reason is that you may want the object to dynamically change its behaviour at run time. Consider the circle example. If you could grow a circle by say calling $circle_obj->setRadius(100);, you could in the setter, set the $this->real_circle instance variable to an instance of BigRealCircle where before it had been SmallRealCircle

Conclusion

While you can't implement true class clusters in PHP, you can simulate the behaviour fairly effectively and it can be a useful tool in your coding utility belt.

05:21 PM | 0 Comments

New look...

I think I have finally finished messing with the way this site looks. I pinched the template from the wordpress theme site and made a few subtle modifications. All in all, I think it looks pretty cool.

04:03 AM | 0 Comments
September 2

What the BLEEP. The best film you will ever see.

WhatTheBleep

If you don't know about it. Go and look at the website. Then, go and buy it.

03:37 AM | 1 Comment