Friday, January 15, 2010

Practical Php Patterns: Adapter

This post starts the Structural patterns part of the Practical Php Pattern series.

Structural patterns are concerned with the structure of the object graph, and influence the hierarchy of subclassing, and where to introduce interfaces and associations.
The first structural pattern we will touch is the Adapter pattern: an Adapter converts an already existent Adaptee class which presents a not usable (maybe implicit) interface to another Target interface, which other Clients components are ready to use.
This pattern is often used to integrate different parts of an application, because it magically makes talk to each other classes which were strangers. You can consider also to modify the classes to improve their relationship, but only if you own them and no backward compatibility is required; the main problem resides in not introducing unnecessary coupling.



In php, wrapping functions with a class can be considered an Adapter's implementation. Consider the Zend Framework codebase: it is full of adapters like Zend_Cache backends (which are wrappers) and authentication and database adapters.
The full object-oriented case is also present: Zend_Paginator_Adapter_DbSelect is an Adapter for Zend_Paginator (Client) which swallows a Zend_Db_Select instance (Adaptee). Including a dependency on the Paginator would be a source of bloat and coupling for the Zend_Db component, so do not consider an Adapter only as a patch to make different libraries work together: its use is often mandtory even in the same application, to connect areas of different concerns.

Participants
  • Client: makes use of an object which implements the Target interface.
  • Target: point of extension for the Client's module.
  • Adaptee: object of a different module or library.
  • Adapter: implementation of Target that forwards real work to the Adaptee, whilst totally hiding him.
This is a code sample where the Adaptee is not a class, which is very common in php given the procedural approach of the majority of its extensions.
<?php
/**
 * The Target interface: a small Collection, since php lacks a native one.
 * The bigger the interface, the greater the hassle while writing Adapters.
 */
interface Collection extends IteratorAggregate
{
    public function add($element);

    /**
     * @return boolean
     */
    public function contains($element);

    public function remove($element);
}

/**
 * The Adaptee is not a class but an array.
 * This is the Adapter. Adapters for procedural structures are common in php,
 * much more than for classes. If it composed an object instead of an array
 * it would still be an implementation of the Adapter pattern.
 */
class ArrayAdapter implements Collection
{
    private $_array;

    public function __construct(array $array)
    {
        $this->_array = $array;
    }

    public function add($element)
    {
        $this->_array[] = $element;
    }

    /**
     * @return boolean
     */
    public function contains($element)
    {
        return in_array($element, $this->_array);
    }

    public function remove($element)
    {
        $index = array_search($element, $this->_array);
        if ($index !== false) {
            unset($this->_array[$index]);
        }
    }

    public function getIterator()
    {
        return new ArrayIterator($this->_array);
    }
}

/**
 * The Client. It would love to print an array, but it only
 * accepts Collections.
 */
class Printer
{
    public function printAndRemove(Collection $c)
    {
        foreach ($c as $index => $element) {
            $c->remove($element);
            printf("| %.6s => %.6s |", $index, $element);
        }
        echo "\n";
    }
}

// the Adaptee
$array = array('foo', 'bar', 'baz');
$adapter = new ArrayAdapter($array);
$printer = new Printer();
$printer->printAndRemove($adapter);
var_dump($adapter->contains('foo'));
In php, there is no private inheritance, so a class adapter (which subclasses the Adaptee) must not be considered as the resulting code would reflect a Decorator pattern instead; an object adapter is the mainstream solution, where the Adapter composes the Adaptee.
There is also a choice between leaving abstract methods on the Client, that the Adapter will override so that it will be used directly, and a cleaner solution which favors object composition. The latter, which is presented in the example and in the diagram, consists in having the Adapter classes conform to a narrow and cohesive interface, which is passed to the Client on the stack or during construction. The Target should be kept short, since having twenty-six operations to implement is usually an inconvenience for the developer who has to write an Adapter.

6 comments:

fqqdk said...

For your last paragraph: you frequently use the Adapter pattern to adapt away 3rd party APIs from your code, so that you don't have to mock up said API in the tests. Mocking up large interfaces is hard work, so that's another argument to reinforce the notion of keeping Target small and cohesive.

Giorgio said...

The difference may be subtle, but I usually think of 3rd party libraries as wrapped in a Facade instead of a Adapter.

Dell Sala said...

What's your take on the difference between the Adapter and Strategy patterns? My understanding is that they are very similar, and the main difference is really intent. A strategy is for swapping in different kinds of behavior. The adapter is the same basic idea but it's purpose is specifically for wrapping an implementation that is already close to what you need, but just needs its interface tweaked.

I bring this up because of your choice to use Zend_Paginator as an example. It's something that has bothered me about the naming in many Zend Framework components. Why is Zend_Paginator_Adapter_Interface named an "adapter"? For it to be an adapter it should adapt something. The implementations of the Zend_Paginator_Adapter_Interface are full-fleged implementations of different paginating strategies, not just wrappers around existing paginating implementations (i.e. 3rd party paginators). Are these not just examples of the Strategy pattern?

Someone else asks the same question.

P.S. I just discovered your blog a couple months ago and it has become one of my favorite sources for high-quality writing on software development with php. Keep up the great work!

Giorgio said...

I think they can be considered different Strategies but, that said, some of them fall in the Adapter's intent.
Consider the Zend_Paginator_Adapter_Iterator: it is really a wrapper around an Iterator to modify its interface. Most of the Adapter-named classes in Zend_Framework uses other components' classes to achieve their goal, and it seems to me they are better named as Adapters. Strategies were originally thought as Flyweight objects, not as full-fledged objects with collaborators.

Anonymous said...

What a great resource!

Anonymous said...

Got it! Thanks!

ShareThis