TheProxyPattern
From Pattern Repository Wiki
Contents |
The Proxy Pattern
Contextual Forces
Motivation
We wish to add an optional behavior to an existing class. This new behavior may protect the class, or log access to it, or delay its behavior or instantiation, or any other single additional behavior. We also need to be able to use the existing class at times without this additional behavior.
Encapsulation
Whether the additional behavior is in place or not at any given point at runtime is hidden from the client... We may also wish to hide the fact that an optional behavior exists at all. Because the additional behavior is located in an additional object, the client is actually dealing with two objects when the proxy is in place, but only one when it is not. This difference is encapsulated as well (a limited form of the encapsulation of cardinality).
Procedural Analog
A simple condition to include or exclude an optional behavior in the midst of non-optional behavior
//Non-optional behavior here if(condition) { // Optional Behavior here } //Non-optional behavior here
Non-Software Analog
I use a hand-held hose to water my garden. A few times a year, I add a container between the hose fitting and the watering head, which puts plant food into the water as it passes through.
I take no different action when watering in this way, and yet there is additional behavior; feeding the plants. Also, various different plant foods could be introduced in this way (although only one at any given time), and again I would take no different action at watering time.
Implementation Forces
Example
public abstract class TargetAbstraction { public abstract ret m(par p); { public class BaseBehavior extends TargetAbstraction { public ret m(par p) { // un-proxied, or “Base” behavior } } public class Proxy extends TargetAbstraction { private BaseBehavior myBaseBehavior; public Proxy(BaseBehavior aBaseBehavior) { myBaseBehavior = aBaseBehavior; } public ret m(par p) { // Can add behavior before the base behavior myBaseBehavior.m(p); // Can add behavior after the base behavior return ret; } }
Questions, concerns, credibility checks
- The Proxy and the BaseBehavior must have the same interface, so the client does not have to take any additional or different action when the Proxy is in place.
- If the BaseBehavior pre-exists, how will we get existing clients to use the Proxy, when appropriate, instead of the BaseBehavior?
- How will the Proxy get the reference to the Base Behavior? In the code example, the reference is passed in, but an object factory or other encapsulating entity can be used as well.
Options in implementation
The form shown above uses delegation from the proxy to the BaseBehavior class to reuse the pre-existing behavior. Another form of the Proxy (called the class Proxy) uses inheritance instead:
There are a number of different proxies, named for the behavior they add to class being proxied. Only some of them are possible with the class proxy form, but all of them are possible with the form that uses delegation.
Some examples:
Logging Proxy: Logs all calls to the method of the original class, either before or after the base behavior, or both.
Protection Proxy: Block access to one or more methods in the original class. When those methods are called, the Protection Proxy may return a null, or a default value, or throw an exception, etc…
Remote Proxy: The Proxy resides in the same process space as the client object, but the original class does not. Hence, the Proxy contains the networking, piping, or other logic required to access the original object across the barrier. This cannot be accomplished with a class proxy.
Virtual Proxy: The Proxy delays the instantiation of the original object until its behavior is called for. If its behavior is never called for, then the original class is never instantiated. This is useful when the original class is heavyweight and/or there are a large number of instances needed. The Virtual Proxy is very lightweight. This cannot be accomplished with a class proxy.
Cache Proxy: The Proxy adds caching behavior to an object that represents a data source.
Etc…
Consequent Forces
Testing issues
The Proxy is specifically designed to delegate to the original class for all the base behavior, and thus the test should test this delegation, as well as the correct addition of behavior to the base behavior. This can be accomplished by replacing the base or original class with a Mock object:
Cost-Benefit (gain-loss)
- Proxies promote strong cohesion.
- Proxies simplify the client object and the object being proxied (by hiding complex issues like remoting and caching, etc…)
- If the instantiation of all classes is encapsulated by policy, inserting a proxy at a later time is significantly easier.
- Proxies often evolve into Decorators when multiple additional behaviors are needed. Knowing this, one does not have to introduce the Decorator until it is needed, avoiding overdesign and analysis paralysis.





