14.3 Abstract Classes and Interfaces
When developing class hierarchies, it's often useful
to insert extra classes to help logically organize code or to provide
a base for polymorphic behavior.
14.3.1 The abstract keyword
Abstract classes are available in PHP5.In Example 14-1, we defined a set of related classes
to describe shapes that includes a base class
Shape and its two member functions
color( ) and sides( ).
Let's suppose we want to add a function
area( ) that calculates the area of a shape. If
we want to allow the area to be calculated for all
Shape objects, then the function must be made
available in the Shape class.However, unlike the color( ) and
sides( ) functions, the area(
) function can only be implemented in the descendant
classes because area calculations vary from shape to shape. The area
of a circle is calculated using the radius, while a rectangle uses
height and width, and so on. Using the abstract
keyword, PHP5 allows you to define member functions without having to
define an implementation. However, a class that contains an abstract
function can't be used to create objects, and must
also be defined as abstract. Example 14-2 shows how
the Shapes class is redefined to include the
abstract area( ) function.
Example 14-2. An abstract base class
<?phpExample 14-2 includes three
abstract class Shape
{
var $color;
var $sides;
function color( )
{
return $this->color;
}
function sides( )
{
return $this->sides;
}
abstract function area( );
function _ _construct($color, $sides)
{
$this->color = $color;
$this->sides = $sides;
}
}
class Circle extends Shape
{
var $radius;
function area( )
{
return 2 * pi( ) * $this->radius;
}
function _ _construct($color, $radius)
{
$this->radius = $radius;
parent::_ _construct($color, 1);
}
}
class Rectangle extends Shape
{
var $width;
var $height;
function area( )
{
return $this->width * $this->height;
}
function _ _construct($color, $width, $height)
{
$this->width = $width;
$this->height = $height;
parent::_ _construct($color, 4);
}
}
class Triangle extends Shape
{
// The length of each side
var $a;
var $b;
var $c;
function area( )
{
// Area using Heron's formula
$s = ($this->a + $this->b + $this->c)/2;
$area = sqrt(
$s * ($s - $this->a) * ($s - $this->b) * ($s - $this->c)
);
return $area;
}
function _ _construct($color, $a, $b, $c)
{
$this->a = $a;
$this->b = $b;
$this->c = $c;
parent::_ _construct($color, 3);
}
}
?>
classesCircle,
Rectangle, and
Trianglethat extend the abstract
Shape class and provide shape specific
implementations of the abstract function area(
). (We have also removed the Polygon
class and the angles( ) function to help
simplify the example.)The following fragment shows which classes from Example 14-2 can be used:
require 'example.14-2.php';Before the fatal error caused by the attempt to create an object from
$t = new Triangle("yellow", 2, 3, 4);
$c = new Circle("blue", 5);
$r = new Rectangle("green", 2, 4);
print "Area of our triangle = {$t->area( )} sq units\n";
print "Area of our circle = {$c->area( )} sq units\n";
print "Area of our rectangle = {$r->area( )} sq units\n";
// The following line causes a fatal error because Shape is abstract
$s = new Shape("green", 5);
the abstract class Shape, the previous example
prints:
Area of our triangle = 2.9047375096556 sq unitsThere are a few rules to observe when defining abstract classes and
Area of our circle = 31.415926535898 sq units
Area of our rectangle = 8 sq units
functions:An abstract member function can't contain a body
definition.A class must be declared as abstract if it contains any abstract
functions.You can declare protected and public functions as abstract, however,
it is an error to declare a private function as abstract.Any descendant classes that don't implement an
abstract function defined in a base class must also be declared as
abstract.
14.3.2 Interfaces
Interfaces
are available in PHP5.Interfaces provide another way of describing functionality in a class
hierarchy. An interface defines member functions
without providing any implementation, and class definitions
implement those functions. Interfaces are
defined using the interface keyword, and class
definitions use interfaces with the implements
keyword. An example is shown in Example 14-3:
Example 14-3. Using Interfaces
<?phpThe Audible interface defined in Example 14-3 acts like an abstract base class for the
interface Audible
{
function sound( );
}
class Animal implements Audible
{
var $name;
var $says;
var $legs;
function sound( )
{
return $this->says;
}
function name( )
{
return $this->name;
}
function numberOfLegs( )
{
$this->legs;
}
function _ _construct($name, $says, $legs)
{
$this->name = $name;
$this->says = $says;
$this->legs = $legs;
}
}
?>
Animal class: you can't create
Audible objects directly, but an
Animal object can be used as if it were an
Audible object. Classes that specify an
interface with the implements keyword must
implement all the functions from the interface.Interfaces are useful for implementing polymorphic behavior and are
commonly used to relate classes that otherwise would not be related.
For example, a Trumpet class
wouldn't necessarily be related to the
Animal class, however we may want to generically
process objects from both classes as Audible:
// access to the Audible interfaceTo further illustrate the point, we can implement the
require "example.14-3.php";
class Trumpet implements Audible
{
function sound( )
{
return "Toot";
}
// Other functions that support a Trumpet
// ...
function orchestraSection( )
{
return "Brass";
}
}
sound( ) function for the
Triangle class we defined earlier:
// access to the Shape classNow Animal, Trumpet, and
require "example.14-1.php";
// access to the Audible interface
require "example.14-3.php";
class Triangle extends Shape implements Audible
{
// The length of each side
var $a;
var $b;
var $c;
function sound( )
{
return "Ding";
}
function area( )
{
// Area using Heron's formula
$s = ($this->a + $this->b + $this->c)/2;
$area = sqrt(
$s * ($s - $this->a) * ($s - $this->b) * ($s - $this->c)
);
return $area;
}
function _ _construct($color, $a, $b, $c)
{
$this->a = $a;
$this->b = $b;
$this->c = $c;
parent::_ _construct($color, 3);
}
}
Triangle objects can all be treated as
Audible objects:
// Create an empty array to hold some objectsInterfaces also provide a mechanism for exposing subsets of a
$things = array( );
// Add some objects
// An Animal is constructed with a name, sound, and leg count
$things[] = new Animal("Cow", "Moo", 4);
// A Trumpet is constructed without any parameters
$things[] = new Trumpet;
// A Triangle is constructed with a color, and the
// length of the three sides
$things[] = new Triangle("Silver", 3, 3, 3);
// A Circle is constructed with a color and a radius
$things[] = new Circle("Blue", 5);
// Check out the sound
foreach ($things as $t)
{
if ($t instanceof Audible)
print $t->sound( );
}
class's functionality. When a
Triangle is being used as an
Audible object, we only care about the
sound( ) member function. The code in the above
example would still work even if we change the names of the other
Triangle member functions: because
Triangle implements
Audible, we can be sure that a
Triangle can be used as an
Audible object.Functions can intentionally limit how an object is used using class
type hints. Consider a function that works with
Audible objects:
// return a formatted string to represent soundThe function showSound( ) can only use
function showSound(Audible $obj)
{
return "!! {$obj->sound( )} !!";
}
$obj parameter if it's an
Audible object because the parameter is declared
as Audible. An interface acts like a contract:
classes that implement an interface are guaranteed to support a
defined set of functions, while users of an interface warrant only to
use those functions.PHP allows multiple interfaces to be implemented in a class
definition by including a comma-separated list of interface names
after the implements keyword. This allows a class
definition to use interfaces to formally describe how it can be used
in different circumstances. We have shown how several different
classes can implement the Audible interface; we
can just as easily add other interfaces such as a
WebDisplayable interfaces that defines functions
for rendering the object as HTML. Of course, each class that
implements an interface must support the functions it
describes.