■Chapter 21: Objects, Patterns, Practice ............................................................
Choice
There is no law that says you have to develop with classes and objects only. Well-designed object-
oriented code provides a clean interface that can be accessed from any client code, whether procedural
or object oriented. Even if you have no interest in writing objects (unlikely if you are still reading this
book), you will probably find yourself using them, if only as a client of PEAR packages.
Encapsulation and Delegation
Objects mind their own business and get on with their allotted tasks behind closed doors. They provide
an interface through which requests and results can be passed. Any data that need not be exposed, and
the dirty details of implementation, are hidden behind this front.
This gives object-oriented and procedural projects different shapes. The controller in an object-
oriented project is often surprisingly sparse, consisting of a handful of instantiations that acquire objects
and invocations that call up data from one set and pass it on to another.
A procedural project, on the other hand, tends to be much more interventionist. The controlling
logic descends into implementation to a greater extent, referring to variables, measuring return values,
and taking turns along different pathways of operation according to circumstance.
Decoupling
To decouple is to remove interdependence between components, so that making a change to one
component does not necessitate changes to others. Well-designed objects are self-enclosed. That is, they
do not need to refer outside of themselves to recall a detail they learned in a previous invocation.
By maintaining an internal representation of state, objects reduce the need for global variables—a
notorious cause of tight coupling. In using a global variable, you bind one part of a system to another. If
a component (whether a function, a class, or a block of code) refers to a global variable, there is a risk
that another component will accidentally use the same variable name and substitute its value for the
first. There is a chance that a third component will come to rely on the value in the variable as set by the
first. Change the way that the first component works, and you may cause the third to stop working. The
aim of object-oriented design is to reduce such interdependence, making each component as self-
sufficient as possible.
Another cause of tight coupling is code duplication. When you must repeat an algorithm in different
parts of your project, you will find tight coupling. What happens when you come to change the
algorithm? Clearly you must remember to change it everywhere it occurs. Forget to do this, and your
system is in trouble.
A common cause of code duplication is the parallel conditional. If your project needs to do things in
one way according to a particular circumstance (running on Linux, for example), and another according
to an alternative circumstance (running on Windows), you will often find the same if/else clauses
popping up in different parts of your system. If you add a new circumstance together with strategies for
handling it (MacOS), you must ensure that all conditionals are updated.
Object-oriented programming provides a technique for handling this problem. You can replace
conditionals with polymorphism. Polymorphism, also known as class switching, is the transparent use of
different subclasses according to circumstance. Because each subclass supports the same interface as the
common superclass, the client code neither knows nor cares which particular implementation it is using.
Conditional code is not banished from object-oriented systems; it is merely minimized and
centralized. Conditional code of some kind must be used to determine which particular subtypes are to be
served up to clients. This test, though, generally takes place once, and in one place, thus reducing coupling.