"Don’t turn on the dark light," my five-year-old son reproaches me when I turn out
the lights in his room. The mental model revealed by this statement is an interesting
and striking simplification of the physics involved. Instead of being opposites, he sees
turning the light off and on as variations of the same process. There’s a bright and a
dark light, and you can turn either one on. In object-oriented lingo, both the bright
light class and the dark light class have a turnOn() operation or method. Like the
dress() method of the Boy and Girl classes in chapter 4, this is polymorphism, a
case of different actions being represented as basically the same.
In this section, we’ll see how Null Objects work, and then discover how to use
them with the Strategy pattern.
7.4.1 Mixing dark and bright lights
A Null Object is the dark light of our object-oriented world. It looks like an ordinary
object, but doesn’t do anything real. Its only task is to look like an ordinary object so
you don’t have to write an if statement to distinguish between an object and a nonobject.
Consider the following:
If the UserFinder returns a non-object such as NULL or FALSE, PHP will scold us:
Fatal error: Call to a member function disable() on a non-object
in user.php on line 2
To avoid this, we need to add a conditional statement:
$user = UserFinder::findWithName('Zaphod Beeblebrox');
if (is_object($user))
$user->disable();
But if $user is a Null Object that has disable() method, there is no need for a
conditional test. So if the UserFinder returns a Null Object instead of a non-object,
the error won’t happen.
A simple NullUser class could be implemented like this:
class NullUser implements User {
public function disable() { }
public function isNull() { return TRUE; }
}
The class is oversimplified, since it implements only one method that might be of real
use in the corresponding user object: disable(). The idea is that the real user class,
or classes, would also implement the interface called User. So, in practice, there
would be many more methods.
7.4.2 Null Strategy objects
A slightly more advanced example might be a Null Strategy object. You have one
object that’s configured with another object that decides much of its behavior, but in
some cases the object does not need that behavior at all.
An alternative to using the Logging decorator shown earlier might be to build logging
into the connection class itself (assuming we have control over it). The connection
class would then contain a logger object to do the logging. The pertinent parts
of such a connection class might look something like this:
class Connection {
public function __construct($url,$logger) {
$this->url = $url;
$this->logger = $logger;
// More initialization
// ...
}
public function query($sql) {
$this->logger->log('Query: '.$sql);
// Run the query
// ...
}
}
Since this class accepts a logger object as input when it’s created, we can configure it
with any logger object we please. And if we want to disable logging, we can pass it a
null logger object:
$connection = new Connection(
mysql://user:password@localhost/webdatabase,
new NullLogger
);
A NullLogger class could be as simple as this:
class NullLogger implements Logger{
public function log {}
}
Figure 7.6 shows the relationships between these classes. The interface may be represented
formally using the interface keyword or an abstract class, or it may be
implicit using duck typing as described in chapter 4.
The PEAR Log package has a Null logger class called Logger_null that is somewhat
more sophisticated than the one we just saw.
Although a Null Object might do something such as return another Null Object,
frequently it’s about doing nothing at all. The next pattern, Iterator, is about doing
something several times.
7.5 ITERATOR
An iterator is an object whose job it is to iterate, usually returning elements one by
one from some source. Iterators are popular. One reason may be that it’s easy to
understand what they do, in a certain limited way, that is. It is relatively easy to see
how they work and how to implement one. But it’s less obvious how and when
they’re useful compared to the alternatives, such as stuffing data into a plain PHP
array and using a foreach loop to iterate.
In this section, we will see how iterators work, look at some good and bad reasons
to use them, contrast them with plain arrays, and see how we can improve iterators
further by using the Standard PHP Library (SPL).
7.5.1 How iterators work
An iterator is an object that allows you to get and process one element at a time. A
while loop using an SPL (Standard PHP Library) iterator has this form:
while ($iterator->valid()) {
$element = $iterator->current();
// Process $element
$iterator->next();
}
There are various interfaces for iterators, having different methods that do different
things. However, there is some overlap. Above all, to be useful at all, every iterator
needs some way of getting the next element and some way to signal when to stop.
Table 7.1 compares the SPL iterator interface with the standard Java iterator interface
and the interface used in the Gang of Four [Gang of Four] book.
7.5.2 Good reasons to use iterators
Three are three situations in which an iterator is undeniably useful in PHP:
When you use a package or library that returns an iterator
When there is no way to get all the elements of a collection in one call
When you want to process a potentially vast number of elements
In the first case, you have no choice but to use the iterator you’ve been given. Problem
3 will happen, for example, when you return data from a database table. A database
table can easily contain millions of elements and gigabytes of data, so the alternative—reading all of them into an array—may consume far too much memory. (On the other
hand, if you know the table is small, reading it into an array is perfectly feasible.)
Another example would be reading the results from a search engine. In this case,
problems 2 and 3 might both be present: you have no way of getting all the results
from the search engine without asking repeatedly, and if you did have a way of getting
all of them, it would far too much to handle in a simple array.
In addition to the undeniably good reasons to use iterators, there are other reasons
that may be questioned, because there are alternatives to using iterators. The most
important alternative is using plain arrays. In the previous situations, using plain arrays
is not a practical alternative. In other situations, they may be more suitable than iterators.
7.5.3 Iterators versus plain arrays
The general argument in favor of iterators is that they
Encapsulate iteration
Provide a uniform interface to it
Encapsulation means that the code that uses an iterator does not have to know the
details of the process of iteration. The client code can live happily ignoring those
details, whether they involve reading from a database, walking a data structure recursively,
or generating random data.
The uniform interface means that iterators are pluggable. You can replace an iterator
with a different one, and as long as the single elements are the same, the client
code will not know the difference.
Both of these are advantages of using iterators. On the other hand, both advantages
can be had by using plain arrays instead.
Consider the following example. We’ll assume we have a complex data structure
such as a tree structure (this is an example that is sometimes used to explain iterators).
The simpler way of doing it would be to return an array from the data structure
instead of an iterator:
$structure = new VeryComplexDataStructure;
$array = $structure->getArray();
foreach ($array as $element) {
echo $value . "\n";
}
It’s simpler and more readable; furthermore, the code required to return the array will
typically be significantly simpler and leaner than the iterator code, mostly because
there is no need to keep track of position as we walk the data structure, collecting elements
into an array. As the Gang of Four say, "External iterators can be difficult to
implement over recursive aggregate structures like those in the Composite pattern,
because a position in the structure may span many levels of nested aggregates." In
other words, iterating internally in the structure is easier.
In addition, PHP arrays have another significant advantage over iterators: you can
use the large range of powerful array functions available in PHP to sort, filter, search,
and otherwise process the elements of the array.
On the other hand, when we create an array from a data structure, we need to make
a pass through that structure. In other words, we need to iterate through all the elements.
Even though that iteration process is typically simpler than what an iterator
does, it takes time. And the foreach loop is a second round of iteration, which also
takes time. If the iterator is intelligently done, it won’t start iterating through the elements
until you ask it to iterate. Also, when we extract the elements from the data
structure into the array, the array will consume memory (unless the individual elements
are references).
But these considerations are not likely to be important unless the number of elements
is very large. The guideline, as always, is to avoid premature optimization (optimizing
before you know you need to). And when you do need it, work on the things
that contribute most to slow performance.
7.5.4 SPL iterators
The Standard PHP Library (SPL) is built into PHP 5. Its primary benefit—from a
design point of view—is to allow us to use iterators in a foreach loop as if they
were arrays. There are also a number of built-in iterator classes. For example, the
built-in DirectoryIterator class lets us treat a directory as if it were an array of objects
representing files. This code lists the files in the /usr/local/lib/php directory.
$iter = new DirectoryIterator('/usr/local/lib/php');
foreach($iter as $current) {
echo $current->getFileName()."\n";
}
In chapter 19, we will see how to implement a decorator for a Mysqli result set to
make it work as an SPL iterator.
7.5.5 How SPL helps us solve the iterator/array conflict
If you choose to use plain arrays to iterate, you might come across a case in which the
volume of data increases to the point where you need to use an iterator instead. This
might tempt you to use a complex iterator implementation over simple arrays when
this is not really needed. With SPL, you have the choice of using plain arrays in most
cases and changing them to iterators when and if that turns out to be necessary, since
you can make your own iterator that will work with a foreach loop just like the
ready-made iterator classes. In the VeryComplexDataStructure example, we can do
something like this:
$structure = new VeryComplexDataStructure;
$iterator = $structure->getIterator();
foreach($iterator as $element) {
echo $element . "\n";
}
As you can see, the foreach loop is exactly like the foreach loop that iterates over
an array. The array has simply been replaced with an iterator. So if you start off by
returning a plain array from the VeryComplexDataStructure, you can replace it with
an iterator later without changing the foreach loop. There are two things to watch
out for, though: you would need a variable name that’s adequate for both the array
and the iterator, and you have to avoid processing the array with array functions,
since these functions won’t work with the iterator.
The previous example has a hypothetical VeryComplexDataStructure class. The
most common complex data structure in web programming is a tree structure. There
is a pattern for tree structures as well; it’s called Composite.
7.6 COMPOSITE
Composite is one of the more obvious and useful design patterns. A Composite is
typically an object-oriented way of representing a tree structure such as a hierarchical
menu or a threaded discussion forum with replies to replies.
Still, sometimes the usefulness of a composite structure is not so obvious. The
Composite pattern allows us to have any number of levels in a hierarchy. But sometimes
the number of levels is fixed at two or three. Do we still want to make it a Composite,
or do we make it less abstract? The question might be whether the Composite
simplifies the code or makes it more complex. We obviously don’t want a Composite
if a simple array is adequate. On the other hand, with three levels, a Composite is likely
to be much more flexible than an array of arrays and simpler than an alternative objectoriented
structure.
In this section, we’ll work with a hierarchical menu example. First, we’ll see how
the tree structure can be represented as a Composite in UML diagrams. Then we’ll
implement the most essential feature of a Composite structure: the ability to add child
nodes to any node that’s not a leaf. (In this case, that means you can add submenus
or menu options to any menu.) We’ll also implement a so-called fluent interface to
make the Composite easier to use in programming. We’ll round off the implementation
by using recursion to mark the path to a menu option. Finally, we’ll discuss the
fact that the implementation could be more efficient.
7.6.1 Implementing a menu as a Composite
Let’s try an example: a menu for navigation on a web page such
as the example in Figure 7.4. Even if we have only one set of
menu headings, there are still implicitly three levels of menus,
since the structure as a whole is a menu. This makes it a strong
candidate for a Composite structure.
The menu has only what little functionality is needed to illustrate
the Composite. We want the structure itself and the ability
to mark the current menu option and the path to it. If we’ve chosen
Events and then Movies, both Events and Movies will be
shown with a style that distinguishes them from the rest of the
menu, as shown in Figure 7.7.
First, let’s sketch the objects for the first two submenus of this
menu. Figure 7.8 shows how it can be represented. Each menu
has a set of menu or menu option objects stored in instance variables,
or more likely, in one instance variable which is an array
of objects. To represent the fact that some of the menus and
menu options are marked, we have a simple Boolean (TRUE/
FALSE flag). In the HTML code, we will want to represent this as a CSS class, but we’re
keeping the HTML representation out of this for now to keep it simple. Furthermore,
each menu or menu option has a string for the label. And there is a menu object to
represent the menu as a whole. Its label will not be shown on the web page, but it’s
practical when we want to handle the menu.
A class diagram for the Composite class structure to represent menus and menu
options is shown in Figure 7.9 It is quite a bit more abstract, but should be easier to
grasp based on the previous illustration. Figure 7.8 is a snapshot of a particular set of
object instances at a particular time; Figure 7.9 represents the class structure and the
operations needed to generate the objects.
There are three different bits of functionality in this design:
Each menu and each menu option has a label, the text that is displayed on the
web page.
The add() method of the Menu class is the one method that is absolutely
required for generating a Composite tree structure.
The rest of the methods and attributes are necessary to make it possible to mark
the current menu and menu option.
The two methods hasMenuOptionWithId() and markPathToMenuOption()
are abstract in the MenuComponent class. This implies that they must exist
in the Menu and MenuOption classes, even though they are not shown in these
classes in the diagram.
The leftmost connection from Menu to MenuComponent implies the fact—
which is clear in Figure 7.8 as well—that a Menu object can have any number of menu
components (Menu or MenuOption objects).
Methods to get and set the attributes are not included in the illustration.
7.6.2 The basics
Moving on to the code, we will start with the MenuComponent class. This class
expresses what’s similar between menus and menu options (Listing 7.9). Both menus
and menu options need a label and the ability to be marked as current.
mark() and isMarked() let us set and retrieve the state of being marked as current.
We have simple accessors for the label. We will also set the label in the constructor,
but we’re leaving that part of it to the child classes.
markPathToMenuOption() will be the method for marking the path; both the
menu object and the menu option object have to implement it. hasMenuOptionWithId() exists to support the marking operation.
To implement the most basic Composite structure, all we need is an add()
method to add a child to a node (a menu or menu option in this case).
class Menu extends MenuComponent {
protected $marked = FALSE;
protected $label;
private $children = array();
public function __construct($label) {
$this->label = $label;
}
public function add($child) {
$this->children[] = $child;
}
}
add() does not know or care whether the object being added is a menu or a menu
option. We can build an arbitrarily complex structure with this alone:
$menu = new Menu('News');
$submenu = new Menu('Events');
$menu->add($submenu);
$submenu = new Menu('Concerts');
$menu->add($submenu);
7.6.3 A fluent interface
This reuse of temporary variables is rather ugly. Fortunately, it’s easy to achieve what’s
known as a fluent interface:
All we have to do is return the child after adding it:
public function add($child) {
$this->children[] = $child;
return $child;
}
Or even simpler:
public function add($child) {
return $this->children[] = $child;
}
A mentioned, this is all we need to build arbitrarily complex structures. In fact, if the
menu option is able to store a link URL, we already have something that could possibly
be useful in a real application.
7.6.4 Recursive processing
But we haven’t finished our study of the Composite pattern until we’ve tried using it
for recursion. Our original requirement was to be able to mark the path to the currently
selected menu option. To achieve that, we need to identify the menu option.
Let’s assume that the menu option has an ID, and that the HTTP request contains
this ID. So we have the menu option ID and want to mark the path to the menu
option with that ID. Unfortunately, the top node of our composite menu structure
cannot tell us where the menu option with that ID is located.
We’ll do what might be the Simplest Thing That Could Possibly Work: search for
it. The first step is to give any node in the structure the ability to tell us whether it
contains that particular menu option. The Menu object can do that by iterating over
its children and asking all of them whether they have the menu option. If one of them
does, it returns TRUE, if none of them do, it returns FALSE:
class Menu extends MenuComponent...
public function hasMenuOptionWithId($id) {
foreach ($this->children as $child) {
if ($child->hasMenuOptionWithId($id)) return TRUE;
}
return FALSE;
}
}
The recursion has to end somewhere. Therefore, we need the equivalent method in
the MenuOption class to do something different. It simply checks whether its ID is
the one we are looking for, and returns TRUE if it is:
class MenuOption extends MenuComponent {
protected $marked = FALSE;
protected $label;
private $id;
public function __construct($label,$id) {
$this->label = $label;
$this->id = $id;
}
public function hasMenuOptionWithId($id) {
return $id == $this->id;
}
}
Now we’re ready to mark the path.
class Menu extends MenuComponent...
public function markPathToMenuOption($id) {
if (!$this->hasMenuOptionWithId($id)) return FALSE;
$this->mark();
foreach ($this->children as $child) {
$child->markPathToMenuOption($id);
}
}
}
If this menu contains the menu option with the given ID, it marks itself and passes
the task on to its children. Only the one child that contains the desired menu option
will be marked.
The MenuOption class also has to implement the markPathToMenuOption()
method. It’s quite simple:
class MenuOption extends MenuComponent...
public function markPathToMenuOption($id) {
if ($this->hasMenuOptionWithId($id)) $this->mark();
}
}
But our traversal algorithm is not the most efficient one. We’re traversing parts of the
tree repeatedly. Do we need to change that?
7.6.5 Is this inefficient?
We have deliberately sacrificed efficiency in favor of readability, since the data structure
will never be very large. The implementation uses one method (hasMenuOptionWithId)
to answer a question and another (markPathToMenuOption) to
make a change. This is a good idea, which is why there is a refactoring to achieve this
separation, called Separate Query from Modifier.
To make it slightly faster, we could have let the first method return the child that
contains the menu option we’re searching for. That would have enabled us to avoid
the second round of recursion. But it would also have made the intent of the has-
MenuOptionWithId() method more complex and therefore harder to understand.
It would have been premature optimization.
And this premature optimization would have involved a premature, low-quality
decision. If we did want to optimize the algorithm, approaching optimization as a task
in itself, we should be looking at more alternatives. For example, we could do the
search, have it return a path to the menu option as a sequence of array indexes, and
then follow the path. Or we could do it with no recursion at all if we kept a list of all
menu options indexed by ID and added references back to the parents in the composite
structure. Starting with the menu option, we could traverse the path up to the root
node, marking the nodes along the way.
One thing the Composite pattern does is to hide the difference between one and
many. The Composite, containing many elements, can have the same methods as a
single element. Frequently, the client need not know the difference. In chapter 17, we
will see how this works in the context of input validation. A validator object may have
a validate() method that works the same way whether it is a simple validator or
a complex one that applies several different criteria.
The Composite View pattern (which is the main subject of chapter 14) is related,
though not as closely as you might think.
7.7 SUMMARY
While design principles are approximate guidelines, design patterns are more like
specific recipes or blueprints; they cannot be used mindlessly. To apply them, we
need to understand where, how, and why they’re useful. We need to look at context,
consider alternatives, tweak the specifics, and use the object-oriented principles
in our decision-making.
We have seen a small selection of design patterns. All of them are concerned with creating
pluggable components. Strategy is the way to configure an object’s behavior by
adding a pluggable component. Adapter takes a component that is not pluggable and
makes it pluggable. Decorator adds features without impairing pluggability. Null Object
is a component that does nothing, but can be substituted for another to prevent a behavior
from happening without interfering with the smooth running of the system. Iterator
is a pluggable repetition engine that can even be a replacement for an array. Composite
is a way to plug more than one component into a socket that’s designed for just one.
In the next chapter, we will use date and time handling as a vehicle for making the
context and the alternatives for design principles and patterns clearer.
Master SharePoint with 3 eLearning Seminars Learn how to build a better SharePoint infrastructure and enable powerful collaboration with MVPs Dan Holme and Michael Noel. Register today!
SharePointConnections Conference Fall 2008 Don’t miss the premier event for Microsoft IT Professionals in Las Vegas, November 10-13. Register and book your room by August 25 and receive a FREE room night (based on a three night minimum stay).
VMworld 2008 - Sign Up Today! Join your peers on September 15-18 at The Venetian Hotel in Las Vegas as VMware hosts VMworld 2008, the leading Virtualization event.
Microsoft® Tech•Ed EMEA 2008 IT Professionals Advance your thinking with new ideas and practical real-world solutions at Microsoft’s FIVE day technical infrastructure conference 3-7 Nov., 2008. Register before 26 September 2008 to save €300.
Order Your SQL Fundamentals CD Today! Learn how to use SQL Server, understand Office integration techniques and dive into the essentials of SQL Express and Visual Basic with this free SQL Fundamentals CD.
Are You Really Compliant with Software Regulations? View this web seminar that will help you with compliance best practices and check out a management solution to assure that you won’t be in jeopardy of an audit.