Design patterns

  Windows IT Pro
Windows IT Library
  - Advertise        
Windows IT Pro Logo

  Home  |   Books  |   Chapters  |   Topics  |   Authors  |   Book Reviews  |   Whitepapers  |   About Us  |   Contact Us  |   ITTV  |   IT Jobs

search for  on    power search   help
 






Design patterns
View the book table of contents
Author: Dagfinn Reiersol
Published: June 2007
Copyright: 2007
Publisher: Manning Publications
 


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.



  1. 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.


  2. 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.


  3. 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.


  4. 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:
$context = $template->getContext();
$message = $context->get('message');
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.



  1. 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.


  2. 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.


  3. 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.


  4. 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).



  1. 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.


  2. 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).


  3. 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.



Page: 1, 2

next page



ADS BY GOOGLE SPONSORED LINKS FEATURED LINKS

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).

Maximize your SharePoint Investment – 8 Cities
Discover best practices and tips for both architecting and administering SharePoint. Early Bird Price of $99 through Sept 15th.

Find a new job now on the all new IT Job Hound!
Search jobs, post your resume, and set up job e-mail alerts!

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!

Top Tools for Virtualization Disaster Recovery & Replication
View this web seminar on August 14th to learn about two tools that will result in faster backup and restore with P2V disaster recovery.

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.



When managing just VMware isn’t enough
Plan/Manage/Secure – NetIQ VMware management. Download whitepaper.

What’s up with your network? Find out with ipMonitor
Availability monitoring for servers, applications and networks – FREE trial

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.
Windows IT Pro Home Register FAQ for Windows WinInfo News
Europe Edition About Us Contact Us/Customer Service Media Kit Affiliates / Licensing  
SQL Server Magazine Office & SharePoint Pro Windows Dev Pro IT Job Hound ITTV
IT Library Technical Resources Directory Connected Home Windows Excavator Windows SuperSite 
 
 Windows IT Pro is a Division of Penton Media Inc.
 Copyright © 2008 Penton Media, Inc., All rights reserved. Terms and Use | Privacy Statement | Reprints and Licensing