Large organization is loose organization. Nay, it would be almost as true to say that organization is always disorganization.
If you only have a hammer, you tend to see every problem as a nail.
Because things are the way they are, things will not stay the way they are.
Way back in the 1970s when user interfaces (UIs) were fairly primitive, a programmer decided to separate key elements of what was required in a graphical user interface (GUI). Each of the parts was given a specific task, and each part communicated with the other parts. The parts were grouped into domain objects and presentation objects. The domain objects were for modeling what perception the user was given of the real world, and the presentation objects were what was viewed on the screen. The parts were divided into a model, a view, and a controller, and the Model-View-Control (MVC) design pattern came into being.
The domain element in the MVC is the model. The model is responsible for what is variously called the data, business, or application logic. For example, take a thermostat. Within the thermostat is a value that represents a temperature, either the actual ambient temperature or a value where a heater is turned Off or On. Somewhere in that thermostat you have a set of data that can be envisioned as set into variables:
$a = current temperature $b = turn off heater $c = turn on heater
At a given time, the actual values may be:
$a=65; $b=67; $c=64;
Those values are generated by the controller settings and a thermometer that reads the ambient temperature. It doesn’t matter where or how the values are generated; that’s the business of the model.
The presentation part of the MVC has two elements: the view and controller. The view, in the thermostat example, is a window that shows the temperature and settings to the viewer. The model provides the ambient temperature, and the controller provides the on/off temperature sent to the view for display. The controller is the device that adjusts the on/off values. (The view displays the controller, however.) Figure 3-1 illustrates the MVC in a thermostat.
A thermostat hanging on the wall and a virtual one represented in a computer program are different in that the view communicates to the controller how the user input is to be handled. Remember that in a computer display, the UI is actually part of the view in that the user can see it for making changes. The view then communicates changes by the user to the controller, which in turn sends the information to the model. Figure 3-2 shows the same object in a class diagram.
The MVC seems to have as many variations as there are implementations. Because of its simplicity and utility, it has been used and misused a good deal. However, the point remains that it stands as a structure that provides loose coupling between its parts. For example, suppose you want to change the view from an analog view to a digital view. That’s easy to do because the model and controller are self-contained entities and really don’t care what the view displays. Likewise, the controller can change from settings in Fahrenheit to Celsius, and as long as the model is sent the information, it doesn’t care what temperature format is used.
The importance of the MVC lies more in the demonstration of loose coupling than in direct functionality. By separating out the different elements (or participants) in accomplishing a task, the MVC added a good deal of flexibility required in large programs. The larger the program, the more the program required the modular flexibility in the MVC.
While the MVC is an important point in the development of design patterns, it is only a starting point. The use, misuse, and overuse of the MVC in programming are well documented. When overemployed, it is not unlike building a house with a single tool—a hammer—to take care of all sawing, measurement, and drilling tasks.
In the landmark publication Design Patterns: Elements of Reusable Object-Oriented Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (best known as The Gang of Four or simply GoF), the MVC is used to begin a discussion of the many tools that make up design patterns. The MVC is credited with decoupling views and models by establishing a subscribe/notify protocol between them. However, GoF go on to show that many different and fundamental design patterns not only make up the MVC, but there is also a far greater range of common patterns for the kinds of work that computer programmers do, wholly apart from MVC. So, rather than looking at design patterns tied in some way to MVC, we need to examine design patterns apart from the MVC. We need instead to examine the principles of design patterns in general and each design pattern in its own right.
This first principle is a tricky one for PHP: program to an interface, not an implementation. In the simplest form, programming to an interface instead of an implementation means to set your variable as an instance of an abstract class or interface data type instead of a concrete implementation. By programming to an interface, you decouple the design from the implementation in such a way that you could (for example) easily replace a complex database implementation with a much simpler mock implementation for testing purposes. In languages that include data typing in variable declarations, you simply name the interface as the data type instead of the concrete class the variable instantiates. For example, in a strongly typed language, you may have the following:
Name of interface
Implements IAlpha; AlphaA is data type IAlpha
Declares type as IAlpha (for example, IAlpha useAlpha)
Instantiates new AlphaA()
You cannot declare a variable to be of an abstract parent (abstract class or interface) data type in PHP because you cannot declare a data type without instantiating an instance of a class. Further, you cannot instantiate an object from an abstract class. A variable can “get” a data type by instantiating a concrete object using the following format:
$useAlpha = new AlphaA();
The variable named $useAlpha
is
an instance of the class AlphaA
and
is considered to be of that data type. So $useAlpha
is of the data type AlphaA
, a concrete implementation of a
concrete class. At the same time, however, while an instance is the data
type of a concrete class, it is also of the data type of the concrete
class’ parent. That bears repeating:
An object instance is the data type of the object it instantiates and the data type of the object’s parent.
How can that be done without strong typing? In PHP, you can program to an interface using code hinting.
In Chapter 2, the section
Type Hinting: Almost Data Typing showed an
example of using code hinting with the interface IProduct
. Two different classes, FruitStore
and CitrusStore
, both implemented the same
interface. Using code hinting, both were able to operate as arguments
where a method with a code hinting parameter of IProduct
was in force. That code hint
specified the IProduct
data type as a
requirement for the argument. By closely examining a segment of that
code, you can see how the operations were coding to an interface. Figure 3-3 shows the first
step in a PHP operation programming to an interface: instantiating
objects to be instances of interface implementations (see Type Hinting: Almost Data Typing for the full
program).
Figure 3-3 instantiations show exactly the opposite of what the principle of programming to an interface suggests: the variables are instantiated as instances of concrete implementations instead of the interface they share. What you cannot see in PHP because of weak data typing is that the concrete implementations are data types of the interface as well.
When type hinting is used, the programmer must include an object of the specified type hint. If that type hint is an interface, then the program will work exactly as though you are typing to an interface. Figure 3-4 shows the details of that process.
In looking at 3-3 and 3-4, you can see that the key method is
doInterface()
. It contains the type
hint. When either the concrete instances of the two concrete
implementations (FruitStore
or
CitrusStore
) are used as arguments in
the doInterface()
method, the output
is predictable. As long as any other implementation of the interface
maintains the IProduct
interface
(including return data types), it won’t matter how complex the program
becomes. Your changes and additions will work the way you expect them to
work, and they will not damage some other part of the program.
To help clarify the principle of programming to an interface instead of an implementation, the examples employ interfaces created using the keyword interface. Before continuing, though, understand that the concept of interface refers to the methods and their signatures, not necessarily the keyword, interface. Every class has an interface made up of its methods’ signatures. Because most design patterns seldom include an extension from a concrete class, you need to understand that an extension from an abstract class is similar to the implementation of an interface.
In this next example, a simple abstract class is extended by two simple implementations. Then, a client class using type hinting shows how you can program to the interface using an abstract class. Comments in the code help to enforce the expected return values.
First, the abstract class IAbstract
has a protected property ($valueNow
), two protected abstract methods
(giveCost
and giveCity
), and a public function that is not
abstract, displayShow
:
<?php abstract class IAbstract { //Property available to all implementations protected $valueNow; /*All implementations must include the following 2 methods: */ //Must return decimal value abstract protected function giveCost(); //Must return string value abstract protected function giveCity(); //This concrete function is available to all //class implementations without overriding //the contents public function displayShow() { $stringCost =$this->giveCost(); $stringCost = (string)$stringCost; $allTogether=("Cost: $" . $stringCost . " for " . $this->giveCity()); return $allTogether; } } ?>
Next, two different extensions of the abstract class have different implementations of the abstract methods:
//NorthRegion.php <?php include_once('IAbstract.php'); class NorthRegion extends IAbstract { //Must return decimal value protected function giveCost() { return 210.54; } //Must return string value protected function giveCity() { return "Moose Breath"; } } ?> //WestRegion.php <?php include_once('IAbstract.php'); class WestRegion extends IAbstract { //Must return decimal value protected function giveCost() { $solarSavings=2; $this->valueNow=210.54/$solarSavings; return $this->valueNow; } //Must return string value protected function giveCity() { return "Rattlesnake Gulch"; } } ?>
With two different implementations of an abstract class, you can
see that programming to an interface in this context really means the
interface of the class and not an interface structure as used with the
keyword interface. Finally, the Client
class sets up a method that includes
code hinting that specifies the abstract class as the interface:
<?php include_once('NorthRegion.php'); include_once('WestRegion.php'); class Client { public function __construct() { $north=new NorthRegion(); $west= new WestRegion(); $this->showInterface($north); $this->showInterface($west); } private function showInterface(IAbstract $region) { echo $region->displayShow() . "<br/>"; } } $worker=new Client(); ?>
The output shows the following:
Cost: $210.54 for Moose Breath Cost: $105.27 for Rattlesnake Gulch
The amounts are different for the different regions because the
abstract methods are implemented differently by the two concrete classes
NorthRegion
and WestRegion
. If an incorrect data type is used
(a string, for example), you will see the following kind of error
message:
Catchable fatal error: Argument 1 passed to Client::showInterface() must be an instance of IAbstract, string given, called in /Library/
So, in its own way, type hinting can help your programs move toward programming to an interface instead of an implementation.
To see how useful this style of programming is, add SouthRegion
and EastRegion
implementations of the IAbstract
abstract class. Remember to use a
decimal value for the giveCost()
method and a string value for the giveCity()
. Likewise, maintain the rest of the
interface for both, and add them to the Client
class.
You should see how easy it is to make additions and changes as long as
you maintain the interface.
Some OOP programmers equate object reuse with inheritance. A class may be a rich collection of properties and methods, and by extending it you get to reuse all of those object elements without having to recode. Extend the class, add the required new properties and methods, and you’re ready to go. Eventually, extension can create problems in the form of tightly bound objects. It is within the context of overinheritance that the second principle is grounded: favor object composition over class inheritance.
So what’s the difference between object composition and class inheritance? This proposition does not imply getting rid of inheritance. Instead, it means that in developing programs where there’s an opportunity to use composition, use it instead of inheritance. In doing so, the child classes do not become bloated with unused properties and methods.
To see the difference between using inheritance and composition, a simple example illustrates using a parent and child class (inheritance) and two separate classes (composition). Before looking at the code, Figure 3-5 shows a general idea of the difference between using inheritance and composition.
Using inheritance, the client can make a single instantiation for the math and text functionality. With composition, the client uses two different instances to gain access to the functionality of both classes. Composition within a design pattern typically refers to composition within a participant in the pattern.
First, look at the code using inheritance. The first class (parent class) is a simple one that has methods for addition and division:
<?php //DoMath.php class DoMath { private $sum; private $quotient; public function simpleAdd($first,$second) { $this->sum=($first + $second); return $this->sum; } public function simpleDivide($dividend, $divisor) { $this->quotient=($dividend/$divisor); return $this->quotient; } } ?>
The second class is for adding text functionality. One method
converts numbers to strings and the other sets up a formatted output. It
inherits all of the DoMath
functionality through extension:
<?php //InheritMath.php include_once('DoMath.php'); class InheritMath extends DoMath { private $textOut; private $fullFace; public function numToText($num) { $this->textOut=(string)$num; return $this->textOut; } public function addFace($face, $msg) { $this->fullFace= "<strong>" . $face . "</strong>: " . $msg; return $this->fullFace; } } ?>
The Client
class instantiates the InheritMath
class and is able to use all of
the functionality inherited from the DoMath
class as well as the included classes
for text work:
<?php //ClientInherit include_once('InheritMath.php'); class ClientInherit { private $added; private $divided; private $textNum; private $output; public function __construct() { $family=new InheritMath(); $this->added=$family->simpleAdd(40,60); $this->divided=$family->simpleDivide($this->added,25); $this->textNum=$family->numToText($this->divided); $this->output=$family->addFace("Your results",$this->textNum); echo $this->output; } } $worker=new ClientInherit(); ?>
The output is a formatted calculated value:
Your results: 4
That output used four different methods, two of which were inherited from a parent class.
Turning to composition, the Client
class uses
two separate classes, each containing two methods. The DoMath
class is identical to the parent class
in the inheritance example, so begin by examining the DoText
class:
<?php //DoText.php class DoText { private $textOut; private $fullFace; public function numToText($num) { $this->textOut=(string)$num; return $this->textOut; } public function addFace($face, $msg) { $this->fullFace= "<strong>" . $face . "</strong>: " . $msg; return $this->fullFace; } } ?>
The DoText
class looks a lot
like the InheritMath
class, and it
is. However, it does not inherit the DoMath
class.
In using composition in this example, the client uses both of the separate classes, and the results are identical. However, the client must instantiate two objects instead of one. Otherwise, the client used in composition is very similar to the one used with inheritance:
<?php //ClientCompose.php include_once('DoMath.php'); include_once('DoText.php'); class ClientCompose { private $added; private $divided; private $textNum; private $output; public function __construct() { $useMath=new DoMath(); $useText=new DoText(); $this->added=$useMath->simpleAdd(40,60); $this->divided=$useMath->simpleDivide($this->added,25); $this->textNum=$useText->numToText($this->divided); $this->output=$useText->addFace("Your results",$this->textNum); echo $this->output; } } $worker=new ClientCompose(); ?>
The results are the same, but the Client
class
has to include multiple classes. That may seem like an argument to favor
inheritance, but composition in larger programs avoids the problem of
maintaining each child class with multiple levels of inheritance and the
possible errors that can occur. For instance, a change in the parent
class can ripple down to the child implementation that interferes with
an algorithm used by the child class.
In the context of design patterns, you will see classes that use other classes in their construction. When one class passes off a task to another class, that is delegation. It’s what makes composition so powerful.
With inheritance, each child IS-A part of another class or classes. With composition, objects may USE-A different class or group of classes for a series of tasks. This does not mean that inheritance should not be used. In fact, most design patterns include inheritance and composition used together. Instead of using inheritance in a long series of child, grandchild, great-grandchild, ad infinitum, a design pattern approach encourages using shallow inheritance and using the functionality of more than a single class. This approach helps to avoid tight binding and crashes occurring when changes are made to designs where concrete classes have child classes.
When working out when and how to use delegation, how much inheritance should be included, and how to ensure reuse in OOP programming, design patterns can be seen as a big cheat sheet. You can quickly look up the general designs that use class diagrams to display where inheritance and composition are employed. Using a Unified Modeling Language (UML), you can learn to look at a class diagram and quickly see the different parts (called participants). (Chapter 4 covers the details of using UMLs to work with design patterns with PHP.)
This book follows the organization of design patterns laid out by the Gang of Four. In the most general sense, design patterns are organized by purpose and scope. Design pattern purpose has been organized into three classifications:
Creational
Structural
Behavioral
This classification is a general view of patterns in terms of what goal they are designed to accomplish. Scope has two categories:
Class
Object
This section looks at these ordering categories and explains how they can be handy in selecting and understanding design patterns.
As the name implies, creational patterns are ones used to create objects. More specifically, they abstract the instantiation process. As a program comes to depend more on composition, it depends less on hardcoded instantiations and more on a flexible set of behaviors that can be arranged into a more complex set. Creational patterns provide ways to encapsulate knowledge about which concrete classes a system uses and hide information about instance creation and composition.
The patterns concerned with the structure of compositions are structural. The structural class patterns employ inheritance to compose interfaces or implementations. The structural object patterns describe ways to compose objects to establish new functionality. Understanding structural patterns contributes to understanding and using interrelated classes as participants in a design pattern.
By far, the largest number of patterns is behavioral object ones. The focal point of these patterns is algorithms and the assignment of responsibilities between objects. Gamma et al. note that these design patterns describe more than the patterns of objects or classes. They describe the patterns of communication between classes and object.
The first of the two scope categories is class. The focus in these patterns is on the relationships between classes and their subclasses. Of the 24 design patterns in their book, GoF include only four in the class scope. The relationships are established through inheritance, and because GoF emphasize composition over inheritance, it is no surprise. Patterns in class scope are static and thereby fixed at compile time.
Part of learning design patterns is learning to select the most appropriate one. Keep in mind that design patterns are not templates. They are general strategies that deal with general problems that crop up in object-oriented programming. This book includes one design pattern from each of the three purpose categories and the two scope categories. In addition, it includes three chapters (Chapter 12 through Chapter 14) that tackle common uses of PHP with MySQL where design patterns will come in handy. The three patterns discussed in these final chapters are all of the object scope and behavioral purpose type, the largest category in the GoF catalog.
One of the first considerations in selecting a design pattern is to ask, “What causes redesign?” For example, suppose you have set up an online help desk. Users make requests and a database stores responses. However, you can plan on both the types of help request and the help responses changing. If your program depends on specific operations, making changes can cause problems. So instead of having hardcoded operations to satisfy a request, a Chain of Responsibility design pattern provides a way to allow the request to be passed along a chain to give more than one object a chance to handle it.
This book provides guidance on what kinds of problems different design patterns can solve. The chapters and patterns provide the context for understanding not only the general principles of design patterns but also specific cases of problems handled by particular patterns.
One thing to consider in choosing a design pattern is what will vary in a design. Instead of looking at the cause of redesign, this approach looks at what you want to be able to change without redesign. As you will see, the focus switches to encapsulating the concept that varies. Table 3-1 shows the nine design patterns this book explains divided into purpose, scope, and aspects of the pattern that can vary.
Table 3-1. Design pattern purpose, scope, and variation
Purpose | Scope | Name | What Can Vary |
---|---|---|---|
Creative | Class | Factory Method | Subclass of object instantiated |
Object | Prototype | Class of object instantiated | |
Structural | Class | Adapter* | Interface of object |
Object | Adapter* Decorator | Object responsibility without subclassing | |
Behavioral | Class | Template Method | Steps in algorithm |
Object | State | States of objects | |
Object | Strategy | An algorithm | |
Object | Chain of Responsibility | Object that can fulfill request | |
Object | Observer | Number of objects that depend on other objects; how many dependent objects can stay up to date |
*The Adapter pattern has two configurations: one class and one object. Chapter 7 examines both.
Each of the patterns has a general use. The variation must be understood in context, and as we look at each pattern, the variation becomes clearer.
Design patterns are smaller architectural elements and more abstract than frameworks. Further, design patterns are less specialized than frameworks. As a result, design patterns are more reusable and flexible than frameworks.
The advantage of a framework, somewhat like the advantage of a template, is that they are more instructive. They are clearer guides to the structures of problem solving. What they make up for in ease of use they give up in architectural flexibility. Using a framework, building applications goes much faster, but what you can build is constrained by the framework itself. A framework can contain object-oriented structures, and often frameworks will be stacked, each layer handling one aspect of a larger design. Some features of frameworks are shared in design patterns, but design patterns are not as specialized, large, or concrete as frameworks.