Proxy Design Pattern in Ruby

1. Definition

The Proxy Design Pattern provides a surrogate or placeholder for another object to control access to it. The primary reason for using a proxy is to act as a controlling intermediary between a client and the real object.

2. Problem Statement

There are situations when we want to add some functionality to an object, but we don't want to modify its structure. This could be for access control, lazy initialization, logging, etc.

3. Solution

The Proxy pattern suggests creating a new proxy class with the same interface as the original object. This proxy class can then wrap the original object, adding additional behaviors and controlling access to it.

4. Real-World Use Cases

1. Lazy loading of a heavyweight object.

2. Access control to resources or operations.

3. Logging access and operations for debugging or auditing purposes.

5. Implementation Steps

1. Define the common interface for both the RealObject and the Proxy.

2. Create the Proxy class that holds a reference to the RealObject.

3. Implement the proxy methods, adding the desired functionality before or after calling the real object's methods.

6. Implementation in Ruby

# Step 1: Common Interface
class AbstractObject
  def request
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end
# Step 2: RealObject
class RealObject < AbstractObject
  def request
    "RealObject: Handling request."
  end
end
# Step 3: Proxy Object
class ProxyObject < AbstractObject
  def initialize(real_object)
    @real_object = real_object
  end
  def request
    # Additional behavior (e.g., logging, access control)
    if access_granted?
      @real_object.request + " (handled by Proxy)"
    else
      "Proxy: Access denied."
    end
  end
  private
  def access_granted?
    # Simulate access control check
    true
  end
end
# Client code
proxy = ProxyObject.new(RealObject.new)
puts proxy.request

Output:

RealObject: Handling request. (handled by Proxy)

Explanation:

1. The AbstractObject class provides a common interface for both RealObject and ProxyObject.

2. RealObject is the actual object that performs the primary function.

3. ProxyObject wraps around RealObject and can control access to it, or add additional functionalities. In this example, it checks access using the access_granted? method and then lets the RealObject handle the request.

4. The client interacts with the ProxyObject, which in turn interacts with the RealObject when appropriate.

7. When to use?

Use the Proxy pattern when:

1. You need a more versatile or sophisticated reference to an object than just a pointer.

2. You want to lazy-initialize your heavy objects only when they're needed.

3. You need to log, monitor, or secure access to the actual object without changing it.


Comments