Wednesday, August 12, 2009

ADVANCED OBJECT-ORIENTED PROGRAMMING CONCEPTS

The previous two chapters introduced a number of new object-oriented program-
ming (OOP) concepts. In the interest of clarity, some topics were discussed in depth and
others glossed over. While the content of those chapters is still fresh in your mind, let’s return to some of the topics that were only touched upon briefly, namely abstract classes, the use of the static keyword, and the implications of type hinting.

Abstract Classes

In Chapter 10 we saw that if a derived class does not implement all the methods of an interface, then it must be declared abstract. Let’s push this concept to the extreme and see what a completely abstract class might look like. Listing 11-1 shows the definition of such a class.

abstract class Bird{ protected $plumage; protected $migratory;
abstract public function __construct();
abstract public function fly(); abstract public function sing(); abstract public function eat();
abstract public function setPlumage($plumage);
abstract public function getPlumage();
abstract public function setMigratory($migratory);
abstract public function getMigratory();
}

Listing 11-1: The definition of the abstract class Bird

As we saw in Chapter 10, any class that contains abstract methods must include the keyword abstract in its class declaration. That class may have any number of data members and any number of methods with or without an implementation. However, if a method lacks an implementation it must also be declared abstract.
The class in Listing 11-1 has data members declared as protected, making
them available to derived classes. This class could be termed a pure abstract class because all of its methods are abstract. Note that all the methods of this class are declared public. Let’s see why that is so.

Private Methods Can’t Be Abstract

Methods identified as abstract cannot be private; they must be either public or protected. The reason is that an abstract private method is a contradic- tion in terms. Because an abstract class has undefined methods it cannot be instantiated (it only exists to be the parent of a derived class). A class with abstract private methods could never be implemented because private meth- ods cannot be inherited. The same reasoning would apply to a final abstract method.

NOTE Recall that a final method cannot be changed in a derived class. An abstract method cannot be final because it must be overridden—i.e., changed.

How does a pure abstract class, with no defined methods, differ from an interface? An interface may not have data members or a constructor. (This may change in future versions of PHP. There is some discussion of allowing interfaces to have constructors.) In order to turn the Bird class, shown in Listing 11-1, into an interface you would have to replace the keywords abstract class with interface and remove $plumage, $migratory, and the constructor. Although interface methods are effectively abstract, you still need to remove the abstract descriptor for each method.

Interface or Pure Abstract Class?

You now know the syntactic differences between interfaces and pure abstract classes, but when should you use one rather than the other? In general, it’s probably better to use an interface than a pure abstract class because of the

flexibility of interfaces. PHP doesn’t allow multiple inheritance for classes; a child class may have only one parent class. However, you can implement any number of interfaces.
It makes more sense to use abstract classes when there is a mix of concrete and abstract methods. You can provide an implementation where identical, derived class behavior is expected, and you can provide an abstract method where behavior will differ. You could, of course, ignore methods for which you expect the behavior of derived classes to diverge, but by declaring a method abstract you ensure that it will be implemented in any derived class. You’ll see how this can be used to your advantage in the following discussion of polymorphism.

Polymorphism

In Chapter 10 you created a MySQLException class by inheriting from Exception. Type hinting allowed you to easily distinguish different kinds of exceptions and made it possible to have more than one catch block. However, when
using type hinting, you also had to order the catch blocks carefully to make sure that the child preceded the parent. Specifically, MySQLException had to precede Exception because a catch block that catches the Exception class will also catch any derived class. Because it is derived from Exception, MySQLException can be caught by an Exception catch block. A parent class can stand in for its children, but a child cannot stand in for its parent. (This may look like a draw- back, but you’ll soon see how it can be used to advantage.)

Controlling How Functions Are Used

Type hinting can give a programmer more control over the way that a func- tion is used. Suppose you derive a Canary class and a Lark class from the Bird class shown in Listing 11-1. You could pass either a canary or a lark to the function in Listing 11-2.

function doSomething(Bird $b){
//do something
$b->sing();
//do something else
}

Listing 11-2: A function that uses type hinting to specify a Bird object

Even though the Bird class is an abstract class that cannot be instantiated, you can use it to type hint the argument to this function in exactly the same way that catch blocks are type hinted.
In Listing 11-2, type hinting prohibits passing anything but a bird to the function—passing any other object or a primitive will result in an error. In this way, a programmer can restrict the way that a function is used. With properly ordered catch blocks you used type hinting to catch specific kinds of exceptions. The doSomething function does the converse; it catches any kind of Bird. The ability to pass any kind of Bird to this function without knowing the

specific kind beforehand, with the expectation that it will behave as it is supposed to behave, is known as polymorphism. The parent takes on the characteristics of the child.
As you are aware, PHP is a weakly-typed language. In the strictest sense, polymorphism requires a strongly-typed language such as Java or C++. In these languages, whenever a variable is declared or used as a function parameter, it is declared as a specific data type. In PHP, type hinting a parameter doesn’t define the data type but merely filters for acceptable types. In terms of the code in Listing 11-2, Bird doesn’t define the type of $b; it simply blocks out all other types. If this is the case, then $b is a variable like any other PHP variable, of no specific type. It is a variant that becomes a type through assignment. You don’t in fact have a Bird class with the capability of performing the methods of whatever child class is passed. You have only the child class itself. Hence it is disputable whether PHP in fact supports
polymorphism.
Regardless of whether PHP is truly polymorphic, the combination of type hinting and abstract methods is a powerful tool. The former guaran- tees a certain kind of object, and the latter guarantees the implementation of particular methods. For these reasons you can be sure that any object passed to the doSomething function will implement the sing method. The
declaration of an abstract sing method ensures that you can’t have a bird that doesn’t sing and the type hint ensures that only a bird may be passed to this function.

NOTE Type hinting is optional in all situations except catch blocks. A variable’s data type in a catch must be specified, and it must be an Exception or a class derived from Exception. Type hinting applies to objects only (although as of PHP 5.1, arrays can also be type hinted). Type-hinted code is also self-documenting because it makes the programmer’s intentions explicit. (We’ll discuss this topic in greater detail in Chapter 14.)

Static Classes

In Chapter 9, you used a static data member to allow only one instance of the MySQL database class. Whenever an attempt was made to create an instance of this class, you were able to test the value of the $instances variable to ensure that no other instances existed. This test works because
a variable declared as static is available to all instances of a class (or in this case, would-be instances).

Static Math Classes

The ability to create classes that are entirely static allows us to encapsulate a set of related unchanging data members and methods. Mathematics is an ideal candidate for this kind of class because constants, such as pi and the way of calculating the absolute value of a number, do not change. Listing 11-3 shows what a piece of the static Math class might look like.

final class Math{
const PI = M_PI;
static public function abs($num){
return abs($num);
}
static public function sqrt($num){
return sqrt($num);
}
}
echo Math::PI;
echo '<br />';
echo Math::abs(-4.15);
echo '<br />';
echo Math::sqrt(9);

Listing 11-3: A portion of the code for a static Math class

So far you have only seen the keyword final applied to methods. When used as a class modifier, it defines a class that cannot be the parent of any other class. A well-defined Math class should have no need of subclasses—it should not need to be extended and none of its methods overridden. The keyword final ensures this.
The Math class contains mathematical constants and performs mathe- matical functions. The constant data member PI can be displayed by using the class name and the scope resolution operator. Static methods are called in a similar fashion. The use of the class name and the scope resolution operator rather than the arrow operator indicates that the properties or methods belong to the class as a whole and not to any specific instance. Therefore, it is illegal to reference the pseudo-variable $this from within a static method because $this refers to the current instance. A static method, by definition, is not tied to any specific instance.

NOTE Unlike some other OO languages, PHP does not allow the keyword static to be applied to a class as a whole. For example, attempting to declare final static class Math will result in an error. Therefore, when I speak of a static class in PHP, I am using the term loosely. I really mean a class that has only static methods.

Instances of Static Classes

Because the keyword static cannot be applied to a class, you can create an instance of a class even if that class has only static data members. For exam- ple, you can create an instance of the Math class from Listing 11-3:

$m = new Math();
echo $m->sqrt(9);

Although this coding style is not recommended, an instance of the Math class will be created, and no error or notice will occur when you call the static method sqrt against this instance.

NOTE This will offend OO purists, because static methods belong to the class as a whole and should not be called against instances. However, changes are afoot for PHP when it comes to calling dynamic methods statically—“We will make calling a dynamic function with the static call syntax E_FATAL.” 1

Preventing Instantiation of a Static Class

It is quite easy to prevent your Math class from being instantiated. Simply add a constructor like the following:

public function __construct(){
throw new Exception("Static class - instances not allowed.");
}

This constructor will throw an exception if there is an attempt to create an instance of the Math class.
We could go on to create a complete Math class by adding all the appro- priate methods, mostly wrapper methods for existing PHP functions, as we did for the absolute value function and the square root function shown in Listing 11-3. All in all, we can create a reasonable facsimile of a static class.
It makes sense to create a static Math class for an entirely OO language such as Java (after all, there’s no procedural way, in this case, of calling mathematical functions), but the need to create static classes in a hybrid language such as PHP is questionable. In this case the static methods of a static Math class provide the equivalent of global functions that already exist in the PHP function library.
Although the value of static classes may be moot, you’ll see shortly that static methods can be very useful.

Design Patterns

Originally, design patterns were templates used for solving common archi- tectural problems, but they have also been applied to computer programming. Patterns are somewhat akin to abstract classes or interfaces, but are even less specific, providing only a general description of a solution.

The Singleton Pattern

One well-known and well-documented design pattern is the singleton pattern,
a pattern that ideally suits the database class you created in Chapters 9 and 10. As the name implies, this pattern is used where only one instance of a class is wanted.
Your implementation of the MySQLConnect class uses a static variable and throws an exception if there is an attempt to construct more than one instance of the class. A more conventional implementation of the singleton pattern might use a private constructor and a static method to return a class

1 PHP Developers Meeting, minutes (Paris, November 11–12, 2005), available at www.php.net/
~derick/meeting-notes.html. (Accessed April 4, 2006.)

instance. Let’s revise the MySQLConnect class to highlight the usefulness of static methods. (I’ll outline only the major changes here; download the code if you want to see them all.)
To begin with, the static data member designed to keep track of the number of instances becomes a static data member for holding a reference to the class instance.

private static $instance = NULL;

The constructor still creates a connection, but the access modifier is changed from public to private and the test for existing instances is removed.

private function __construct($hostname, $username, $password){
if(!$this->connection = mysql_connect($hostname, $username, $password)){
throw new MySQLException(mysql_error(), mysql_errno());
}
}

Because the constructor has been declared as private, you can only invoke it from within the class. This may seem like an impossibility (how do you get inside a class that you can’t create?), but a static method provides the means, as shown in Listing 11-4.

static public function getInstance($hostname, $username, $password){
//instance must be static in order to be referenced here if(self ::$instance == NULL ){
self::$instance = new MySQLConnect ($hostname, $username, $password);
return self::$instance;
}else{
$msg = "Close the existing instance of the ". "MySQLConnect class.";
throw new MySQLException($msg, self::ONLY_ONE_INSTANCE_ALLOWED);
}
}

Listing 11-4: Static method for returning an instance

In order to reference the instance handle inside a static method, the handle itself must be static. If no instance exists, the constructor is called and the returned object is copied into the static class variable $instance. The getInstance method then returns a reference to this static data member.
Now, instead of directly creating an instance of the MySQLConnect class by calling the constructor, you invoke the static getInstance method to perform that task for you.

$instance = MySQLConnect::getInstance('localhost', 'user', 'password');

It was noted earlier that static methods can only reference static data members. Conversely, static methods are prohibited from referencing regular data members. This makes sense when you remember that regular data mem- bers belong to and are created when objects are instantiated. By definition a static method does not require an object, so those non-static data members don’t exist. Likewise, as you saw earlier, a static method cannot use the pseudo- variable $this, since $this refers to the current instance.

NOTE A singleton class should also disallow clones. You’ll see how this is done in Chapter 13.

Which Implementation?

This revised MySQLConnect class has exactly the same functionality as the ori- ginal. Apart from the way an instance is created, there is no other change to the interface of the MySQLConnect class. However, having a copy of the lone instance stored in a static class variable allows you to return that instance instead of throwing an exception, should an attempt be made to create a second instance. This is exactly what some implementations of a singleton database class do, but it is not always the desired behavior. What if the user wants to connect to a different server? For this reason, in the section “Making Other Connections” on page 68, we chose to force the user to close the current connection before creating a new one.
The coding style of the original implementation may be more direct and more readily understood, but having a reference to the class instance could prove useful in some circumstances. If the getInstance method receives a request to connect to the same host with the same username, why not return the current instance rather than throwing an exception?
Which version is preferable? It’s up to you to decide.

Where to Go from Here

The keywords abstract and static and the ability to type hint add powerful capabilities that didn’t exist prior to PHP 5. Creating abstract methods enforces specific kinds of behavior, and static methods and data members make the implementation of a singleton pattern both easy and effective. Type hinting makes the developer’s intentions clear and programmatically enforces them.
These capabilities are not just syntactic icing on top of a procedural language; they are a robust implementation of a fully OO language. PHP may be unable to create a true static class, and whether it is truly polymorphic is debatable, but the issue for PHP is always functionality rather than language purity. There is no doubt that it does not suffer in this respect.
To this point we have created our own classes from scratch or inherited from existing ones defined in the Standard PHP Library (Iterator and Exception). PHP 5 includes many other classes besides those defined in the SPL. In the next chapter we’ll use two of them, SimpleXMLElement and SOAPClient.

0 comments: