Design Pattern Evangelist Blog

Smart pointers about software design

Proxy Design Pattern

Place administrative wrapper objects around objects often to help manage their complexity or resources.


Ambassador as a Proxy

Introduction

The Proxy Design Pattern is the first design pattern of the Composable Design Patterns series.

Sometimes objects need additional administration care beyond their core functionality. The uncaring designer will shift that burden onto the client developer possibly clearing his/her own conscious by describing what needs to be done for proper administration in documentation.

There are a few potential problems with relying upon documentation alone:

If the developer knows enough to describe administrative care in the documentation, then the developer may know enough to provide an implementation solution as well.

The Proxy Design Pattern is a way to add an administrative wrapper layer around basic functionality, so that the client developer won’t have to deal with it. It’s akin to a diplomatic ambassador who’s the proxy for the government he or she represents.

Structure

I lauded the flexibility one gets with Composable Design Patterns. We’re not going to quite see that flexibility with Proxy.

I mostly include Proxy with the Composable patterns since it’s a steppingstone toward the concept of composability and flexibility that we’ll eventually see.

Here is my UML Class Diagram rendition of the Gang of Four (GoF) diagram provided in their Design Pattern book:

Proxy via GoF

I have a few issues with the GoF’s diagram as shown above:

Here’s my updated UML Class Diagram to address some of my issues with their version:

Proxy via with my UML diagram

The instantiated client will have a feature reference to a Proxy instance, which will have a feature to a ConcreteFeature instance. When client makes a call to feature.execute(), Proxy’s execute() will run any code before calling its feature.execute(). When the Proxy instance calls feature.execute(), ConcreteFeature’s execute() will run its core code. It will return to Proxy which will run any additional optional code, and then it will return back to client.

This diagram is a more detailed example of what I provide previously in Composable Design Patterns as template for the Composable patterns:

Composable Pattern Template

Lazy Initialization

Administrative care is probably the most common context for the Proxy Design Pattern, but it’s not necessarily the only one.

Imagine you have a class that requires significant resources when instantiated. You may not wish to instantiate an object until you’re ready to invoke it. This is Lazy Initialization — a type of administrative care.

Lazy Initialization allows a developer to manage resource allocation with more nuance, but it requires additional coding overhead. The Proxy Design Pattern allows us to move the Lazy Initialization implementation into a separate proxy class so that the client developer won’t have to implement that additional overhead.

Here’s a UML class diagram updated specifically for a Lazy Initialization Proxy:

Proxy with Lazy Initialization

Proxy will be a lightweight object until it’s executed. If the Client flow never executes it, then a ConcreteFeature is never instantiated. The Client no longer needs to be concerned about the administration of ConcreteFeature. Proxy will manage it, even if Client does not know that Proxy exists. Its lazy initialization needs only be implemented once, and it will be consistently applied.

Use Case: Proxy for Memory Management

I have a beef with several things in the GoF Design Patterns book.

Lack of Encapsulation with Creational Design Pattern Method Names

The GoF were obsessed with encapsulation. They emphasized that client code should only know about interfaces. The client code should not call new() directly, because that would require their direct knowledge of the concrete class.

They defined several creational design patterns to encapsulate new() from the client code. However, most of their creational design pattern method names imply the creation design pattern being used. The public method names reflect the creation mechanism design rather than the client developer’s needs. While the concrete classes are encapsulated, the underlying creational mechanism may not be.

This isn’t a major issue until the designer decides that a different creational design pattern might be a better choice. Then what? Change the existing method name to match the new creational pattern and break the API for any existing client code? Maintain the existing method name and imply a creation mechanism that’s no longer applicable? Neither choice is ideal.

Let’s return to API principles. An API should be designed from the client’s point of view, not the designers. Therefore, design the Creational API as one should for any APIs. The client code doesn’t care about the creational design pattern or its implementation details. That’s the concern of the designer. The client code only desire is to acquire an object instance.

I started to use one consistent method name, acquire(), for all of my creational method names, regardless of the actual creational mechanism being used. Additional creational methods can still be added for different creational cases, but they should still be named from the client’s context, for example: acquireByName(Name name).

The Sin of Omission

I was a C++ developer when I learned the design patterns in 2004. Memory management was always a major concern with C++. The GoF don’t address memory management in their design pattern book, and C++ is one of their two demonstration languages. Some of their C++ examples leak memory.

Some creation mechanisms required memory clean up, whereas others did not. How would the client code know when to delete memory or not, especially with my creational agnostic acquire() method name, which provided no clues as to whether the returned object should be deleted or not?

Is memory management really the responsibility for the client code, especially when the client code doesn’t know how the memory was allocated in the first place? I don’t think it is. I had done some CORBA development in C++ a few years before learning the design patterns. CORBA thrusts a lot of mysterious memory management upon developers. I was never quite sure if I was releasing memory correctly or not. I always resented it, especially when I learned at the department holiday party that my recent CORBA memory management update was causing the application to crash in production every 2 hours or so.

From: The Rise and Fall of CORBA:

Another problem area is the C++ language mapping. The mapping is difficult to use and contains many pitfalls that lead to bugs, particularly with respect to thread safety, exception safety, and memory management. A number of other examples of overly complex and poorly designed APIs can be found in the CORBA specification, such as the naming, trading, and notification services, all of which provide APIs that are error-prone and difficult to use.

How can client code correctly manage memory when it doesn’t even know the mechanism that acquired that acquired that memory in the first place?

I returned to API principles once more. Consider this from the client’s point of view. What does the client code know, and what can it do? The client code knows when it’s done with an object. It may not know how to manage the memory or any other resources being used by the object, but it can let another entity with resource management knowledge know that it is releasing its claim to that object.

Much in the same way that there’s a creational method that acquires an instance, I introduced a matching method that allows the client code to release it. The API might look something like this:

class Factory {
    public static Feature acquire() {
        // Acquire a Feature instance using any creational mechanism desired.
    }

    public static void release(Feature feature) {
        // Perform any memory management associated with the released object that’s consistent with the creational mechanism.
    }
}

Here’s a UML class diagram of how this could be designed:

Factory with acquire() and release()

I don’t trust other developers. Heck, I don’t trust myself.

This works, except for one minor problem - the technique cannot be enforced, and it is not a common practice. Developers have to remember to call release(Feature feature) consistently. That’s not going to happen. I doubt that I’d even remember to call it consistently.

Even if developers take every effort to call release(…) consistently, there’s still the issue of another developer adding a return before the release(…) or a premature Exception that bails out of the method before release(…) has been called.

This is an insidious resource/memory leak. The code will work even when release(…) is not called, so any issues are unlikely to be caught with testing. The code will still work in production, for a while, but resources and memory will be leaking. Resource exhaustion won’t manifest like a tire blowout while driving on the highway. It will manifest like a slowly leaking tire in the parking lot.

What Other Tools Are in our Toolbox?

CAVEAT: I have not been a C++ developer for over 10 years, and I was using an old release of C++ even then. Mechanisms for C++ resource management may have changed quite a bit since then. What I’m about to describe may not be current state of the art.

C++ has an idiom called Resource Allocation Is Instantiation (RAII). It’s a horrible name, but it’s a great technique. Unlike Java, C++ allocates local objects on the call stack. When the call stack pops, the deconstructor for any local object is called, and their resources are restored. This happens on any return or a thrown Exception.

I leveraged RAII along with Proxy to eliminate the burden of the client code having to call release(…). Here’s the basic design:

Factory with acquire() and release()

Java RAII?

I leveraged RAII often when I was a C++ developer. It was useful for more than just memory management. It was useful for any operations that came in open()/close() pairs, such as mutexes, database locks, etc.

I wanted to continue using it even when I moved to a Java environment. I remember putting resource recovery code in finalize(), without quite understanding what I was doing. C++ developers who move to Java often think of finalize() as the Java destructor, but that’s not quite the case. It’s called when an object is cleaned in garbage collection, not when it goes out of scope. There’s no guarantee that garbage collection will ever happen. finalize() has been deprecated, since it’s misused so often, including by knuckleheads like me.

For the most part, we don’t need to worry about memory in Java, but what about open()/close() concepts in Java that aren’t memory?

The try/finally block can do this, but it was a bit awkward. Java’s try-with-resources is a better option. With try-with-resources, an AutoCloseable object is created in a try block. When exiting the try block, the object’s close() method will be executed, and any additional resource management can be done there. Try-with-resources is basically RAII when you can’t explicitly create an object on the call stack.

But what if you have a class that requires resource cleanup management, but it’s not AutoCloseable? You can’t use try-with-resources directly with that class without modification. And the class may be external so that you cannot modify it. This sounds like the perfect place for a Proxy.

Here’s what the UML class diagram might look like:

C# RAII?

C# has the same concept as Java, but with different nomenclature via it’s IDisposable interface. See also: Implement a Dispose method and Using objects that implement IDisposable.

Other Administrative Concerns for Proxy

Lazy Initialization and “Stack” Memory Management aren’t the only administrative concerns for Proxy. Proxy could also be used for a Cache, Database wrappers, Remote Method Invocation wrappers, etc.

But we have a minor issue. In its traditional presentation, there’s only one Proxy class. What if the Concrete Class has multiple administrative concerns? The next pattern, Decorator, will address this. My UML Class Diagram for Proxy hints at a solution when Proxy delegates to Feature rather than ConcreteFeature.

Summary

Proxy provides a mechanism to manage some administrative concerns in the design rather than thrusting them upon Client developers, who may not get the right anyhow.

References

There are many online resources with diagrams and implementations in different programming languages. Here are some free resources:

Here are some resources that can be purchased or are included in a subscription service:

Comments

Previous: Composable Design Patterns – Basic Concepts

Next: Decorator Design Pattern

Home: Design Pattern Evangelist Blog