Proxy Design Pattern in Rust

1. Definition

The Proxy design pattern provides a surrogate or placeholder for another object to control access to it. A proxy can act as an intermediary between the client and the real object.

2. Problem Statement

Imagine you have an expensive object that consumes a lot of resources when instantiated, and you only want to create it when absolutely necessary. How can you delay this instantiation or control the access to the expensive object?

3. Solution

The Proxy pattern lets you provide a substitute for another object. This proxy controls the creation and access of the real object, ensuring that it is created only when necessary or adding additional operations before or after the request reaches the real object.

4. Real-World Use Cases

1. Virtual Proxy: Lazily initializing an expensive object.

2. Remote Proxy: Providing a local representation for an object in a different address space.

3. Protective Proxy: Controlling access to the original object.

5. Implementation Steps

1. Create an interface that both the RealObject and the Proxy will implement.

2. Implement the Proxy class such that it holds a reference to the RealObject.

3. Control the instantiation or access of the RealObject through the Proxy.

6. Implementation in Rust Programming

// Abstract subject
trait Subject {
    fn request(&self);
}
// Real subject
struct RealSubject;
impl Subject for RealSubject {
    fn request(&self) {
        println!("RealSubject: Handling the request.");
    }
}
// Proxy
struct Proxy {
    real_subject: Option<RealSubject>,
}
impl Proxy {
    pub fn new() -> Self {
        Proxy { real_subject: None }
    }
    // Lazily instantiate the real subject
    fn ensure_real_subject(&mut self) {
        if self.real_subject.is_none() {
            self.real_subject = Some(RealSubject);
        }
    }
}
impl Subject for Proxy {
    fn request(&self) {
        println!("Proxy: Checking preconditions before accessing RealSubject.");
        if let Some(real_subject) = &self.real_subject {
            real_subject.request();
        }
        println!("Proxy: Additional tasks after accessing RealSubject.");
    }
}
// Client code
fn main() {
    let mut proxy = Proxy::new();
    proxy.request();
}

Output:

"Proxy: Checking preconditions before accessing RealSubject."
"RealSubject: Handling the request."
"Proxy: Additional tasks after accessing RealSubject."

Explanation:

1. Subject is the common interface for both RealSubject and Proxy.

2. RealSubject is the real object that the proxy represents.

3. Proxy controls access to RealSubject. It checks preconditions before accessing the real subject and performs additional tasks afterward.

4. In the client code, we interact with the RealSubject through the Proxy.

7. When to use?

The Proxy pattern is useful when:

1. You need to control access to an object, either to defer its creation, represent it remotely, or add protective measures.

2. You want to add additional responsibilities to an object dynamically without modifying its structure.

3. The object needs to be accessed in a controlled manner, either for lazy initialization, logging, access control, or other reasons.


Comments