Proxies occur naturally all over the place, for example in politics we elect someone to represent us in government because we cannot all be there. So it naturally follows that we would use the same pattern in software design. Here is an easy example: we have a class that implements an interface and submits a credit-card charge to a service provider. The interface provides what the client code expects, and the class adapts that to the provider. So this is actually an adapter pattern:
But what happens if the provider is offline? Well, we could change the adapter class so that it detects the problem and stores the transaction to process later. But that makes the class responsible for both interfacing to the service provider and caching transactions when the provider is not available. We would really like to stick as close as possible to Robert Martin's "Single Responsibility Principle" and separate responsibilities to easily manage them (Martin).
The solution is to provide an invisible man-in-the-middle to deal with the caching if the service provider is not available. This class needs to look just like the adapter so the client can use it, and it needs to have an instance of the adapter that it uses to process the credit card charges. It proxies the adapter for the client and adds the caching functionality! Notice the adapter returns true or false so the proxy can decide if it needs to cache or not:
The discussion of proxy in the Gang of Four book distills down to this structure (Gamma):
Our example has followed their diagram to a tee: the proxy is dependent on the card service provider adapter and the proxy has the responsibility of instantiating it. But what will happen when management tells us to change service providers? Clearly we need to add an adapter, but do we need to rewrite the proxy? Or add a new type of proxy?
My friend Robert Mills used a diagram in his course book at Verizon that subtly modifies the pattern, one that he adapted from Craig Larman (Mills, Larman). Bob defined it alongside of some other application stuff he used in his examples, so I removed some of the fluff here to show just the difference between this and the GoF structure:
What is the big difference? Well two differences to be precise. Here the proxy class not only implements the same interface as the subject, it also references an object of that same interface. And instead of the proxy instantiating the subject it is injected into the proxy with constructor injection. So with this subtle change in our diagram the same proxy class can stand in for any adapter:
In figure 5 we need someone to decide which adapter to use and inject it into the proxy, so we gave that responsibility to the service provider factory. It is reasonable that the selection of the adapter and the selection of a proxy (if necessary) are cohesive responsibilities. And if a proxy really does need to depend on a specific adapter, then we can always revert to the GoF and structure by typing the injection of the subject to one specific type.
References
See the references page.
No comments:
Post a Comment