Re: PHP 5 abstract method and class type hints of extending classes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Jeremy Weir wrote:
The quesion is: how would one make an abstract method that can be compatible with all extending classes that define the method using different class type hints?

The php block below is how I thought it should work, but will give this error at parse time:
<error>Fatal error: Declaration of DisplayObjectOne::display() must be compatible with that of DisplayBase::display()</error>
because the hint in the concrete method is different from that in the abstract method.


<?php
// base display class
abstract class DisplayBase {
    abstract public function display(ObjectBase $object);
}

// display ObjectOne
class DisplayObjectOne extends DisplayBase {
    public function display(ObjectOne $object){
        print_r($object);
    }
}

// display ObjectTwo
class DisplayObjectTwo extends DisplayBase {
    public function display(ObjectTwo $object){
        var_export($object);
    }
}

If you use type-hinting, this is a more strict contract. You're telling PHP that "this method must receive an object that is of this class or any subclass"


in DisplayObjectTwo, you're saying "this method CANNOT accept a DisplayBase or any child object unless it extends ObjectTwo"

In other words, imagine if what you want to do was allowed. Someone comes along who writes a method that uses an instanceof test like:

$b = new ObjectOne;
$a = new DisplayObjectTwo;
if ($a instanceof DisplayBase) {
    $a->display($b);
}

to display another object, you get a serious problem, because the code will fail with all DisplayObject* that aren't DisplayObjectOne, but the instanceof test will succeed!

In other words, your display() function is in fact not a generic function, but is specific to each class, so the use of a DisplayBase class makes absolutely no sense at all. Better is to implement a method inside ObjectBase called getDefaultDisplayObject() which will instantiate the display object that is needed. Custom classes can then override this method to return the custom display object.

Even though it is better, this still isn't great design. What you probably want is this:

abstract class DisplayBase
{
public static function factory(ObjectBase $o)
{
$class = 'Display' . get_class($o);
if (!class_exists($class)) {
throw new Exception('No display object for "' . get_class($o) . '"');
}
$disp = new $class($o);


    }

    abstract public function __construct($o);
    abstract public function display($options); // might want this
}

class DisplayObjectOne extends DisplayBase
{
    private $object;
    public function __construct($o)
    {
        if (!$o instanceof ObjectOne) {
            throw new Exception('parameter must be an ObjectOne');
        }
        $this->object = $o;
    }

    public function display($options)
    {
        print_r($this->object);
    }
}

class DisplayObjectTwo extends DisplayBase
{
    private $object;
    public function __construct($o)
    {
        if (!$o instanceof ObjectTwo) {
            throw new Exception('parameter must be an ObjectTwo');
        }
        $this->object = $o;
    }

    public function display($options)
    {
        print_r($this->object);
    }
}

Now, your hypothetical user can do

try {
    if ($d instanceof DisplayBase) {
        $d->display($options);
    }
} catch (Exception $e) {
    // handle problems
}

and rest assured that the code will work under all circumstances. Hope this helps :)

Regards,
Greg

--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php


[Index of Archives]     [PHP Home]     [Apache Users]     [PHP on Windows]     [Kernel Newbies]     [PHP Install]     [PHP Classes]     [Pear]     [Postgresql]     [Postgresql PHP]     [PHP on Windows]     [PHP Database Programming]     [PHP SOAP]

  Powered by Linux