skip to content

Proxy Pattern - Control Over Access to an Object

Updated: at 12:00 AM

1. The Problem

Problem: You Need Control Over Access to an Object

In real systems, you often don’t want direct access to an object.

Common scenarios:

Naive solution: wrap logic around the object everywhere.

if user.is_admin:
    service.do_work()

or

log()
service.do_work()

This leads to:

We need a way to interpose behavior without changing the real object or the calling code.

2. The Proxy Pattern: Control Access Without Changing the Object

The Proxy Pattern introduces a stand-in object that has the same interface as the real object but controls how and when the real object is accessed.

Core idea:

This gives you:

Formally:

3. Types of Proxies

Think of Proxy as “same interface, extra control”.

Common variants:

  1. Virtual Proxy Delay creation of expensive objects (lazy loading)

  2. Protection Proxy Enforce permissions / access rules

  3. Remote Proxy Represent a remote object locally

  4. Caching Proxy Store results, avoid repeated work

  5. Logging / Monitoring Proxy Observe behavior without changing logic

Same structure. Different intent.

4. Implementation: Proxy Pattern in Python

Example: Expensive Data Fetching Service

Subject Interface

from abc import ABC, abstractmethod

class DataService(ABC):
    @abstractmethod
    def fetch_data(self) -> str:
        pass

Real Subject (Expensive Object)

import time

class RealDataService(DataService):
    def __init__(self):
        time.sleep(2)  # expensive initialization

    def fetch_data(self) -> str:
        return "Sensitive data from database"

This object is:

Proxy: Lazy + Access Control

class DataServiceProxy(DataService):
    def __init__(self, user_role: str):
        self._user_role = user_role
        self._real_service = None

    def fetch_data(self) -> str:
        if self._user_role != "admin":
            raise PermissionError("Access denied")

        if self._real_service is None:
            self._real_service = RealDataService()

        return self._real_service.fetch_data()

Client Code (Unchanged)

service = DataServiceProxy(user_role="admin")
print(service.fetch_data())

Client:

5. What Proxy Actually Solves (Systematically)

Separation of Concerns

ConcernLives Where
Core logicRealSubject
Access rulesProxy
Logging / cachingProxy
Client behaviorClient

No cross-contamination.

Key Properties

6. Proxy vs Decorator

Proxy vs Decorator

DecoratorProxy
Adds behaviorControls access
Focus on extensionFocus on mediation
Often stackedUsually single gatekeeper

Decorator enhances. Proxy restricts / defers / controls.

7. When to Use Proxy (and When Not To)

Use Proxy when:

Don’t use Proxy when:

8. Common Pitfalls