v\:* {behavior:url(#default#VML);}
o\:* {behavior:url(#default#VML);}
w\:* {behavior:url(#default#VML);}
.shape {behavior:url(#default#VML);}
<!– /* Font Definitions */ @font-face {font-family:Courier; panose-1:2 7 4 9 2 2 5 2 4 4; mso-font-alt:”Courier New”; mso-font-charset:0; mso-generic-font-family:modern; mso-font-format:other; mso-font-pitch:fixed; mso-font-signature:3 0 0 0 1 0;} @font-face {font-family:Verdana; panose-1:2 11 6 4 3 5 4 4 2 4; mso-font-charset:0; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:536871559 0 0 0 415 0;} @font-face {font-family:”Lucida Console”; panose-1:2 11 6 9 4 5 4 2 2 4; mso-font-charset:0; mso-generic-font-family:modern; mso-font-pitch:fixed; mso-font-signature:-2147482993 6144 0 0 31 0;} /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:””; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:12.0pt; font-family:”Times New Roman”; mso-fareast-font-family:”Times New Roman”;} h1 {mso-margin-top-alt:auto; margin-right:0in; mso-margin-bottom-alt:auto; margin-left:0in; mso-pagination:widow-orphan; mso-outline-level:1; font-size:16.5pt; font-family:Arial; color:black; font-weight:bold;} p {mso-margin-top-alt:auto; margin-right:0in; mso-margin-bottom-alt:auto; margin-left:0in; mso-pagination:widow-orphan; font-size:12.0pt; font-family:”Times New Roman”; mso-fareast-font-family:”Times New Roman”;} code {mso-ansi-font-size:11.0pt; mso-bidi-font-size:11.0pt; font-family:Courier; mso-ascii-font-family:Courier; mso-fareast-font-family:”Times New Roman”; mso-hansi-font-family:Courier; mso-bidi-font-family:”Courier New”;} pre {margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; tab-stops:45.8pt 91.6pt 137.4pt 183.2pt 229.0pt 274.8pt 320.6pt 366.4pt 412.2pt 458.0pt 503.8pt 549.6pt 595.4pt 641.2pt 687.0pt 732.8pt; font-size:10.0pt; font-family:”Courier New”; mso-fareast-font-family:”Times New Roman”;} span.atitle1 {mso-style-name:atitle1; mso-ansi-font-size:19.0pt; mso-bidi-font-size:19.0pt; font-family:Arial; mso-ascii-font-family:Arial; mso-hansi-font-family:Arial; mso-bidi-font-family:Arial; font-weight:bold;} @page Section1 {size:8.5in 11.0in; margin:1.0in 1.25in 1.0in 1.25in; mso-header-margin:.5in; mso-footer-margin:.5in; mso-paper-source:0;} div.Section1 {page:Section1;} /* List Definitions */ @list l0 {mso-list-id:1565407075; mso-list-template-ids:26618656;} @list l0:level1 {mso-level-tab-stop:.5in; mso-level-number-position:left; text-indent:-.25in;} ol {margin-bottom:0in;} ul {margin-bottom:0in;} –>
/* Style Definitions */
table.MsoNormalTable
{mso-style-name:”Table Normal”;
mso-tstyle-rowband-size:0;
mso-tstyle-colband-size:0;
mso-style-noshow:yes;
mso-style-parent:””;
mso-padding-alt:0in 5.4pt 0in 5.4pt;
mso-para-margin:0in;
mso-para-margin-bottom:.0001pt;
mso-pagination:widow-orphan;
font-size:10.0pt;
font-family:”Times New Roman”;
mso-ansi-language:#0400;
mso-fareast-language:#0400;
mso-bidi-language:#0400;}
Design patterns were introduced to the software community in Design Patterns, by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (colloquially known as the “gang of four”). The core concept behind design patterns, presented in the introduction, was simple. Over their years of developing software, Gamma et al found certain patterns of solid design emerging, just as architects designing houses and buildings can develop templates for where a bathroom should be located or how a kitchen should be configured. Having those templates, or design patterns, means they can design better buildings more quickly. The same applies to software.
Design patterns not only present useful ways for developing robust software faster but also provide a way of encapsulating large ideas in friendly terms. For example, you can say you’re writing a messaging system to provide for loose coupling, or you can say you’re writing an observer, which is the name of that pattern.
It’s difficult to demonstrate the value of patterns using small examples. They often look like overkill because they really come into play in large code bases. This article can’t show huge applications, so you need to think about ways to apply the principles of the example — and not necessarily this exact code — in your larger applications. That’s not to say that you shouldn’t use patterns in small applications. Most good applications start small and become big, so there is no reason not to start with solid coding practices like these.
Now that you have a sense of what design patterns are and why they’re useful, it’s time to jump into five common patterns for PHP V5.
The factory pattern
Many of the design patterns in the original Design Patterns book encourage loose coupling. To understand this concept, it’s easiest to talk about a struggle that many developers go through in large systems. The problem occurs when you change one piece of code and watch as a cascade of breakage happens in other parts of the system — parts you thought were completely unrelated.
The problem is tight coupling. Functions and classes in one part of the system rely too heavily on behaviors and structures in other functions and classes in other parts of the system. You need a set of patterns that lets these classes talk with each other, but you don’t want to tie them together so heavily that they become interlocked.
In large systems, lots of code relies on a few key classes. Difficulties can arise when you need to change those classes. For example, suppose you have a User
class that reads from a file. You want to change it to a different class that reads from the database, but all the code references the original class that reads from a file. This is where the factory pattern comes in handy.
The factory pattern is a class that has some methods that create objects for you. Instead of using new
directly, you use the factory class to create objects. That way, if you want to change the types of objects created, you can change just the factory. All the code that uses the factory changes automatically.
Listing 1 shows an example of a factory class. The server side of the equation comes in two pieces: the database, and a set of PHP pages that let you add feeds, request the list of feeds, and get the article associated with a particular feed.
Listing 1. Factory1.php
<?php
interface IUser
{
function getName();
}
class User implements IUser
{
public function __construct( $id ) { }
public function getName()
{
return "Jack";
}
}
class UserFactory
{
public static function Create( $id )
{
return new User( $id );
}
}
$uo = UserFactory::Create( 1 );
echo( $uo->getName()."\n" );
?>
|
An interface called IUser
defines what a user object should do. The implementation of IUser
is called User
, and a factory class called UserFactory
creates IUser
objects. This relationship is shown as UML in Figure 1.
Figure 1. The factory class and its related IUser interface and user class
If you run this code on the command line using the php
interpreter, you get this result:
% php factory1.php
Jack
%
|
The test code asks the factory for a User
object and prints the result of the getName
method.
A variation of the factory pattern uses factory methods. These public static methods in the class construct objects of that type. This approach is useful when creating an object of this type is nontrivial. For example, suppose you need to first create the object and then set many attributes. This version of the factory pattern encapsulates that process in a single location so that the complex initialization code isn’t copied and pasted all over the code base.
Listing 2 shows an example of using factory methods.
Listing 2. Factory2.php
<?php
interface IUser
{
function getName();
}
class User implements IUser
{
public static function Load( $id )
{
return new User( $id );
}
public static function Create( )
{
return new User( null );
}
public function __construct( $id ) { }
public function getName()
{
return "Jack";
}
}
$uo = User::Load( 1 );
echo( $uo->getName()."\n" );
?>
|
This code is much simpler. It has only one interface, IUser
, and one class called User
that implements the interface. The User
class has two static methods that create the object. This relationship is shown in UML in Figure 2.
Figure 2. The IUser interface and the user class with factory methods
Running the script on the command line yields the same result as the code in Listing 1, as shown here:
% php factory2.php
Jack
%
|
As stated, sometimes such patterns can seem like overkill in small situations. Nevertheless, it’s still good to learn solid coding forms like these for use in any size of project.
The singleton pattern
Some application resources are exclusive in that there is one and only one of this type of resource. For example, the connection to a database through the database handle is exclusive. You want to share the database handle in an application because it’s an overhead to keep opening and closing connections, particularly during a single page fetch.
The singleton pattern covers this need. An object is a singleton if the application can include one and only one of that object at a time. The code in Listing 3 shows a database connection singleton in PHP V5.
Listing 3. Singleton.php
<?php
require_once("DB.php");
class DatabaseConnection
{
public static function get()
{
static $db = null;
if ( $db == null )
$db = new DatabaseConnection();
return $db;
}
private $_handle = null;
private function __construct()
{
$dsn = 'mysql://root:password@localhost/photos';
$this->_handle =& DB::Connect( $dsn, array() );
}
public function handle()
{
return $this->_handle;
}
}
print( "Handle = ".DatabaseConnection::get()->handle()."\n" );
print( "Handle = ".DatabaseConnection::get()->handle()."\n" );
?>
|
This code shows a single class called DatabaseConnection
. You can’t create your own DatabaseConnection
because the constructor is private. But you can get the one and only one DatabaseConnection
object using the static get
method. The UML for this code is shown in Figure 3.
Figure 3. The database connection singleton
The proof in the pudding is that the database handle returned by the handle
method is the same between two calls. You can see this by running the code on the command line.
% php singleton.php
Handle = Object id #3
Handle = Object id #3
%
|
The two handles returned are the same object. If you use the database connection singleton across the application, you reuse the same handle everywhere.
You could use a global variable to store the database handle, but that approach only works for small applications. In larger applications, avoid globals, and go with objects and methods to get access to resources.
The observer pattern
The observer pattern gives you another way to avoid tight coupling between components. This pattern is simple: One object makes itself observable by adding a method that allows another object, the observer, to register itself. When the observable object changes, it sends a message to the registered observers. What those observers do with that information isn’t relevant or important to the observable object. The result is a way for objects to talk with each other without necessarily understanding why.
A simple example is a list of users in a system. The code in Listing 4 shows a user list that sends out a message when users are added. This list is watched by a logging observer that puts out a message when a user is added.
Listing 4. Observer.php
<?php
interface IObserver
{
function onChanged( $sender, $args );
}
interface IObservable
{
function addObserver( $observer );
}
class UserList implements IObservable
{
private $_observers = array();
public function addCustomer( $name )
{
foreach( $this->_observers as $obs )
$obs->onChanged( $this, $name );
}
public function addObserver( $observer )
{
$this->_observers []= $observer;
}
}
class UserListLogger implements IObserver
{
public function onChanged( $sender, $args )
{
echo( "'$args' added to user list\n" );
}
}
$ul = new UserList();
$ul->addObserver( new UserListLogger() );
$ul->addCustomer( "Jack" );
?>
|
This code defines four elements: two interfaces and two classes. The IObservable
interface defines an object that can be observed, and the UserList
implements that interface to register itself as observable. The IObserver
list defines what it takes to be an observer, and the UserListLogger
implements that IObserver
interface. This is shown in the UML in Figure 4.
Figure 4. The observable user list and the user list event logger
If you run this on the command line, you see this output:
% php observer.php
'Jack' added to user list
%
|
The test code creates a UserList
and adds the UserListLogger
observer to it. Then the code adds a customer, and the UserListLogger
is notified of that change.
It’s critical to realize that the UserList
doesn’t know what the logger is going to do. There could be one or more listeners that do other things. For example, you may have an observer that sends a message to the new user, welcoming him to the system. The value of this approach is that the UserList
is ignorant of all the objects depending on it; it focuses on its job of maintaining the user list and sending out messages when the list changes.
This pattern isn’t limited to objects in memory. It’s the underpinning of the database-driven message queuing systems used in larger applications.
The chain-of-command pattern
Building on the loose-coupling theme, the chain-of-command pattern routes a message, command, request, or whatever you like through a set of handlers. Each handler decides for itself whether it can handle the request. If it can, the request is handled, and the process stops. You can add or remove handlers from the system without influencing other handlers. Listing 5 shows an example of this pattern.
Listing 5. Chain.php
<?php
interface ICommand
{
function onCommand( $name, $args );
}
class CommandChain
{
private $_commands = array();
public function addCommand( $cmd )
{
$this->_commands []= $cmd;
}
public function runCommand( $name, $args )
{
foreach( $this->_commands as $cmd )
{
if ( $cmd->onCommand( $name, $args ) )
return;
}
}
}
class UserCommand implements ICommand
{
public function onCommand( $name, $args )
{
if ( $name != 'addUser' ) return false;
echo( "UserCommand handling 'addUser'\n" );
return true;
}
}
class MailCommand implements ICommand
{
public function onCommand( $name, $args )
{
if ( $name != 'mail' ) return false;
echo( "MailCommand handling 'mail'\n" );
return true;
}
}
$cc = new CommandChain();
$cc->addCommand( new UserCommand() );
$cc->addCommand( new MailCommand() );
$cc->runCommand( 'addUser', null );
$cc->runCommand( 'mail', null );
?>
|
This code defines a CommandChain
class that maintains a list of ICommand
objects. Two classes implement the ICommand
interface — one that responds to requests for mail and another that responds to adding users. The UML is shows in Figure 5.
Figure 5. The command chain and its related commands
If you run the script, which contains some test code, you see the following output:
% php chain.php
UserCommand handling 'addUser'
MailCommand handling 'mail'
%
|
The code first creates a CommandChain
object and adds instances of the two command objects to it. It then runs two commands to see who responds to those commands. If the name of the command matches either UserCommand
or MailCommand
, the code falls through and nothing happens.
The chain-of-command pattern can be valuable in creating an extensible architecture for processing requests, which can be applied to many problems.
The strategy pattern
The last design pattern we will cover is the strategy pattern. In this pattern, algorithms are extracted from complex classes so they can be replaced easily. For example, the strategy pattern is an option if you want to change the way pages are ranked in a search engine. Think about a search engine in several parts — one that iterates through the pages, one that ranks each page, and another that orders the results based on the rank. In a complex example, all those parts would be in the same class. Using the strategy pattern, you take the ranking portion and put it into another class so you can change how pages are ranked without interfering with the rest of the search engine code.
As a simpler example, Listing 6 shows a user list class that provides a method for finding a set of users based on a plug-and-play set of strategies.
Listing 6. Strategy.php
<?php
interface IStrategy
{
function filter( $record );
}
class FindAfterStrategy implements IStrategy
{
private $_name;
public function __construct( $name )
{
$this->_name = $name;
}
public function filter( $record )
{
return strcmp( $this->_name, $record ) <= 0;
}
}
class RandomStrategy implements IStrategy
{
public function filter( $record )
{
return rand( 0, 1 ) >= 0.5;
}
}
class UserList
{
private $_list = array();
public function __construct( $names )
{
if ( $names != null )
{
foreach( $names as $name )
{
$this->_list []= $name;
}
}
}
public function add( $name )
{
$this->_list []= $name;
}
public function find( $filter )
{
$recs = array();
foreach( $this->_list as $user )
{
if ( $filter->filter( $user ) )
$recs []= $user;
}
return $recs;
}
}
$ul = new UserList( array( "Andy", "Jack", "Lori", "Megan" ) );
$f1 = $ul->find( new FindAfterStrategy( "J" ) );
print_r( $f1 );
$f2 = $ul->find( new RandomStrategy() );
print_r( $f2 );
?>
|
The UML for this code is shown in Figure 6.
Figure 6. The user list and the strategies for selecting users
The UserList
class is a wrapper around an array of names. It implements a find
method that takes one of several strategies for selecting a subset of those names. Those strategies are defined by the IStrategy
interface, which has two implementations: One chooses users randomly and the other chooses all the names after a specified name. When you run the test code, you get the following output:
% php strategy.php
Array
(
[0] => Jack
[1] => Lori
[2] => Megan
)
Array
(
[0] => Andy
[1] => Megan
)
%
|
The test code runs the same user lists against two strategies and shows the results. In the first case, the strategy looks for any name that sorts after J
, so you get Jack, Lori, and Megan. The second strategy picks names randomly and yields different results every time. In this case, the results are Andy and Megan.
The strategy pattern is great for complex data-management systems or data-processing systems that need a lot of flexibility in how data is filtered, searched, or processed.
Conclusions
These are just a few of the most common design patterns used in PHP applications. Many more are demonstrated in the Design Patterns book. Don’t be put off by the mystique of architecture. Patterns are great ideas you can use in any programming language and at any skill level.
Design Patterns in PHP – Factory Method and Abstract Factory
(Page 1 of 5 )
Normally, in object oriented programming, object creation is not difficult. But what if your object needs to be created based on different conditions or other matters of context? Then you will spend hours in debugging and updating–unless you know about design patterns. David Fells explains how they work, and uses the creation of a maze to illustrate his points.
Overview
In object oriented programming, object creation – also known as instantiation – is an implied requirement. Objects must at some point be created for use. Obviously, creating objects is not a difficult task and most languages, PHP included, have simple and intuitive syntax for doing so.
When developing larger, more complex systems though, object creation can become difficult. There are situations where different objects may need to be created based on different conditions or based on the context of the object creating it. Creating objects of concrete types explicitly in code can make these situations a nightmare when it comes time to make revisions and additions. When a new class is introduced, you get to follow a trail of code and commence the hours of debugging that will inevitably follow such an endeavor. This is where design patterns come in.
This article will discuss the usage of Factory Method [DP107] and Abstract Factory [DP87] as they pertain to developing applications in PHP using object oriented programming techniques.
Design Patterns in PHP – Factory Method and Abstract Factory – Factory Method
The first pattern used to simplify object instantiation is the Factory Method pattern. The Factory Method pattern defines an interface for object creation but defers the actual instantiation to subclasses. Take, for example, an application that processes Electronic Funds Transfers (ETFs). There are numerous types of ETFs including virtual check, credit card, wire transfer and so on. Using a non-pattern based approach, the application code requesting an ETF object would need to know precisely what subclass of ETF is needed, and it would need to know the context in which that type of ETF is requested. We would end up with code looking something like this:
switch ($etfType) {
case ETF_VIRTUALCHECK :
$etf = new VirtualCheck();
$etf->processCheck();
break;
case ETF_CREDITCARD :
$etf = new CreditCard();
$etf->chargeCard();
break;
case ETF_WIRETRANSFER :
$etf = new WireTransfer();
$etf->chargeCard();
break;
}
Any time we want to add another ETF, we would have to manually update this switch statement anywhere it appeared. We would also have to update any other conditional code that appears. The CreditCard class hinted at above offers the same problem as the ETF itself in that each credit card type (VISA, MasterCard, AMEX) has its own validation scheme and many types have different numerical formats. We would see a similar switch statement to determine what type of credit card we were dealing with either in the CreditCard class constructor or in the switch statement above creates the CreditCard object.
By implementing the Factory Method, we code our application so it only expects a class that conforms to an interface – that is, it has certain methods and properties that can be used to submit an ETF and check whether it failed or succeeded. This promotes loose coupling in the application because you are not binding a concrete subclass to application code. The result is a great increase in flexibility and maintainability. It is easy to add new ETF subclasses and implement them because you are not hard coding the application to expect a specific subclass, just a class with a specific interface. Look at this example.
class ETF {
var $data;
function ETF($data) {
$this->data = $data;
}
function process() {}
function getResult() {}
}
class VirtualCheck extends ETF {}
class WireTransfer extends ETF {}
class ETFFactory {
function createETF($data) {
switch ($data[‘etfType’]) {
case ETF_VIRTUALCHECK :
return new VirtualCheck($data);;
case ETF_WIRETRANSFER :
return new WireTransfer($data);
default :
return new ETF($data);
}
}
}
$data = $_POST;
$etf = ETFFactory::createETF($data);
$etf->process();
This is a crude implementation but the intent should be clear. Assume the contents of $_POST represent everything you need for the type of ETF that is happening, including a ‘etfType’ that says what sort of ETF you are using. This would come from the user making a selection in a form and filling out the correct information. This implementation provides numerous advantages over the first.
1. Data validation can be left entirely to the subclasses and occur without interaction from calling code.
2. Calling code only needs to know of one way to get an ETF object.
3. Creation logic is encapsulated – the ETFFactory decides what concrete class to create based on the contents of $data[‘etfType’]. Calling application code knows nothing of concrete subclasses of ETF.
4. If special measures need to be taken, such as creating a specific type of credit card object, this can take place in one location without the calling application code being involved.
Using this approach consolidates creation logic in a single class – ETFFactory. This eliminates duplication in code when an object is created in multiple locations. With the first method, if a class name is changed or a new ETF class is added, we have to modify the ETF creation code everywhere it appears in our application. With the Factory Method implementation, this is not the case. Our calling application code knows of only ETFFactory and ETF. Subclasses of ETF provide the required specialized behaviors to process themselves in the appropriate way but the calling code needs only to use the process() method.
Design Patterns in PHP – Factory Method and Abstract Factory – Abstract Factory
The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes [DP87]. This pattern takes the abstraction displayed in the example above to the next level by providing a common factory interface for a given family of objects. The code that actually uses the factory to create objects only expects an object that conforms to the interface of the abstract factory and does not know any details about concrete factory classes.
Using the example from Design Patterns, consider a game that creates a maze. We have a method that knows what to do to create a maze and it will instantiate all the objects we need to construct the maze – components such as rooms, doors and walls. The example below defines the classes involved – method code is left out deliberately since it is not important to this discussion.
class MapSite {
function enter() {}
}
define(‘North’, 0);
define(‘East’, 1);
define(‘South’, 2);
define(‘West’, 3);
class Room extends MapSite {
var $mapSite = array();
var $roomNumber;
function Room($roomNumber) {}
function getSide($direction) {}
function setSide($direction, $mapSite) {}
}
class Wall extends MapSite {
function Wall() {}
function enter() {}
}
class Door extends MapSite {
var $room1;
var $room2;
var $isOpen;
function Door() {}
function enter() {}
function otherSideFrom($room);
}
class Maze {
function Maze() {}
function addRoom($room) {}
// Etc…
}
class MazeGame {
function createMaze() {
$aMaze = new Maze();
$room1 = new Room(1);
$room2 = new Room(2);
$aDoor = new Door($room1, $room2);
$room1->setSide(North, new Wall());
$room1->setSide(East, $aDoor);
$room1->setSide(South, new Wall());
$room1->setSide(West, new Wall());
$room2->setSide(North, new Wall());
$room2->setSide(East, new Wall());
$room2->setSide(South, new Wall());
$room2->setSide(West, $aDoor);
$aMaze->addRoom($room1);
$aMaze->addRoom($room2);
}
}
In this example, we define a MapSite class that will act as a base class for anything that could apear in a maze, such as a door, a room, or a wall. We then define the constants North, East, South, and West to be used for tracking the sides or orientation of these MapSite objects. Following the constant definitions are the definitions for the Wall, Room and Door classes. These objects should be obvious in intent.
The setSide() method of the Room class expects the direction of the side and the object to be placed there – any object derived of the class MapSite. This would typically be a Wall or a Door, but it could support more objects easily. The constructor of the Door class expects two Room objects – the door must be aware of the rooms it is connecting. Next we define the Maze class, which is used to represent our actual maze object in code. We use the addRoom() method to attach rooms to the maze.
Finally, we look at the MazeGame class and its createMaze() method. The createMaze() method creates a maze object, two rooms and a door, then defines what objects occupy the sides of the two rooms and attaches them to our Maze object. At this point, we have successfully created a Maze and put some rooms into it.
Design Patterns in PHP – Factory Method and Abstract Factory – Drawbacks of the Example
Now that you understand the example case, we need to talk about the drawbacks with the implementation used in this example. The main roadblock with using this implementation is that the MazeGame object is hard coded to create specific classes of objects – that is, it creates Maze, Wall, Door and Room directly without use of any factory methods. We can improve this design by refactoring our createMaze method to use a MazeFactory object for object instantiation. Here is an example.
class MazeFactory {
function MazeFactory() {}
function createMaze() { return new Maze(); }
function createRoom($roomNumber) { return new Room($roomNumber); }
function createDoor($room1, $room2) { return new Door($room1, $room2); }
function createWall() { return new Wall(); }
}
class MazeGame {
function createMaze() {
$factory = new MazeFactory();
$aMaze = $factory->makeMaze();
$room1 = $factory->makeRoom(1);
$room2 = $factory->makeRoom(2);
$aDoor = $factory->makeDoor($room1, $room2);
$room1->setSide(North, $factory->makeWall());
$room1->setSide(East, $aDoor);
$room1->setSide(South, $factory->makeWall());
$room1->setSide(West, $factory->makeWall());
$room2->setSide(North, $factory->makeWall());
$room2->setSide(East, $factory->makeWall());
$room2->setSide(South, $factory->makeWall());
$room2->setSide(West, $aDoor);
$aMaze->addRoom($room1);
$aMaze->addRoom($room2);
}
}
This method is significantly better because it moves creational knowledge out of the createMaze() method and into the MazeFactory class. This will work fine if we only want to use one family of Maze objects, but what if we want to create subclasses of Wall, Door and Room to allow different behaviors? The examples given in Design Patterns use enchanted rooms and rooms with bombs as examples.
An enchanted room could have special behaviors, such as requiring conditions to be met before a door could be opened or closed. A room with a bomb would know what conditions caused a bomb to detonate and would track whether or not the bomb had gone off. If the bomb had gone off, it would keep up with damage to walls from the bomb. If we wanted to use these classes, we would have to parameterize our factory object to check some sort of input condition to know which family of objects to create.
This is where the Abstract Factory pattern comes in. This pattern uses the Factory Method pattern to handle actual object instantiation, but the value of the Abstract Factory pattern comes at a higher level. We code our calling code not only to use factory methods for object creation but to expect a factory object that conforms to a certain interface. This means that we can use different factories – all based on a single abstract factory interface – to create different families of objects. Calling code would only need to expect a class that derives from the original MazeFactory class.
For brevity’s sake we will not type out the code to define the subclasses of Room, Wall, and Door, but we will define the subclasses of the MazeFactory object that are used to create enchanted mazes and mazes with bombs in them.
class EnchantedMazeFactory extends MazeFactory {
function makeRoom($roomNumber) { return new EnchantedRoom($roomNumber); }
function makeDoor($room1, $room2) { return new EnchantedDoor($room1, $room2); }
}
class BombedMazeFactory extends MazeFactory {
function makeRoom($roomNumber) { return new RoomWithABomb($roomNumber); }
function makeWall() { return new BombedWall(); }
}
We can now use different concrete factory classes to create different families of products – in this case, different types of Doors, Walls, and Rooms. We are now left with one last problem – createMaze(), at last glance, is hard coded to create a MazeFactory object. Since createMaze() only needs to create the objects themselves through a standard factory interface, there is no need for the method to ever actually create the factory. We should pass the factory in as an argument to createMaze() and then let the method do its work.
class MazeGame {
function createMaze($factory) {
$aMaze = $factory->makeMaze();
$room1 = $factory->makeRoom(1);
$room2 = $factory->makeRoom(2);
$aDoor = $factory->makeDoor($room1, $room2);
$room1->setSide(North, $factory->makeWall());
$room1->setSide(East, $aDoor);
$room1->setSide(South, $factory->makeWall());
$room1->setSide(West, $factory->makeWall());
$room2->setSide(North, $factory->makeWall());
$room2->setSide(East, $factory->makeWall());
$room2->setSide(South, $factory->makeWall());
$room2->setSide(West, $aDoor);
$aMaze->addRoom($room1);
$aMaze->addRoom($room2);
}
}
Now in our createMaze() method, no assumptions are made about the type of factory we need. Some other code that is responsible for figuring out what type of factory to create would actually instantiate the MazeFactory and pass it to createMaze(), as in the following example.
$game = new MazeGame();
$game->createMaze(new EnchantedMazeFactory());
After all is said and done, we have created (with a little help from Design Patterns) a very flexible set of classes for producing mazes. It should be noted that in an actual implementation, createMaze() would be using data of some kind to determine what components are required in the maze and the method calls on the various MapSite objects would not be hard coded as they are in the example.
Design Patterns in PHP – Factory Method and Abstract Factory – Conclusion
The Factory Method and Abstract Factory patterns allow us to build a great deal of flexibility into our applications by abstracting the process of object instantiation and by helping consolidate creational knowledge in the appropriate classes. The Factory Method pattern teaches us how to use methods for object creation rather than directly instantiating objects through client code. This lets the factory method itself do any work it needs to do in creating the object – work that could involve contextual considerations or initializing certain resources in the object. The Abstract Factory class teaches us how to create groups of related objects with a common interface, creating client code that expects neither a specific type of object nor a specific type of factory.
One of the most important concepts in good object oriented design is “design to an interface, not an implementation.” What this means is that you should code your application to expect standard sets of object behaviors but not to expect specific object types. This results in a system of objects that know as little as possible about one another and, so long as the interfaces to these classes do not change, the internals of the classes can vary without adversely affecting other classes in the system.
In the final createMaze() example above, we see this in action on two levels. First, we pass any MazeFactory type object to the method, which is used to instantiate various MapSite objects and load them into a Maze. Second we are working with the Maze and MapSite objects without worrying about their specific class, just their interface – we know a Room needs a room number when we create it and we know we can enter a room. It does not matter to the client code that the room may be an EnchantedRoom or a RoomWithABomb – all it needs to know is how to create it and how to use it. This is the very definition of programming to an interface. The opposite case is true with the first maze example where createMaze() directly creates specific Room, Door, and Wall objects.
The use of these two patterns is a good starting point for programmers just beginning to learn about patterns and good object oriented design. This is the first in what will be a series of articles on design patterns with PHP examples and is intended for developers who are somewhat new to design patterns. Thanks for reading and please don’t forget to leave some feedback in the comments section!