PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1

CHAPTER 6 ■ OBJECTS AND DESIGN


var $_touchezpas;


Code had to be checked closely, of course, because privacy was not strictly enforced. Interestingly,
though, errors were rare, because the structure and style of the code made it pretty clear which
properties wanted to be left alone.
By the same token, even in PHP 5, we could break the rules and discover the exact subtype of an
object that we are using in a class-switching context simply by using the instanceof operator.


function workWithProducts( ShopProduct $prod ) {


if ( $prod instanceof cdproduct ) {


// do cd thing


} else if ( $prod instanceof bookproduct ) {


// do book thing


}


}


You may have a very good reason to do this, but in general, it carries a slightly uncertain odor. By
querying the specific subtype in the example, I am setting up a dependency. While the specifics of the
subtype were hidden by polymorphism, it would have been possible to have changed the ShopProduct
inheritance hierarchy entirely with no ill effects. This code ends that. Now, if I need to rationalize the
CdProduct and BookProduct classes, I may create unexpected side effects in the workWithProducts()
method.
There are two lessons to take away from this example. First, encapsulation helps you to create
orthogonal code. Second, the extent to which encapsulation is enforceable is beside the point.
Encapsulation is a technique that should be observed equally by classes and their clients.


Forget How to Do It


If you are like me, the mention of a problem will set your mind racing, looking for mechanisms that
might provide a solution. You might select functions that will address an issue, revisit clever regular
expressions, track down PEAR packages. You probably have some pasteable code in an old project that
does something somewhat similar. At the design stage, you can profit by setting all that aside for a while.
Empty your head of procedures and mechanisms.
Think only about the key participants of your system: the types it will need and their interfaces. Of
course, your knowledge of process will inform your thinking. A class that opens a file will need a path;
database code will need to manage table names and passwords, and so on. Let the structures and
relationships in your code lead you, though. You will find that the implementation falls into place easily
behind a well-defined interface. You then have the flexibility to switch out, improve, or extend an
implementation should you need to, without affecting the wider system.
In order to emphasize interface, think in terms of abstract base classes rather than concrete
children. In my parameter-fetching code, for example, the interface is the most important aspect of the
design. I want a type that reads and writes name/value pairs. It is this responsibility that is important
about the type, not the actual persistence medium or the means of storing and retrieving data. I design
the system around the abstract ParamHandler class, and only add in the concrete strategies for actually
reading and writing parameters later on. In this way, I build both polymorphism and encapsulation into
my system from the start. The structure lends itself to class switching.

Free download pdf