PHP Objects, Patterns and Practice (3rd edition)

(Barry) #1
CHAPTER 20 ■ OBJECTS, PATTERNS, PRACTICE

When I first started working with patterns, I found myself creating Abstract Factories all over my
code. I needed to generate objects, and Abstract Factory certainly helped me to do that.
In fact, though, I was thinking lazily and making unnecessary work for myself. The sets of objects I
needed to produce were indeed related, but they did not yet have alternative implementations. The
classic Abstract Factory pattern is ideal for situations in which you have alternative sets of objects to
generate according to circumstance. To make Abstract Factory work, you need to create factory classes
for each type of object and a class to serve up the factory class. It’s exhausting just describing the
process.
My code would have been much cleaner had I created a basic factory class, only refactoring to
implement Abstract Factory if I found myself needing to generate a parallel set of objects.
The fact that you are using patterns does not guarantee good design. When developing, it is a good
idea to bear in mind two expressions of the same principle: KISS (“Keep it simple, stupid”) and “Do the
simplest thing that works.” eXtreme programmers also give us another, related, acronym: YAGNI. “You
aren’t going to need it,” meaning that you should not implement a feature unless it is truly required.
With the warnings out of the way, I can resume my tone of breathless enthusiasm. As I laid out in
Chapter 9, patterns tend to embody a set of principles that can be generalized and applied to all code.


Favor Composition over Inheritance


Inheritance relationships are powerful. We use inheritance to support runtime class switching
(polymorphism), which lies at the heart of many of the patterns and techniques I explored in this book.
By relying on solely on inheritance in design, though, you can produce inflexible structures that are
prone to duplication.


Avoid Tight Coupling


I have already talked about this issue in this chapter, but it is worth mentioning here for the sake of
completeness. You can never escape the fact that change in one component may require changes in
other parts of your project. You can, however, minimize this by avoiding both duplication (typified in
our examples by parallel conditionals) and the overuse of global variables (or Singletons). You should
also minimize the use of concrete subclasses when abstract types can be used to promote
polymorphism. This last point leads us to another principle:


Code to an Interface, Not an Implementation


Design your software components with clearly defined public interfaces that make the responsibility of
each transparent. If you define your interface in an abstract superclass and have client classes demand
and work with this abstract type, you then decouple clients from specific implementations.
Having said that, remember the YAGNI principle. If you start out with the need for only one
implementation for a type, there is no immediate reason to create an abstract superclass. You can just as
well define a clear interface in a single concrete class. As soon as you find that your single
implementation is trying to do more than one thing at the same time, you can redesignate your concrete
class as the abstract parent of two subclasses. Client code will be none the wiser, since it continues to
work with a single type.
A classic sign that you may need to split an implementation and hide the resultant classes behind an
abstract parent is the emergence of conditional statements in the implementation.

Free download pdf