O'Reilly logo

Learning PHP Design Patterns by William Sanders

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. Using UMLs with Design Patterns

Reality seems valueless by comparison with the dreams of fevered imaginations; reality is therefore abandoned.

Emile Durkheim

The earliest phase of social formations found in historical as well as in contemporary social structures is this: a relatively small circle firmly closed against neighboring, strange, or in some way antagonistic circles.

Georg Simmel

Neither the life of an individual nor the history of a society can be understood without understanding both.

C. Wright Mills

Why Unified Modeling Language (UML)?

In their book, Design Patterns: Elements of Reusable Object-Oriented Software, Gamma, Helm, Johnson, and Vlissides (Addison-Wesley) employ a UML that may have slight variations from 1990s era UML standards, as well as contemporary UML 2.0 standards. However, those variations are slight, and by employing the GoF version of a UML, the reader will be able to compare the design patterns using PHP in this book with the Gang of Four’s original work. So whatever arguments may exist about the best UML for programming, you will find strict consistency between the UML in this book and the original work by Gamma and his associates.

If you are not familiar with UMLs, you will need patience. As you work with design patterns, they will become clearer in the context of the design and specific usage with PHP. Because of the minimum data typing used in PHP, implementing a design directly from a UML is very difficult until you are able to make the adjustments for data type and understanding the pattern itself.

Class Diagrams

Previous chapters displayed some simple class diagrams, and this section shows more details of class diagrams and how to use them with design patterns. In general, design pattern class diagrams display relations and communication between participants (classes and interfaces). For example, Figure 4-1 shows a typical class diagram of a design pattern. In this particular diagram, you will find five participants:

  • Client (implied)

  • Creator

  • ConcreteCreator

  • Product

  • ConcreteProduct

Each of the participants is a class or interface (abstract class or interface). The names of interfaces are in italics whereas concrete classes are in bold roman text. Note that the Client is shown in a light gray box. That means that it is implied in the pattern, and in the case of the Factory Method pattern, the Client class is not part of the pattern.

Class diagram (Factory Method)
Figure 4-1. Class diagram (Factory Method)

The Client class is often an integral part of design patterns; sometimes it is implied, and sometimes it is absent from the diagram altogether. Clients make requests from the main program, and the arrows help to show the different relations between the Client and the main part of the program. Often, programmers use the name “main” to indicate the client, but with design patterns, the “main” terminology is misleading. The main part of the program is in the interrelated participants and not the client.

Figure 4-2 is the same pattern minus the pseudocode annotations to provide a clearer focus on the fundamental elements in the pattern.

Class diagram (Factory Method without annotations)
Figure 4-2. Class diagram (Factory Method without annotations)

The Factory Method pattern instantiates objects through a factory (Creator) to separate the instantiation process from the requestor. The client wants (requests) a product of some sort, but instead of instantiating it directly from the ConcreteProduct, it is directed (as seen in the class diagram) to make the request through the Creator. Without class diagrams, knowing where to direct the client to make a request would be ambiguous, as would all participant relations. Class diagrams provide a view of the design pattern so that you can quickly see the relationships illustrated in the UML. The exact nature of relationships is specific, and the following sections help break down the symbols used for the pattern participants and the connections.

Participant Symbols

The Gang of Four refer to the classes and interfaces that make up a design pattern as participants. The name of the participant is in boldface. Interfaces (interfaces and abstract classes) are italicized. Some participants are shown with a line beneath the name with a list of key methods. Abstract methods are italicized, and concrete methods are in normal type face. Figure 4-3 shows a close look at the Creator interface.

Elements in an interface participant
Figure 4-3. Elements in an interface participant

In concrete implementations of interfaces, the abstract methods are concrete as well. Figure 4-4 shows the implementation of the Creator interface.

Concrete class and method from abstract class or interface
Figure 4-4. Concrete class and method from abstract class or interface

Note that the method, called AnOperation() in the interface, is not included in the concrete class. That’s because it has been implemented already and inherited by the ConcreteCreator. However, the FactoryMethod() must be implemented because it was an abstract method in the parent class.

A final notation associated with participants is the pseudocode annotations. Figure 4-5 shows a clearer view of the annotations removed from the Factory Method pattern.

Pseudocode annotations
Figure 4-5. Pseudocode annotations

Pseudocode annotations provide further information about the structure of the design pattern. They are designed to give the developer a better sense of different code expectations with participants.

Relationship Notations

Pseudocode annotations provide further information about the structure of the design pattern. They are designed to give the developer a better sense of different code expectations with participants (see Figure 4-6).

Class relationships
Figure 4-6. Class relationships

The associations and their meanings may vary in different renditions of the UML and this collage is a somewhat simplified UML of the relations that can be found in Chapter 1 of Design Patterns: Elements of Reusable Object-Oriented Software, where you can find more details if desired. In case you wish to use additional design patterns with PHP not covered in this book, you will be familiar with the UML used by GoF.

Acquaintance Relations

The basic and possibly the most common relationship between participants in a design pattern is the acquaintance. An acquaintance relationship is where one participant holds a reference to another participant. It is represented by a simple arrow between the Client and ISubject and the Proxy and RealSubject, as shown in a Proxy design pattern in Figure 4-7.

Association relations between Client and ISubject and between Proxy and RealSubject
Figure 4-7. Association relations between Client and ISubject and between Proxy and RealSubject

In order to see a concrete example of such an association, the following listing of the Proxy class shows what an association would look like in PHP:

<?php
class Proxy extends ISubject
{
    private $realSubject;

    protected function request()
    {
        $this->realSubject=new RealSubject();
        $this->realSubject->request();
    }

    public function login($un,$pw)
    {
        //Evaluates password etc.
        if($un==="Professional" && $pw==="acp")
        {
            $this->request();
        }
        else
        {
            print "Cry 'Havoc,' and let slip the dogs of war!";
        }
    }
}
?>

Generally, when one class holds a reference to another class, it would merely need to have a declaration. In a strongly typed language like C# where declarations include the data type, the “association” reference would look like the following:

private RealSubject realSubject;

As you can see in the Proxy listing, there’s also a declaration for a private variable with the same name:

private $realSubject;

The problem is that the variable can be instantiated as any data type. So, in order to hold a reference, the variable either has to be extended from a concrete instantiation of the variable, or it must be instantiated in the class where it is declared. In this case, it is instantiated:

$this->realSubject=new RealSubject();

So now, the Proxy class holds a reference to the RealSubject class and has an acquaintance relation with it.

Aggregation Relationship

The Gang of Four point out that no single code set can demonstrate what an aggregate relationship looks like, so keep in mind that the example used here simply shows what an implemented design pattern (Strategy) using aggregation looks like. In some respects, an aggregate relation is like an acquaintance relation but stronger. As Gamma et al. note, aggregation implies that an aggregate object and its owner have identical lifetimes. That is something like heart and lungs. As long as the heart keeps pumping blood, the lungs can move oxygenated blood throughout the system. Each is independent as an operating organ, but if one stops, so will the other.

The Strategy design pattern includes an aggregate relationship between the Context class and the Strategy interface, as shown in Figure 4-8.

Strategy class diagram with aggregate relationship between Context class and Strategy interface
Figure 4-8. Strategy class diagram with aggregate relationship between Context class and Strategy interface

Now, look at a PHP listing of a Context class:

<?php

class Context
{
    private $strategy;

    public function __construct(IStrategy $strategy)
    {
        $this->strategy = $strategy;
    }

    public function algorithm($elements)
    {
        $this->strategy->algorithm($elements);
    }
}

?>

The code hint reference is to the IStrategy interface. (The interface name is IStrategy of the Strategy participant in the design pattern.) In this way, you can see that the Context class holds a reference to the Strategy interface through code hinting and without having to instantiate an implementation of IStrategy in the Context participant (class). The Client class will have to provide a concrete strategy implementation when instantiating an instance of the Context class. For example, a client making a request would include a ConcreteStrategyA:

<?php
interface IStrategy
{
    public function algorithm($elements);
}

?>

As can be seen in the simple IStrategy interface, it has a single method, algorithm(). However, the Context class appears to have implemented the algorithm() method. Actually, the Context class is configured with a concrete implementation of IStrategy through the constructor function. The important feature of the relationship is to note how the two objects form an aggregation and how they have identical lifetimes.

Of all the relations, the aggregate relation is the most difficult to explain and understand because of its many variations. Rather than attempt further explanation at this time and risk oversimplifying and misrepresenting it, as you go through the design patterns in the book that use this aggregation, its specific use will be explained.

Inheritance and Implementation Relations

The Gang of Four uses the same notation for inheritance and implementation: a triangle. Figure 4-9 shows an abstract class and interface with child classes.

Inheritance and implementation use the same triangle notation
Figure 4-9. Inheritance and implementation use the same triangle notation

Single inheritance or implementation uses a single triangle (triangle-on-a-stick), whereas indications of multiple inheritance or implementation display the triangle lying flat on the common connecting lines to the concrete classes.

You may wonder why GoF chose to use the same triangle symbol for both inheritance and implementation. In part, it is because virtually all design patterns that use inheritance from a class do so from an abstract class. Abstract classes imply implementation of the abstract methods, so whether interfaces or abstract classes are employed, implementation is part of the relationship. In many places in their book, GoF use abstract classes and interfaces interchangeably (referring to them simply as interfaces) because both include interfaces used by the child classes. Likewise, in many design patterns, it does not matter whether abstract classes or interfaces are used in the pattern. In one implementation of the design pattern, the programmer will use an abstract class, and in another implementation of the same design pattern, interfaces are used.

Creates Relations

When one object creates an instance of another in a design pattern, the notation is a dashed line with an arrow pointing to the object that has been instantiated. You will see this kind of relationship in patterns that use factories such as the Factory Method (see Chapter 5) or Abstract Factory design patterns, but you will encounter it in some other patterns as well. Figure 4-10 shows a class diagram of the Factory Method with the ConcreteCreator using a factoryMethod() to create instances of ConProductA and ConProductB. The Client holds references to both the Creator and Product interfaces so that it can specify the exact product request.

Creates relations between a concrete creator and concrete products
Figure 4-10. Creates relations between a concrete creator and concrete products

In the discussion of acquaintance relationships between classes using PHP, in some cases it may be necessary to instantiate a class to hold reference to another class or interface. Such instantiation is due to creating a data type to hold the reference and not for using the instantiated object. Where this is necessary in PHP, do not use the dashed line. The relation is acquaintance, and even though an instance is created, it is only for establishing the acquaintance.

Multiple Relations

Sometimes you will see a class diagram where the arrows in acquaintance or aggregation relations have a ball at the end of the arrows, as in Figure 4-11.

Black ball indicates multiple objects referenced
Figure 4-11. Black ball indicates multiple objects referenced

The black ball means that multiple objects are being referenced or aggregated. The Flyweight pattern is used to reference multiple instances of shared objects or different objects.

Object Diagrams

Unlike a class diagram that shows the classes, object diagrams show only the instances and arrows indicating the object referenced. The naming convention employed is a class instance name headed by the lowercase letter a. So, an instance of a class named Client would be aClient. The text in the lower half of the object diagram modules is the name of the target object or a method.

A good example of using an object diagram can be found in the Chain of Responsibility design pattern. The pattern is used where more than a single object may handle a request, such as a help desk application. The client issues a request and then a handler interface passes on the request until a concrete handler is found to deal with the request. Figure 4-12 illustrates the chain.

A Chain of Responsibility pattern passes a request along a chain of handlers
Figure 4-12. A Chain of Responsibility pattern passes a request along a chain of handlers

In Figure 4-12, the request is passed from A to B to C and so on until the request can be handled and terminates to search for a handler. Figure 4-13 shows the same process in an object diagram.

Object diagram of Chain of Responsibility pattern
Figure 4-13. Object diagram of Chain of Responsibility pattern

Object diagrams provide another way of viewing a specific implementation of a design pattern and the relations between the objects generated through the pattern. They can help clarify object relations.

Interaction Diagrams

The final diagram GoF uses is one to trace the order in which requests are executed. These interaction diagrams are like object diagrams in the naming of objects, but their timeline is vertical—from top to bottom. Solid lines with arrowheads point to the direction of the request, and dashed lines with arrowheads indicate instantiation. Figure 4-14 shows the same Chain of Responsibility sequence as Figure 4-13, but in an interaction diagram. (The gray time direction arrow and time labels have been added but do not appear in the actual diagrams.)

Interaction diagram of Chain of Responsibility pattern
Figure 4-14. Interaction diagram of Chain of Responsibility pattern

The vertical rectangles indicate the time at which the object is active. As can be seen in Figure 4-14, the client object is active the entire time, the first concrete handler becomes active with the request from the client, and the second concrete handler does not become active until the request is passed from the first concrete handler.

The Role of Diagrams and Notations in Object-Oriented Programming

Before going on to examine the first design pattern and implementing it in PHP, I want to pause a moment and look at what we’re trying to do with diagrams and notations. Just in case we forget, diagrams and the notations associated with them are explaining, examining, and building design patterns. The extent to which they are useful in assisting us, we can employ them. However, the second they are not useful, they should be abandoned and new ones created.

For years, the only programming diagram I knew was the old flowchart. It looks fine, and while I rarely used them for program planning or as guides for writing a program, I understood their use. However, in looking at them now, I realize they might be very bad tools for object-oriented programming. Flowcharts are guides to sequential programming. In contrast, the UMLs developed for OOP look and feel different. They break up a program into modules called classes, and notations show relations and interactions.

Tools for UMLs

If you look, you can probably find tools that will help draw UMLs, but I’d recommend against it. In Unified Modeling Language User Guide, 2nd Edition, when discussing automated UML tools, Grady Booch points out the following:

Given automated support for any notation, one of the things that tools can do is help bad designers create ghastly designs much more quickly than they ever could without them.

If you treat the UMLs as aids for thinking, you’ll probably be better off as far as your code designs are concerned and understand design patterns a lot better. For example, Figure 4-15 shows the kind of diagram (Strategy design pattern) I’ve used with PHP projects to good effect.

Sketching out design on paper
Figure 4-15. Sketching out design on paper

As I start working on the project, I’ll make more sketches with more details, annotations, comments, and bits of code. I am forced to think while I sketch out the pattern and its details. With larger projects and groups of programmers, some kind of UML drawing tools may be handy for communication between programmers for the sake of clarity. At this time, though, pencil and paper stand as my main UML tools (I like to use the discarded pages from my cartoon-a-day calendar).

Other UMLs

As far as UMLs go, rather than thinking of “standards,” think of how they can be usefully employed. Booch suggests a diagram taxonomy divided into Structure and Behavior Diagrams, with Interaction Diagrams being a major subtype of the behavior category. Of these, I have found statecharts to be especially useful for thinking about designs using state machines—the State design pattern in particular. The statecharts help the developer focus on states, change of states, triggers, and transitions. So while statecharts are not part of the UML set GoF uses, they can be handy for thinking through problems that involve state machines like those in State design patterns. (See Chapter 10, The State Design Pattern, for examples of statecharts.)

For getting started with design patterns, besides becoming familiar with the notations, learn about what the notations mean. For example, knowing the meaning of aggregation is more important than knowing it is represented by a line with a diamond on its tail and an arrowhead on its head. Further, if you start using the design pattern UML, you can better frame programming problems as having design pattern solutions. By using a pencil and a piece of paper, your thought process will be better connected than if you use automated tools, and you’ll have a better chance of understanding design patterns.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required