Introduction

Even if you have never set eyes on a pattern catalog in your life, you have probably come across the phrase design patterns in recent years. You may even have assumed that patterns were yet another fad. After the turn of the century it suddenly seemed that patterns were everywhere, and PHP was not immune. Yet with increased support for objects in PHP 5, design patterns are set to become an essential tool for the PHP coder.

In this article I introduce patterns in the context of PHP 5. From a basic definition, I work through to a short example. I also describe the structure of a pattern and explain some of the principles that the classic patterns promote and deploy.

Design Pattern Basics

In order to get a handle on patterns, we need to cover some introductory ground. This section provides you with the kind of overview that will get you through the meet and greet cocktail party on a geek cruise. Don't worry, we'll move on to the beer and pizza later.

What is a Design Pattern?

As a coder you must encounter some requirements time and time again according to your business domain. The need to handle more than one database, for example, or to process an HTTP request so that the appropriate operations are performed in response. Such requirements demand solutions, and the solutions define the shapes and structures in your code. The first time you see the problem, you may have to think long and hard before a solution presents itself. When you see it again, you can refine the solution, or even replace it - selecting something neater perhaps, or something that has a more benign effect on your system as a whole.

After a while you will develop a feel for the forces at play in the systems you create, and craft a set of techniques to field them. Each technique you deploy will create new demands. If you centralize HTTP request processing, for example, you need a strategy to dispatch an appropriate view with your response. If, on the other hand, you process requests on a page by page basis the views self-select, but you need to be careful to resist the temptation to cut and paste code from page to page, and instead carefully factor out duplication.

Now that you have this knowledge, how easy is it to communicate to a new team member? Without a name for a technique, how do you describe it in a sentence standing at a white board? If you leave a project, how clear will those carefully crafted shapes in your system be to your successor? When you learned this stuff the hard way, how likely is it that someone else had already traveled the same route as you? How much time would you have saved if their conclusions had been named and written down?

If you design systems you work with patterns of design. Design pattern catalogs are simply a distillation of the neatest, the most common, the most useful. There is no mystery beyond the magic of naming and inscription, and yes, that is powerful magic.

This is one of my favorite design pattern definitions:

In the world of software, a pattern is a tangible manifestation of an organization's tribal memory.

(Grady Booch, Core J2EE Patterns)
Design patterns are just that. They are solutions to common problems, written down so that they can be easily shared by others in a community. This means that the lessons they inscribe do not need to be relearned over and over again as the same problems are independently encountered.

On its own, though, this is not quite enough. A less inspiring definition might go something like this:

A design pattern describes a problem together with one or more suggested solutions in the context of a system.
These three elements, problem, solution and context, are all essential components of a pattern.

Problem

Any experienced programmer knows that the real work often lies in the finding not the fixing of a bug. So it is with design patterns. An understanding of the problem should precede the selection of the tool that addresses it. A design pattern kicks off with a summary and a discussion of the problem that is to be addressed.

Critics of design patterns occasionally argue that techniques are deployed inappropriately and indiscriminately. This is probably a fair criticism of pattern deployment, but not necessarily a charge that can be laid at the door of the design patterns themselves. A pattern places immediate focus upon the the problem that its solution addresses. A good coder will therefore devote as much care to studying the shapes and smells in code that seem to call for a pattern's deployment as he will to the process of deployment.

Here's the Gang of Four on the subject:

Another important change over the past year has been a greater emphasis on the problem that a pattern solves. It's easiest to see a pattern as a solution, as a technique that can be adapted and reused. It's harder to see when it is appropriateƒ_"to characterize the problems it solves and the context in which it's the best solution. In general, it's easier to see what someone is doing than to know why, and the "why" for a pattern is the problem it solves. Knowing the purpose of a pattern is important too, because it helps us choose patterns to apply. It also helps us understand the design of existing systems. A pattern author must determine and characterize the problem that the pattern solves, even if you have to do it after you've discovered its solution.

(Gamma et al, Design Patterns)

Solution

This element is often confused with the pattern itself. This is understandable - patterns are named after their solutions after all. The thrust of a design pattern aims squarely at the deployment of a solution, and then analyzes its effects and variations.

A pattern's solution is presented in a number of ways. It is described in a practical example, and discussed in principle. Such descriptions are backed up by diagrams (using the Universal Modeling Language, a tool for representing classes, objects and interactions). Patterns also include illustrative code examples in one or more object-oriented languages.

It is important to distinguish patterns from cookbook recipes. You would rarely cut and paste a code example from a pattern into a project. A solution is described in general terms rather than small usable fragments. Where code is used, its purpose is to illustrate the principles and forces at play rather than to provide a drop-in fix.

Context

The problem space described in a design pattern itself provides some context. There are other elements, though, almost as important. When you deploy a pattern you create a new set of conditions. These consequences, both good and bad, are discussed in detail, as are the related patterns that might address them. In this way, patterns often tend to engender other patterns. This quasi-viral aspect is probably one of the characteristics that alarms some critics.

30 Years of Design Patterns

Although patterns are simply discussions about problems and solutions and in that sense ubiquitous and timeless, there is a particular form and ethos to them. This originates with an academic called Christopher Alexander who developed patterns in the 1970s as a way of describing elements of architecture. That's architecture in the literal sense of designing the structures in which we live and work. Alexander defined patterns so that common forces and techniques could be summarized, shared and combined.

Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.

(Christopher Alexander, A Pattern Language)
When design patterns jumped disciplines in the 80s it was a small but influential band of object-oriented programmers working with a language called SmallTalk who embraced them. Some crafts evolve over hundreds, even thousands of years. A farrier or a carpenter can draw upon a rich lexicon to describe the steps he must take to complete a task. Computer science has no such tradition to draw upon, and object-oriented programmers were discovering distinct techniques and challenges in a still newer sub-discipline.

With the rise of C++ and Java, patterns found a wider community. Because object-oriented problems and solutions survive the leap from language to language more or less intact, it wasn't just the idea of the design pattern that survived. Many of the patterns themselves remain applicable across many languages (although care must be taken in this respect).

In 1995 Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides laid down a set of core patterns in Design Patterns: Elements of Reusable Object-Oriented Software. The authors were affectionately dubbed 'The Gang of Four' and their text became known as the Gang of Four book, or GOF. These patterns were described in the context of Smalltalk and C++, but the briefest of glances at Java's foundation classes reveals that their influence was there right at the birth of the platform.

By the end of the nineties most design pattern books used Java for their examples. Although there are many pattern catalogs, the Gang of Four patterns remain the most famous and most languages capable of supporting object-oriented programming have their books and websites porting the pattern examples. This, of course, includes PHP.

With the release of PHP 4 at the turn of the century serious object-oriented development at last became possible. Even so, the popularity of this development approach took some in the PHP community by surprise. Objects had been included in PHP 3 really as a wrapper around associative arrays, and the enhancements ushered in by PHP 4, though significant, still fell some way short of full support. Nonetheless, developers embraced objects both in third party systems and, importantly, PHP's own PEAR repository, where object-oriented code soon became best practice. Leon Atkinson wrote about patterns on Zend.com and Harry Fuecks founded his popular site at phppatterns.com.

If PHP 4 reinvented the language as object-oriented, PHP 5 was all but designed with patterns in mind. One, Iterator, is actually part of PHP itself. Design patterns even have their own small section in the PHP manual. Of all the obvious changes to the Zend engine, most are improvements to its support for objects. These enhancements are well documented elsewhere, but the key point is that objects now lie at the heart of PHP. With the opportunities for development this offers the programmer comes the responsibility to use the tools well. Design patterns can help.

Patterns and the Hidden Agenda

I may have already given the game away by discussing patterns and object-oriented programming as if they were two sides to the same coin. The fact is, in the context of computer programming design patterns almost exclusively describe object-oriented solutions. There are exceptions of course - Martin Fowler's Transaction Script pattern springs to mind - but these tend to prove the rule.

It's not just classes and objects that are smuggled in under the skirts of the design pattern catalog. Patterns tend to encourage some key principles of design, some of which are covered in the section Pattern Principles below.

The Structure of a Pattern

Patterns come in different forms, and as with all else, there is debate as to which is best. Some writers like Martin Fowler favor a less formal discursive approach, with relatively few sections. Others, like the Gang of Four use many tightly defined sections. Here are some of the sections used by the Gang of Four:

  • Intent: One or two sentences that sum up the pattern. This summary can be extracted for use in list formats.
  • Motivation: An overview of the problem, usually couched in terms of an example.
  • Applicability: A breakdown of the different situations in which the pattern's solution might usefully be applied.
  • Structure: A UML class diagram that illustrates the participants of the system and their relationships.
  • Participants/Collaborations: The components that the pattern describes, and the ways in which they work together.
  • Consequences: Every design decision has ramifications, both good and bad. This section looks at the outcomes you can expect once you have deployed a pattern.
  • Implementation: A full discussion of the solution.
  • Sample Code: a simplified example using an object-oriented language. The scenario for the example should already be familiar from other sections.
  • Known Uses: Patterns are said to be discovered, not invented. A pattern must have been observed in the wild to truly earn the name. According to some, the 'rule of three' requires that three documented examples of the pattern solution in use must be offered before the pattern can be considered valid.
  • Related Patterns: Patterns tend to interact well with other patterns. This section lists those that are required for or suggested by the current pattern.

Beyond the Hype: Why use Patterns?

Patterns describe methods for solving problems. As the distillation of real-world experience, these methods are tried and tested. Real programmers have been there before you, thought the problem through, and written down their conclusions, together with any exceptions, warnings and alternative strategies. What's not to like?

Equally important is the fact that patterns can help you to recognize that problems exist in the first place. A problem in this instance may equally be an opportunity ƒ_" a chance to make your code easier to extend and reuse, for example. It is these shapes in your code that you will begin to notice as you become familiar with design patterns.

Design patterns also give us names that we can use to describe the techniques we deploy in our projects. This is useful because most of these concepts and practices can be difficult to sum up in a few sentences. If I say 'let's use a bunch of Decorator classes' to a colleague she should know at once what I mean. If I say 'let's use a set of classes that share a common type whose objects can be combined into a pipeline so that we can perform a series of actions in an order that's configurable at runtime', she may be justified in asking for a few more details.

By and large patterns are unashamedly object-oriented and, partly for this reason, they tend to apply well across different object capable languages. There are exceptions, of course, especially in some enterprise patterns, and patterns that rely upon particular kinds of typing. Nonetheless, this applicability means that you don't have to wait for a pattern to be described in the context of your particular language before using it. This is great news for PHP programmers. We can benefit instantly from the discoveries and discussions of programmers from the SmallTalk, C++, Java and C# communities.

By their nature and name, patterns are design-oriented. This means that they tend to promote good practice in general, and some key principles. The result should be code that is easy to maintain and to deploy across many projects.

Patterns breed patterns. Once deployed, one pattern tends to create the conditions in which others might be useful. Larger patterns can be composed of sets of other, more focused design patterns. Although you should avoid littering a project with patterns just because they fit together nicely, this interoperability is handy because a single design decision can set a condition which suits one or more related solutions.

Finally, and not least, patterns are pretty. I'm serious about this one. There is a deep aesthetic appreciation to be found in the way that the components of a pattern combine both in their static and dynamic configurations. Simple but elegant interfaces and inheritance structures combine to make flexible and complex runtime interactions possible. It's neat, it's powerful, it's satisfying.

Pattern Principles

There isn't a monolithic pattern design manifesto, no central committee of correct programming to which pattern authors must answer, though I guess some forums may make it look that way at times.

There are, however, some key principles and outcomes that tend to motivate patterns.

Code to an Interface not an Implementation

You may have come across the concept of polymorphism. Also known as class switching, polymorphism is the practice of using a common superclass to mask the distinct implementations laid down by its concrete children.

We need look no further than the PEAR repository for a good example of this.

PHP provides similar but nonetheless different ways of talking to each of the databases that it supports. This means that you can end up tying your code down. If you embed MySQL function calls into a system, you become committed to that platform. Code that has no real stake in the database server with which it communicates is nevertheless bound up with MySQL. If the code is deployed in a new environment, one that uses SQLite for example, it will have to be amended before it works. You could end up maintaining two versions of the same code, which is a notorious headache.

The PEAR DB package provides a solution by offering a central interface for making SQL queries, behind which it hides the specifics of its communication with the database server.

Here is some code to connect to a database and retrieve a data set using PEAR::DB.

<?php

require_once("DB.php");

$dsn = "sqlite://./venue.db";// get this from config
$db = DB::Connect($dsn);
$result = $db->query("SELECT * FROM venue");

while (
$row = $result->fetchRow(DB_FETCHMODE_ASSOC)) {
    print
"id: {$row[id]} name: {$row[name]}\n";
}

?>

We pass a DSN (Data Storage Name) to the DB object's static connect() method and get an object in return. We use this object to make a query and then to generate another object that encapsulates a database result set.

Figure 1 uses the Unified Modeling Language (UML) to illustrate the participants of this process:

Figure 1

The DB class uses the provided DSN to select which concrete subclass of DB_common it should return. The diagram shows two such implementations: DB_mysql and DB_sqlite. Notice the closed, empty arrow joining them to DB_common. This denotes inheritance.

Our client code doesn't know or care about this. It only knows that by calling connect() it will get a class of the DB_common type which is therefore guaranteed to support the query() and fetchrow() methods.

So, by focusing on the interface rather than the implementation when we use the DB package, we gain code that is independent of its wider context. Depending upon the DSN, which would be retrieved from an external source such as a configuration file, the code fragment will run with MySQL, SQLite, Oracle or one of any number of other database platforms.

The process of pulling embedded components away from their context is known as decoupling, and it is one of the great objectives of many patterns. With client code decoupled from the database implementation, it becomes easy to add new databases without the need for changes elsewhere.

Polymorphism can also help reduce duplication within and between your components. When you support multiple implementations within a component you may find that parallel conditionals creep into your code. Here's a simple example.

function getVenue($id) {

    
$query = "SELECT * FROM venue where id = $id";

    if (
$this->type == "mysql") {
        
$res = mysql_query($query);
        return
mysql_fetch_array($res);
    } else if (
$this->type == "sqlite") {
        
$res = sqlite_query($this->dbh,$query);
        return
sqlite_fetch_array($res);
    }
}

function
getAllVenues() {

    
$query = "SELECT * FROM venue";

    if (
$this->type == "mysql") {
        
$res = mysql_query($query);
        while (
$row = mysql_fetch_assoc($res)) {
            
$ret[] = $row;
        }
        return
$ret;
    } else if (
$this->type == "sqlite") {
        
$res = sqlite_query($this->dbh,$query);
        return
sqlite_fetch_all($res);
    }
}

These two methods are extracted from a class that presents venue data for a listings system. Note that as usual with sample code, error checking has been removed to improve clarity. It's clear from this example that at some point the author was told to support both MySQL and SQLite. He simply added a $type property, and used a conditional statement to determine which set of functions to call. This is barely acceptable in a single method. It becomes a real problem when two or more methods must implement similar conditionals as is the case here.

Looking at this sample, it is also clear that this conditional is likely to appear elsewhere in the system. So if the author needs to support a new database, he will be forced to make changes in many different places: a laborious job and a magnet for bugs.

Here we clean things up by coding to the DB_common interface:

function getVenue($id) {

    
$result = $this->db->query("SELECT * FROM venue where id = $id");

    return
$result->fetchRow(DB_FETCHMODE_ASSOC);
}

function
getAllVenues() {

    
$result = $this->db->query("SELECT * FROM venue");

    while (
$row = $result->fetchRow(DB_FETCHMODE_ASSOC)) {
        
$ret[] = $row;
    }
    return
$ret;
}

As you can see, the conditional has been eliminated. The decision as to which platform to support was made just once, when the DB_common implementation stored in the $db property was instantiated.

So, how do you go about coding to an interface? In simple terms, it's a matter of defining the key responsibilities of your classes. If you find that a class is trying to do too many things, and especially if you need a conditional to select between alternative strategies or contexts, then you should consider creating a new type and encapsulating the details of implementation behind its interface. You should avoid instantiating objects within classes where possible. Instead, have objects passed in to the constructor or other methods. Where you do accept an object as a method argument, try to ask for a parent type rather than a specific concrete implementation unless you really need that particular functionality. This allows you to pass in different implementations as needs and contexts change.

[ continued in part 2 of PHP Patterns: Introduction ]