PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1
CHAPTER 9 ■ GENERATING OBJECTS

// instantiate a BookProduct objec
} else if ( $row['type'] == "cd" ) {
$product = new CdProduct();
// instantiate a CdProduct object
} else {
// instantiate a ShopProduct object
}
$product->setId( $row['id'] );
$product->setDiscount( $row['discount'] );
return $product;
}


The getInstance() method uses a large if/else statement to determine which subclass to
instantiate. Conditionals like this are quite common in factory code. Although you should attempt to
excise large conditional statements from your projects, doing so often has the effect of pushing the
conditional back to the moment at which an object is generated. This is not generally a serious
problem, because you remove parallel conditionals from your code in pushing the decision making
back to this point.
In this chapter, then, I will examine some of the key Gang of Four patterns for generating objects.


The Singleton Pattern


The global variable is one of the great bugbears of the object-oriented programmer. The reasons should
be familiar to you by now. Global variables tie classes into their context, undermining encapsulation (see
Chapter 6, “Objects and Design,” and Chapter 8, “Some Pattern Principles,” for more on this). A class
that relies on global variables becomes impossible to pull out of one application and use in another,
without first ensuring that the new application itself defines the same global variables.
Although this is undesirable, the unprotected nature of global variables can be a greater problem.
Once you start relying on global variables, it is perhaps just a matter of time before one of your libraries
declares a global that clashes with another declared elsewhere. You have seen already that, if you are not
using namespaces, PHP is vulnerable to class name clashes, but this is much worse. PHP will not warn
you when globals collide. The first you will know about it is when your script begins to behave oddly.
Worse still, you may not notice any issues at all in your development environment. By using globals,
though, you potentially leave your users exposed to new and interesting conflicts when they attempt to
deploy your library alongside others.
Globals remain a temptation, however. This is because there are times when the sin inherent in
global access seems a price worth paying in order to give all your classes access to an object.
As I hinted, namespaces provide some protection from this. You can at least scope variables to a
package, which means that third-party libraries are less likely to clash with your own system. Even so,
the risk of collision exists within the namespace itself.


The Problem


Well-designed systems generally pass object instances around via method calls. Each class retains its
independence from the wider context, collaborating with other parts of the system via clear lines of
communication. Sometimes, though, you find that this forces you to use some classes as conduits for
objects that do not concern them, introducing dependencies in the name of good design.
Imagine a Preferences class that holds application-level information. We might use a Preferences
object to store data such as DSN strings (Data Source Names hold table and user information about a
database), URL roots, file paths, and so on. This is the sort of information that will vary from installation

Free download pdf