While thinking about a programming language deficiency, I rediscovered polymorphism. Overloading a function allows a function call to behave differently when passed variables of different type. I was trying to devise a method of simulating function overloading, because PHP does not support it. I considered implementing a function with an if-else statement ladder that tests the type of the actual argument and executes statements that correspond to the argument’s type. This technique may ultimately result in a monolithic function or a function implementation that is too knowledgeable of multiple class hierarchies. Rethinking a problem that I was hoping to solve with function overloading allowed me to accept the lack of this language feature and think of other techniques.
There are two problems that arise from the if-else statement ladder approach. The implementation of such a function would require the function to have knowledge of every data type to be used with it. This problem can be extended to knowing class hierarchies, if subclassing is involved. This means that an introduction of a new data type to be processed by the function would require the function to be modified, which creates the possibility that the modification will break other existing code that relies on the function. The second problem is having the function’s maintainer think about how the function should operate on the different data types that can be passed to it. This responsibility is better placed on the people who maintain the different data types.
One solution that deals with the problems in the if-else statement ladder is polymorphism. Polymorphism allows a set of heterogeneous elements to be treated identically. It is achieved through inheritance. In PHP, interface inheritance and implementation inheritance can be written explicitly through the use of interfaces and class extensions, respectively. Interfaces specify a class interface without providing an implementation. Classes from different class hierarchies can implement an interface, and in this way, it can be seen as different from abstract classes. When a class is defined to implement an interface, the language enforces a rule that the class implements all features of the interface. A method that operates on an interface will accept an object of any class that implements the interface, and it will function correctly.
Here is a toy example of interface inheritance, polymorphism, and PHP type hinting:
<?
interface HasArea
{
public function area();
public function areaUnit();
}
abstract class Shape
{
private $color;
public function __construct( $color )
{
$this->color = $color;
}
public function getColor()
{
return $this->color;
}
}
class Rectangle extends Shape implements HasArea
{
private $w;
private $h;
public function __construct( $color, $w, $h )
{
parent::__construct($color);
$this->w = $w;
$this->h = $h;
}
public function area()
{
return ($this->w * $this->h);
}
public function areaUnit()
{
if( $this->area() > 1 )
return "square meters";
else
return "square meter";
}
}
class Territory implements HasArea
{
private $name;
public function __construct( $name )
{
$this->name = $name;
/* not used in this example... */
}
public function area()
{
return 5;
}
public function areaUnit()
{
return "cities";
}
}
function outputArea( HasArea $ha )
{
echo "The area is: "
. " {$ha->area()} {$ha->areaUnit()}\n";
}
$HAs = array(
new Rectangle("red",2,3),
new Territory( "somename" )
);
foreach( $HAs as $HA )
{
outputArea( $HA );
}
?>
Type hints help the PHP interpreter enforce the restriction that outputArea()
operates only on objects of data types that implement the HasArea
interface. Rectangle
and Territory
are from unrelated class hierarchies. outputArea()
can operate on these classes, since these classes implement the HasArea
interface.
The explored method accomplishes only some of the features offered by function overloading. In the above example, outputArea()
was restricted to one argument. In some programming languages, a function can be overloaded on the number of arguments along with the types of those arguments and the order that those types appear in the argument list. This method, however, was useful in a problem I considered solving with function overloading.