Control Access with the Proxy Pattern

Thu, Jan 25, 2024 2-minute read

The Proxy Pattern is a structural design pattern commonly used in object-oriented programming. This pattern provides a surrogate or placeholder object, known as a proxy, to control access to another object, which could be remote, expensive to create, or needs to be secured. The Proxy Pattern is particularly useful for performing operations before or after the request gets to the primary object, such as access control, logging, or lazy instantiation.

Key Concepts of the Proxy Pattern:

  1. Proxy: This is the object that clients interact with. It maintains a reference to the real object, known as the subject, and controls access to it.
  2. Subject: The real object that the proxy represents.
  3. Client: The client interacts with the Proxy object, believing it is interacting with the real object.

Types of Proxies:

  1. Virtual Proxy: Used for lazy instantiation of a heavy object.
  2. Remote Proxy: Provides a local representation for an object in a different address space.
  3. Protective Proxy: Controls access to a sensitive object.
  4. Smart Proxy: Adds additional functionality when accessing an object, like reference counting or logging.

Proxy Pattern in JavaScript:

In JavaScript, the Proxy Pattern can be implemented using the native Proxy object introduced in ECMAScript 6 (ES6). Here’s an example:

Example 1: Logging Proxy

Let’s say you have an object, and you want to log every time a property is accessed or set. You can create a Proxy to add this behavior:

const target = {
    name: 'John Doe',
    age: 30
};

const handler = {
    get(target, property) {
        console.log(`Property '${property}' has been read.`);
        return target[property];
    },
    set(target, property, value) {
        console.log(`Property '${property}' set to '${value}'.`);
        target[property] = value;
        return true;
    }
};

const proxy = new Proxy(target, handler);

console.log(proxy.name);  // Logs: Property 'name' has been read.
proxy.age = 31;           // Logs: Property 'age' set to '31'.

Example 2: Validation Proxy

Suppose you want to validate data before setting a property. A Proxy can be used to intercept and validate property assignments:

const user = {
    name: 'Alice',
    age: 25
};

const validator = {
    set(target, property, value) {
        if (property === 'age' && !Number.isInteger(value)) {
            throw new Error('Age must be an integer.');
        }
        target[property] = value;
        return true;
    }
};

const proxyUser = new Proxy(user, validator);

proxyUser.age = 30;      // Works fine.
proxyUser.age = 'thirty'; // Throws an error: Age must be an integer.

In these examples, the Proxy object is used to add extra behaviors (logging and validation) to normal object operations without modifying the original objects. This encapsulates the additional functionalities and makes the code more modular and maintainable.