Much of the literature on software design nowadays focuses on design patterns.
Design patterns are an attempt to make the principles of good object-oriented
design more explicit. Patterns are defined as "a recurrent solution to a problem." But
using them is not as simple as following a cookbook recipe. Applying a pattern can
be daunting, since the description in a book is usually somewhat abstract and you
have to figure out how exactly to use it in a situation that is different from the example
given in the book.
Not long ago, I was trying to get my son, age five, to ski. It started well: he saw his
older sister skiing down a mild slope, and immediately became eager to show us how
easily he could do the same thing.
He couldn’t, though. He insisted on putting his skis on in the middle of the slope,
and immediately fell flat on his back. I told him it was a nice try, but he disagreed.
He had simply lost all interest. I suggested we go lower where it was less challenging.
Instead he insisted on going to the top. I humored him and we went up. He had one
look down the slope and said, "It’s scary."
Of course it was scary. Of course he refused to try it.
I finally persuaded him to do it at the very bottom where the surface was almost
flat. Better than nothing, I told myself.
Afterward, I started to ponder the cognitive limitations of a five-year-old. At that age,
a child is capable of learning the skill. He’s OK with the "how," but the "why" is beyond
his ken. The idea that it will be more fun later if he takes the time to practice is meaningless
to him. So is the concept that there is some middle ground between scary (the
fear of falling) and boring (trudging across a flat field of snow as if wearing snowshoes).
This may seem like an odd introduction to design patterns, but the thing is, "why"
is an important question when applying patterns. You can learn how to implement
them, but if you don’t know what good they are and in what situations to apply them,
you may well do more harm than good by using them.
Much of the literature on software design nowadays focuses on design patterns.
Design patterns are an attempt to make the principles of good object-oriented
design more explicit. Patterns are defined as "a recurrent solution to a problem." But
using them is not as simple as following a cookbook recipe. Applying a pattern can
be daunting, since the description in a book is usually somewhat abstract and you
have to figure out how exactly to use it in a situation that is different from the example
given in the book.
As we’ve already hinted, an even greater challenge is discovering when you have the
problem that the pattern is supposed to solve. Unless your requirement is extremely
similar to an example you’ve seen, it’s seldom obvious. And there are lots of situations
in which you can use a design pattern but would be better off not doing it because you
don’t need the extra flexibility that the pattern provides. For instance, the book Design
Patterns [Gang of Four] describes a pattern called Command, which involves creating
an object-oriented class for each type of command in your program. So if you have
an Edit command, you write a class called EditCommand and when you want to run
the command, you instantiate the class and run a method that does whatever the command
is supposed to do:
$editcommand = new EditCommand;
$editcommand->execute();
But why? You don’t need a separate class just to execute a command. A simple function
will do. (Even in strict object-oriented languages such as Java, you don’t need a
class for each command, just a method.)
Then what’s the point? According to the book, the intent of the Command pattern
is to encapsulate a request as an object so that you can "parameterize clients with different
requests, queue or log requests, and support undoable operations." There are
other suggestions as well for when the Command pattern is applicable. But if you
don’t need to do <em>any </em>of those things, creating command objects probably won’t do you
any good. Unless using the pattern actually results in code that is simpler, has less
duplication, or is easier to understand, it may be better to steer clear of it.
Martin Fowler says, "I like to say that patterns are ‘half baked,’ meaning that you
always have to figure out how to apply it to your circumstances. Every time I use a
pattern I tweak it a little here and there." The converse is also often the case. If I’ve
developed a design, partly by designing it first and partly by refactoring it, I often
find that it can be described by a pattern, or several patterns, without matching any
of them exactly.
The problem with many applications of design patterns is that the designers
haven’t taken the time to compare the design with one without the pattern or with
one that uses a different pattern.
In this chapter, we will look at some of the more basic design patterns, primarily
from the book Design Patterns [Gang of Four]. The selection of patterns is necessarily
somewhat arbitrary. Whole books have been written about patterns, so it’s impossible
to cover them all. The ones we will see in this chapter are Strategy, Adapter, Decorator,
Null Object, Iterator, and Composite. Several others will be covered in later chapters.
7.1 STRATEGY
The Strategy pattern is crucial, perhaps the most crucial pattern in modern objectoriented
design. It’s about creating pluggable, replaceable, reusable components. One
example of this is the Template object described in the section on the single-responsibility
principle in the previous chapter. If we pass the File and TemplateData objects
into the constructor as suggested, we are getting close to a Strategy pattern.
For a more complete, yet still simplistic, example of the Strategy pattern, let’s
implement a basic example from earlier chapters using this pattern. This is a simplistic
example, and the Strategy pattern is overkill in this case. But the example shows how
the Strategy pattern is implemented and how it can be an alternative to implementation
inheritance. We’ll study the basic mechanics using "Hello world." The example
is too simple to be meaningful in the real world, so in addition we’ll discuss its usefulness
in real situations.
The Strategy pattern will also recur in many contexts in later chapters.
7.1.1 "Hello world" using Strategy
Figure 7.1 shows the class diagram for the example shown in chapter 2. The parent
class, HtmlDocument, implements the generic features represented by the start and
end tags of the HTML document. The HelloWorld child class implements the specific
features, represented by the actual content of the document. So to generate
something other than a greeting, say an announcement, we can add another child
class that generates the content of an announcement.
We can move the getContents() method to a Strategy object instead. Instead
of using a subclass of HtmlDocument, we can use HtmlDocument configured with
a Strategy object instead. In UML, this looks like Figure 7.2.
This may look impressive; it’s hard to tell from the UML diagram that it represents
totally unnecessary complexity. We are just using it to make sure we understand
the mechanical aspects of the pattern. HtmlContentStrategy might as well be
an abstract class, but I’ve defined it as an interface to make it clear that it doesn’t
need to contain any working code. This means that there is no implementation
inheritance left in the design.
But what does it look like in code? The HtmlDocument class still generates the
start and end of the document. But rather than get the content from a method that’s
implemented in a subclass, it gets it from the Strategy object.
class HtmlDocument {
private $strategy;
public function __construct($strategy) {
$this->strategy = $strategy;
}
public function getHtml() {
return "<html><body>".$this->strategy->getContents().
"</body></html>";
}
}
We want to be able to plug different Strategy objects into the HtmlDocument object.
So the HtmlDocument object needs a consistent way to call the Strategy object. In
other words, it needs a consistent interface, which is defined by an interface.
interface HtmlContentStrategy {
public function __construct($name);
public function getContents();
}
Now any HtmlDocument object will be able to use any Strategy object that
implements this interface, since all it requires is the ability to call the getContents() method.
But wait a minute. What about the constructor? The interface defines that, too.
The Strategy object for generating the "Hello world" message needs the world name
as an argument to the constructor. Are we sure that other Strategy objects for generating
HTML content will also need the same thing? I’m afraid not; in fact, I fear that
they will need all sorts of other information to do their jobs.
What do we do about that? It’s simple; we just eliminate the constructor from the
interface. Since the HtmlDocument class doesn’t instantiate the Strategy class, all
objects that implement the interface can be used even if their constructors differ. So
the interface just needs the getContents() method:
interface HtmlContentStrategy {
public function getContents();
}
Now we can implement the "Hello world" feature as a Strategy class:
class HelloWorldStrategy implements HtmlContentStrategy {
var $world;
public function __construct($world ) {
$this->world = $world ;
}
public function getContents() {
return "Hello ".$this->world ."!";
}
}
What this class does is trivial, but the pattern is extremely useful in more complex
situations.
7.1.2 How Strategy is useful
Using Strategy in place of implementation inheritance is the way to create pluggable
components and is useful in implementing the open-closed principle.
The most important reason for this is the fact that parent and child classes are
highly coupled. They depend on each other in ways that are not necessarily obvious.
An object that belongs to a class hierarchy can call a method from any class in the hierarchy
(unless it is a private method) simply by using $this. And $this gives no clue
as to which of the classes in the hierarchy the method belongs to.
Contrast this with the situation in which an object holds a reference to an object
that is not part of an inheritance hierarchy. Let’s say we have a User object that contains
an Address object. In a method in the user object, we can call a method on
$this or $this->address. In either case, it is clear which class the method
belongs to. And unless we give the User object a reference to the Address object, the
Address object is unable to call methods belonging to the User object (except by creating
a new User object). So we have a one-way dependency; this makes it much more
likely that we can reuse the Address class in another context. This means that the
classes are much easier to disentangle than a parent and a child class that may use each
other’s methods freely.
This shows why there is high coupling, but this high coupling can also be convenient,
since it’s easy to use all those methods.
Strategy can be used in so many different situations that it is almost impossible
to narrow its range of application. It can be applied to express almost any difference
in behavior.
While Strategy is about pluggable behavior for a class, the next pattern—Adapter—is about changing the interface of an existing class to make it pluggable in
a different context than its original one.
7.2 ADAPTER
The Adapter pattern is typically used to retrofit a class with an altered API. You may
need a different API to make it compatible with another, existing class. Or perhaps
the original API is too cumbersome and hard to use.
An Adapter is extra complexity, so if you can, it might be better to refactor the original
class so it gets the API you want in the first place. But there might be good reasons
why you can’t or don’t want to do that. Two of the reasons may be
The class is already in use by many clients, so changing its interface will require
changing all the clients.
The class is part of some third-party software, so it’s not practical to change it.
You can, of course, change open-source software, but that means you’re in trouble
when the next version arrives.
In an ideal world, you might get to design everything for yourself and redesign it
when necessary. Then you would rarely need Adapters, if ever. But in the real world,
they become necessary because of constraints such as these.
In this section, we’ll start with an extremely simple example, moving from there
to an example showing how to adapt real template engines. Then we’ll see an even
more advanced example involving multiple classes. Finally, we’ll discuss what to do if
we need compatibility between several different interfaces so that a more generic interface
is required.
7.2.1 Adapter for beginners
Sometimes all you need to do when creating an Adapter is change the names of methods.
This is easy. If we have a template class with the method assign()and we want
the name set() instead, we can use a simple Adapter that just delegates all the work
to the template class.
Take our "simplest-possible template engine" example, the Template class from
the previous chapter. It has the methods set() and asHtml(). What if we want to
use the names Smarty uses instead: assign() and fetch()? The example in
Listing 7.1 shows how this can be done.
To use this class, all we have to do is wrap the template object in the adapter by passing
it in the constructor:
$template = new SimpleTemplateAdapter(new Template('test.php'));
$template now uses the Smarty method names, but it does not work quite like a
Smarty object, since it’s still defined as a template rather than a template engine. In the
next section, we will see how to overcome this more challenging, conceptual difference.
7.2.2 Making one template engine look like another
For a more realistic example, let’s use two template engines: Smarty and PHPTAL.
Smarty is perhaps the most widely-known and popular template engine. PHPTAL is
interesting and different. We’ll discuss that further in chapter 13; for now, we’re just
looking at the possibilities of the Adapter pattern, and these two template engines are
different enough to make it a challenge.
In particular, the two template engines are conceptually different in their design.
PHPTAL uses a template object that is constructed with a specific template file. So you
set the template first, add the variables you want inserted into the HTML output, and
then execute it:
$template = new PHPTAL_Template('message.html');
$template->set('message','Hello world');
echo $template->execute();
A Smarty object is a different kind of animal: it’s not a template; it’s an instance of the
template engine. After you’ve created the Smarty object, you can hand it any template
file for processing.
The conceptual difference creates a difference in sequence. With PHPTAL, you
specify the template file first and then you set the variables; with Smarty, it’s the other
way around:
$smarty = new Smarty;
$smarty->assign('message','Hello world');
$smarty->display('message.tpl');
Imagine that our site is currently based on Smarty, but we want to change it to PHPTAL.
In order to avoid having to rewrite all the PHP code that uses the templates, we
want the templates to still appear to the PHP code as Smarty templates, so we can
leave the code that uses them mostly unchanged. In other words, the Smarty interface
is the one we want to keep using, even though the actual templates are PHPTAL templates.
So the Adapter class will give the PHPTAL template engine a Smarty "skin."
With one exception, the methods we’ll write are the most basic ones needed to display
a simple HTML page based on a template. If we need more methods, we can add
them later.
We’ll start by defining the PHPTAL template interface formally. As always in PHP,
declaring the interface is not strictly needed, but it gives us a useful overview of what
we’re doing.
interface SmartyTemplateInterface {
public function fetch($template);
public function display($template);
public function assign($name,$value);
public function get_template_vars();
}
The Adapter reflects the conceptual differences between the two template engines. A
Smarty object requires no constructor arguments, so we can skip the constructor in
this class. The PHPTAL_Template object has to be constructed, but it demands the
template file name in the constructor. Since the Smarty interface does not supply the
file name until we generate the output using fetch() or display(), we have to
wait until then before constructing the PHPTAL template object. Listing 7.2 shows
the Adapter class.
Since we don’t create the PHPTAL object before it’s time to generate the output,
we have to store the variables in the meantime. This is done using the Smartycompatible
assign() method. We keep the variable in the $vars array
belonging to the Adapter.
It’s only when the fetch() method is called that we have the template file name
available. So now we can create the PHPTAL_Template object. Since the Smarty and
the PHPTAL templates normally have different file extensions, we convert from one
(.tpl) to the other (.html). Now we can copy the variables from the Adapter class
to the template. PHPTAL has a convenient setAll() method to do this. Since we
now have both the template filename and the variables set, we can generate the output
by using PHPTAL’s execute() method.
get_template_vars() is Smarty’s way of retrieving a variable that has been set
in the Smarty object. We emulate its behavior by returning a specific variable if its
name has been specified, or the whole array of variables if it hasn’t.
PHPTAL has no display() method, but it’s trivial to implement by echoing the
output from the fetch() method.
7.2.3 Adapters with multiple classes
Sometimes we have to do even more tricks to get an adapter to work. If the API we’re
emulating uses more than one class, we may have to emulate all of them. One example
is the opposite process of the one we just did. If we want to give a Smarty template
a PHPTAL skin, we run into a different kind of challenge: The PHPTAL
template class has no way of retrieving the variables you’ve set in it. Instead, you have
to get an object called a Context from the template object and get the variables from
that object:
This might not be a problem in normal use of the template engine, but if we have
used the Context object (testing is a likely use for it), we might want it in the adapter
interface.
Let’s see how we can do that. Here is the PHPTAL interface:
interface PhptalTemplateInterface {
public function set($name,$value);
public function execute();
public function getContext();
}
Now for the Adapter itself. Listing 7.3 shows how the Adapter uses a Smarty object
internally to do the actual work, while appearing from the outside as a PHPTAL template
with limited functionality.
Figure 7.3 is a class diagram showing the structure of the design. The interface, the
PhptalSkin class, and the PhptalContext class all belong to the adapter, but all the real
work is done by the humble Smarty class.
In the real world, the Smarty class is not so humble. This example is simplified to
utilize only a few basic methods of the PHPTAL interface and the Smarty object. We
have shown only two methods of the around 40 methods of the Smarty object. In
practice, we would be likely to implement more of them, although in most projects
there is no good reason to implement more than we actually need.
Listing 7.3 shows how the PhptalSkin class is implemented.
The Smarty object is the Smarty template engine, the object that’s going to do the
real work. The PHPTAL interface requires that we specify the template file when we
construct the template object. Since the Smarty object does not store the name of the
template file, we have to keep it in the Adapter. The file name conversion is the
inverse of the file name conversion from the previous Adapter.
Since a PHPTAL template returns a PHPTAL_Context object, the adapter needs an
object that does a similar job without being an actual PHPTAL_Context object. For
this purpose, we use a PhptalSkinContext object. We’ll take a look at the class in a
moment. It is just a simple variable container, and for now, all we need to know about
it is that we can store variables in it with a set() method.
The execute() method calls Smarty’s equivalent, the fetch() method. Since the
fetch() method requires the template name (or more specifically a template
resource), we give it the template name that was supplied to the constructor.
While we’re at it, let’s change the way the assign() method works to make it more
secure. All output should be escaped to prevent cross-site scripting (XSS) attacks. With
Smarty, this means you either have to escape the strings before adding them to the template
or explicitly use Smarty’s escaping features. The problem is that the escaping in this
example is primitive and not applicable to anything beyond simple values. It would have
to be made much more sophisticated to allow it to work in existing applications. The
subject of template security will be discussed further in chapter 13.
The set() method sets the corresponding variable in the Smarty object. It also sets
the variable in the context object so that it can be retrieved in the PHPTAL fashion.
An alternative way to implement this would be to store the variables in the Adapter
and to copy all of them into the Context or Smarty object when they’re needed. The
current solution duplicates the data, but there is no reason right now why that should
cause problems, so there is probably little practical difference between the alternatives.
We can use the getContext() method to return the PhptalSkinContext object, so
that we can retrieve the variables in the same way as with a real PHPTAL_Context
object.
Listing 7.4 shows the PhptalSkinContext class. This is just a thin wrapper around a
PHP array.
The class has a subset of the interface of PHPTAL_Context class. get() retrieves a
single variable; getHash() retrieves all of them.
7.2.4 Adapting to a generic interface
You may ask, why not use inheritance? Why not let the Adapter be a child class of
Smarty or PHPTAL? In fact, the Gang of Four book indicates this as an option. The
effect of letting the first of our Adapters inherit from the Smarty class will be to allow
the use of any Smarty method that’s not in the PHPTAL interface. The Adapter’s
interface then becomes a somewhat messy mixture of Smarty and PHPTAL methods.
But if we’re switching to Smarty anyway, that might be just as well. Developers could
gradually switch to using the Smarty interface.
But there is one more consideration: in Uncle Bob’s terminology, we’ve now taken
the first bullet. We were cruising happily along, using PHPTAL templates for all our
web pages, and suddenly someone hits us with the requirement to use Smarty instead.
We know now that a certain kind of change can happen: switching template engines.
And if it happens once, it could happen again. So what we probably want to do is to
protect the system from further changes of the same type. The way to go in this case
would be to move toward a generic template interface, which would not be identical
to either the PHPTAL interface or the Smarty interface. The generic interface should
be as easy as possible to adapt to a new template engine. In other words, it should be
easy to write an Adapter that has the generic interface and delegates the real work to
the new template engine.
So far, we have at least some indication of what’s needed for a generic Template-
Adapter interface. It will need to have an interface that re-creates the functionality of
both the PHPTAL and the Smarty objects. We don’t want to have to use fancy tricks
such as the Context object. So the interface should have a method to get variables. It
should also have a display() method. And the need to convert the template name
is a tricky thing that needs to be smoothed over. If we assume that the template only
needs a single template file name in some form, the generic interface might just require
the file name without the extension and add the extension automatically.
Adapter is a pattern that works by wrapping an object in another. A Decorator also
does that, but for a different purpose.
7.3 DECORATOR
Adapters are the tortillas of object-oriented programming. You wrap an object in an
Adapter, and it looks completely different but tastes almost the same. Decorator is
another kind of wrapper, but the intent is not to change the interface. Instead, a Decorator
changes the way an object works—somewhat—but leaves its appearance relatively
intact. So it’s more like sprinkling salt on the dish: the result tastes slightly
different, but looks similar.
But technically, what Adapters and Decorators do is mostly the same: you wrap the
decorator around another object. A term that has been used to describe this principle
is Handle-Body. There is a "Handle" object that wraps a "Body" object.
For an example, we’ll use a so-called Resource Decorator for a database connection.
Then we’ll discuss how to make sure we can add multiple Decorators to an object.
7.3.1 Resource Decorator
For an example, let’s try a Resource Decorator [Nock]. This is typically used to add
extra behavior to a database connection. Let’s say we’re dissatisfied with the way PEAR
DB handles errors. We want to use PHP 5 exceptions instead. One way to achieve
that is to wrap the PEAR DB connection in a class that generates the exceptions. We’ll
start with a simple example using only one decorator (see Listing 7.5).
The constructor accepts a PEAR DB object as an argument. This means that we can
create our decorated connection as follows:
$connection = new PearExceptionDecorator(DB::Connect(
'mysql://user:password@localhost/webdatabase'));
Passing the "Body" object in the constructor is typical of decorators, but in this simple
case, it would work even if we instantiated the PEAR DB connection inside the
constructor.
The query() method calls the PEAR DB connection’s query() method and
throws an exception if there is an error (an SQL syntax error, for example).
The nextID() method just delegates to the PEAR DB object. This method is really
just one example of many methods that are available from the PEAR DB object that
we don’t need to change. To get the decorated object to work like the original object,
we might want to implement a lot of these delegating methods.
In this case, there are at least two benefits to using a Decorator. One is that we can’t
simply change the PEAR package to add this feature to it. (Strictly speaking, we can
change it, since it’s open source, but then we have to maintain it afterward, and that’s
not worth the trouble.) The other is that our way of handling exceptions is more likely
to change than the PEAR package. The PEAR package is relatively stable; it has to be,
because it has lots of users. The Decorator might change because we need a different
kind of error handling. Perhaps we want to use exceptions in a somewhat more sophisticated
way, using a more specific exception class, for instance. Perhaps we want compatibility
with PHP 4. We could have a similar decorator that would work in PHP 4,
using some error handling or logging capability that is not exception based, and just
swap the decorators depending on the PHP version.
7.3.2 Decorating and redecorating
The previous example is the simplest form of a decorator. The more advanced thing
to do is to decorate and "redecorate." Since the decorated object works in a way that’s
similar to the original object, you can apply more than one Decorator to add different
responsibilities. For example, if we had a Decorator to add logging to the connection,
we could do something like this.
$connection = new PearLoggingDecorator(
new PearExceptionDecorator(
DB::Connect('mysql://user:password@localhost/webdatabase')));
But what if we have a lot of delegating methods—such as the nextID() method in
the Decorator we’ve just seen? We don’t want to duplicate all those in both Decorators.
So we’ll make a parent class to keep the delegating methods in (see Listing 7.6).
As in the previous example, a practical version of the class is likely to contain many
more delegating methods.
Any decorator for a PEAR DB object can now be derived from the abstract parent
class. We need only override the methods we want to change. Figure 7.4 shows this
simple inheritance hierarchy. The parent class is abstract, but its methods are not. Any
method that is not implemented in a child Decorator will work like the method in the
decorated object. The Logger class is just a helper for the logging Decorator.
Therefore, the PearExceptionDecorator no longer needs the nextID() method
or any other method it doesn’t add anything to. This is shown in Listing 7.7.
Now we can implement the logging Decorator using the same procedure. What we
want to log will depend on the circumstances, but for the example, let’s log every
query. Perhaps we would want to do that while our application is in the testing stages.
When it becomes stable, we can remove the Decorator. A more conventional alternative
would be to disable logging; the advantage of the Decorator is that we can get rid
of the logging code entirely so it doesn’t clutter the application.
Listing 7.8 shows the logging Decorator.
We are using the PEAR Log package. In the constructor, we store a logger object in an
instance variable. When we call the query() method on the decorated connection,
it logs the SQL statement as a notice before executing the query.
While Figure 7.4 illustrates the relationships between the classes, the configuration of
objects at runtime is something else. This is shown in the UML object diagram in
Figure 7.5. The colons (:PearLoggingDecorator) indicate that we are dealing with
objects—instances of the named classes—rather than with the classes as such.
The PearLoggingDecorator uses the PearExceptionDecorator, which uses the
PEAR DB object. The query() call is passed from the top to the bottom of this chain,
and the results are passed back up.
NOTE: There is no deeper meaning to the words "top" and "bottom," "up" and
"down" in this context. They just refer to the placement of the objects in
the diagram. This placement is arbitrary.
The decorators are set up in an order that seems logical, but if we swapped the two
decorators, it would still work, and we might not notice the difference.
From a pattern skeptic point of view, we may ask some critical questions when a
Decorator is suggested. Is the decorator really needed? Do the component and the
Decorator really need to be separate, or can they be merged into one class? You might
want to keep them separate because the Decorator’s behavior is not always needed, or
to comply with the single-responsibility principle: if the decorator’s behavior is likely
to change for different reasons than the component’s. Resource Decorators may be
considered an example of this: the software that handles the database might change,
but it’s probably more stable than what you are adding to it.
Strategy is for changing and replacing behavior. Decorator is a way to add behavior.
When we want to stop a behavior from happening, we can either write a plain old conditional
statement or use a Null Object.
WinConnections Conference Fall 2008 Don’t miss the premier event for Microsoft IT Professionals in Las Vegas, November 10-13. Register and book your room by August 25 and receive a FREE room night (based on a three night minimum stay).
Master SharePoint with 3 eLearning Seminars Learn how to build a better SharePoint infrastructure and enable powerful collaboration with MVPs Dan Holme and Michael Noel. Register today!
SharePointConnections Conference Fall 2008 Don’t miss the premier event for Microsoft IT Professionals in Las Vegas, November 10-13. Register and book your room by August 25 and receive a FREE room night (based on a three night minimum stay).
VMworld 2008 - Sign Up Today! Join your peers on September 15-18 at The Venetian Hotel in Las Vegas as VMware hosts VMworld 2008, the leading Virtualization event.
Microsoft® Tech•Ed EMEA 2008 IT Professionals Advance your thinking with new ideas and practical real-world solutions at Microsoft’s FIVE day technical infrastructure conference 3-7 Nov., 2008. Register before 26 September 2008 to save €300.
Order Your Fundamentals CD Today! Gain an introduction to Exchange, learn server security requirements, and understand how unified communications can play a role in your messaging strategies with this free Exchange CD.
Are You Really Compliant with Software Regulations? View this web seminar that will help you with compliance best practices and check out a management solution to assure that you won’t be in jeopardy of an audit.
Virtualization Congress Oct. 14-16 in London Don't miss Virtualization Congress, the premiere EMEA conference dedicated to hardware, OS and application virtualization. Oct. 14-16 in London.