Thursday, March 25, 2010

Adapter Pattern vs Proxy Pattern

These are two similar patterns that both wrap an object (or set of objects) to expose slightly different behavior to a client object. As with the difference between Bridge and Strategy patterns, the difference between an Adapter and a Proxy is partly one of focus; which pattern you use does not necessarily change the structure of your solution, but the choice depends on how you approach the problem. Both patterns are wrappers, but you use the two patterns for very different reasons.

Some of the confusion that I've seen between these two patterns is one of nomenclature. I was talking to a friend today (hence the reason for this post) where he got into an argument with someone, and the basic cause of confusion was between the idea of a class (any class) as a 'proxy' for a data object and the Proxy pattern itself. As for the Adapter pattern and the verb 'adapt' -- well, every line of code is an adaptation, right? It's a pretty generic verb.

So: Most of the confusion is proxy vs Proxy. I'm using the latter, the capitalization, to indicate the Design Pattern.

Adapter

Common descriptions of the Adapter pattern tend to say that it's an extension of the Facade pattern to deal with polymorphism or to provide a specific interface. Yeah, that's nice. In practice, when I'm getting my hands dirty writing code, I use the Adapter pattern when I am approaching the problem from the client end - as the user of the class that's about to be adapted, not as the author of the library or utility class that winds up getting adapted.

Usually what happens is that I'm writing a bit of code and I start to implement an algorithm when I realize that I can use a certain library class to do the job instead. But the class I'm working in demands a certain interface! Rather than rewriting the algorithm using that interface, I instead glue the library class in. And that, really, is all that an Adapter is: it's an implementation of an interface that doesn't do any of the work itself -- it just passes the calls along to some other, existing class.

It retrospect, you can see that you have Adapted the library class to the new interface.

Proxy

Proxies, on the other hand, tend to do work. Real work. Usually I create a proxy knowing exactly what it is that I want to do. I need a client-side representation of a server object, or a class to represent some big, unwieldy system object that I'm really only going to provide some minor methods on (and hence don't need to instantiate the whole thing). My proxy looks like the real object, but only provides the methods needed locally -- and so only has the complexity to do that limited work.

Often Proxies are one-to-one wrappers; you'll have two classes to represent the object. One class (the non-proxy, usually written first) represents the underlying data, and the Proxy wraps that class to add some specific, narrow behavior.

A proxy might exist across a network connection; it might represent a view into a data object, or a partial instance of a large data file, or maybe it just provides some functions that would be expensive or complex to access directly. I often write proxies to do one simple thing to a more complex class. Instead of making some class more complex, the Proxy steps in.

Which is Which?

In some cases the distinction is obvious. In a client-server application, you might write a client Proxy for a server-side object. You might gate calls to a complex central system through a Proxy, which will provide transaction ordering or synchronous behavior or somesuch. If you need to shove Tab A into Slot B and a simple pass-through class will do, that's an Adapter.

But say you want to write a class to encapsulate OS-level calls to the file system. Is that an Adapter or a Proxy? It's pretty simple, like a pass-through, so it's an Adapter, right? But an object of that class is treated like the underlying object, so is it a Proxy for the actual file on disk?

This is one of the cases where normal pattern use breaks down. Such a class is both a proxy and an adapter. A file object is a proxy for the actual file, but the class itself adapts the OS library calls to your custom project/application/module.

And so it doesn't really matter what you call it. The confusion comes because 'proxy' can be used to refer to the pattern itself (in which a class does work to hide complexity or extract specific behavior) or to an object that represents some real data object. One might say that every data-object instance in your application is a proxy. The concept of proxy still holds independent of the jargon that us OO design-pattern wonks use. Any class can be a proxy - even an Adapter class.

But note that you wouldn't normally have a Proxy that 'adapts' to anything. The Proxy pattern might 'adapt' something, but if the class you write is an Adapter, it might be a proxy but it won't be a Proxy.

As I said above, when I write an Adapter, I know it; the problem in front of me is fairly clear: "I need to shove this old/library class into this custom interface." And when I write a Proxy, it's clear that I'm providing specific new behavior.

No comments: