Skip to content

Proxy Pattern

Category: Structural Pattern

Overview

Provide a surrogate or placeholder for another object to control access to it. This pattern is used to create a representative object that controls access to another object, which may be remote, expensive to create, or require protection.

Usage Guidelines

Use when:

  • Defer expensive object creation until needed (lazy initialization)
  • Control access based on permissions or authentication
  • Access objects in different address spaces (remote objects)
  • Log access to objects or cache results of expensive operations

Avoid when:

  • Direct access is sufficient and simple
  • No access control or lazy loading required
  • Proxy overhead is unacceptable for performance
  • Can modify original class directly

Implementation

from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Optional

class Image(ABC):
    """Abstract interface for images."""

    @abstractmethod
    def display(self) -> str:
        """Display the image."""
        pass

    @abstractmethod
    def get_filename(self) -> str:
        """Get the image filename."""
        pass

class RealImage(Image):
    """Real image that is expensive to load."""

    def __init__(self, filename: str) -> None:
        """Initialize and load the image."""
        self.filename = filename
        self._load_from_disk()

    def _load_from_disk(self) -> None:
        """Simulate expensive loading operation."""
        pass

    def display(self) -> str:
        """Display the image."""
        return f"Displaying {self.filename}"

    def get_filename(self) -> str:
        """Get filename."""
        return self.filename

class ImageProxy(Image):
    """Virtual proxy for lazy loading images."""

    def __init__(self, filename: str) -> None:
        """Initialize proxy without loading image."""
        self.filename = filename
        self._real_image: Optional[RealImage] = None

    def display(self) -> str:
        """Display image, loading it if necessary."""
        if self._real_image is None:
            self._real_image = RealImage(self.filename)
        return self._real_image.display()

    def get_filename(self) -> str:
        """Get filename without loading image."""
        return self.filename

    def is_loaded(self) -> bool:
        """Check if real image is loaded."""
        return self._real_image is not None

Usage

# Virtual Proxy - lazy loading
image = ImageProxy("large_photo.jpg")
print(image.is_loaded())  # False - not loaded yet
print(image.display())  # Now loaded and displayed
print(image.is_loaded())  # True - already loaded

Trade-offs

Benefits:

  1. Controls access to real object
  2. Defers expensive operations until needed (lazy initialization)
  3. Implements authentication and authorization for access control
  4. Adds behavior without modifying real object

Drawbacks:

  1. Adds additional classes and indirection increasing complexity
  2. Proxy adds overhead affecting performance
  3. Lazy initialization may cause delays in response time
  4. Thread-safe proxies can be complex

Real-World Examples

  • ORM frameworks with database proxy objects for lazy loading
  • Virtual images as placeholders in documents
  • Network proxies like HTTP proxies, SOCKS proxies
  • Security proxies for authentication and authorization layers
  • Adapter
  • Decorator
  • Facade

API Reference

design_patterns.structural.proxy

Proxy Pattern Module

The Proxy pattern provides a surrogate or placeholder for another object to control access to it. There are several types of proxies: - Virtual Proxy: Controls access to expensive-to-create objects - Protection Proxy: Controls access based on permissions - Remote Proxy: Represents an object in a different address space - Caching Proxy: Caches results of expensive operations

Example

Using a virtual proxy for lazy loading:

image = ImageProxy("large_photo.jpg")
# Image not loaded yet

image.display()  # Now image is loaded and displayed
image.display()  # Uses cached image, no reload

Database

Bases: ABC

Abstract interface for database.

Source code in src/design_patterns/structural/proxy.py
class Database(ABC):
    """Abstract interface for database."""

    @abstractmethod
    def query(self, sql: str) -> str:
        """Execute a query.

        Args:
            sql: SQL query.

        Returns:
            Query result.
        """
        pass

query(sql) abstractmethod

Execute a query.

Parameters:

Name Type Description Default
sql str

SQL query.

required

Returns:

Type Description
str

Query result.

Source code in src/design_patterns/structural/proxy.py
@abstractmethod
def query(self, sql: str) -> str:
    """Execute a query.

    Args:
        sql: SQL query.

    Returns:
        Query result.
    """
    pass

DatabaseProxy

Bases: Database

Caching proxy for database queries.

Source code in src/design_patterns/structural/proxy.py
class DatabaseProxy(Database):
    """Caching proxy for database queries."""

    def __init__(self) -> None:
        """Initialize proxy with real database and cache."""
        self._real_database = RealDatabase()
        self._cache: dict[str, str] = {}

    def query(self, sql: str) -> str:
        """Execute query with caching.

        Args:
            sql: SQL query.

        Returns:
            Cached or fresh query result.
        """
        if sql in self._cache:
            return f"Cached: {self._cache[sql]}"

        result = self._real_database.query(sql)
        self._cache[sql] = result
        return result

    def clear_cache(self) -> None:
        """Clear the query cache."""
        self._cache.clear()

    def get_cache_size(self) -> int:
        """Get number of cached queries.

        Returns:
            Cache size.
        """
        return len(self._cache)

__init__()

Initialize proxy with real database and cache.

Source code in src/design_patterns/structural/proxy.py
def __init__(self) -> None:
    """Initialize proxy with real database and cache."""
    self._real_database = RealDatabase()
    self._cache: dict[str, str] = {}

clear_cache()

Clear the query cache.

Source code in src/design_patterns/structural/proxy.py
def clear_cache(self) -> None:
    """Clear the query cache."""
    self._cache.clear()

get_cache_size()

Get number of cached queries.

Returns:

Type Description
int

Cache size.

Source code in src/design_patterns/structural/proxy.py
def get_cache_size(self) -> int:
    """Get number of cached queries.

    Returns:
        Cache size.
    """
    return len(self._cache)

query(sql)

Execute query with caching.

Parameters:

Name Type Description Default
sql str

SQL query.

required

Returns:

Type Description
str

Cached or fresh query result.

Source code in src/design_patterns/structural/proxy.py
def query(self, sql: str) -> str:
    """Execute query with caching.

    Args:
        sql: SQL query.

    Returns:
        Cached or fresh query result.
    """
    if sql in self._cache:
        return f"Cached: {self._cache[sql]}"

    result = self._real_database.query(sql)
    self._cache[sql] = result
    return result

Document

Bases: ABC

Abstract interface for documents.

Source code in src/design_patterns/structural/proxy.py
class Document(ABC):
    """Abstract interface for documents."""

    @abstractmethod
    def read(self) -> str:
        """Read the document.

        Returns:
            Document content.
        """
        pass

    @abstractmethod
    def write(self, content: str) -> str:
        """Write to the document.

        Args:
            content: Content to write.

        Returns:
            Write result.
        """
        pass

read() abstractmethod

Read the document.

Returns:

Type Description
str

Document content.

Source code in src/design_patterns/structural/proxy.py
@abstractmethod
def read(self) -> str:
    """Read the document.

    Returns:
        Document content.
    """
    pass

write(content) abstractmethod

Write to the document.

Parameters:

Name Type Description Default
content str

Content to write.

required

Returns:

Type Description
str

Write result.

Source code in src/design_patterns/structural/proxy.py
@abstractmethod
def write(self, content: str) -> str:
    """Write to the document.

    Args:
        content: Content to write.

    Returns:
        Write result.
    """
    pass

Image

Bases: ABC

Abstract interface for images.

Source code in src/design_patterns/structural/proxy.py
class Image(ABC):
    """Abstract interface for images."""

    @abstractmethod
    def display(self) -> str:
        """Display the image.

        Returns:
            Display message.
        """
        pass

    @abstractmethod
    def get_filename(self) -> str:
        """Get the image filename.

        Returns:
            Filename string.
        """
        pass

display() abstractmethod

Display the image.

Returns:

Type Description
str

Display message.

Source code in src/design_patterns/structural/proxy.py
@abstractmethod
def display(self) -> str:
    """Display the image.

    Returns:
        Display message.
    """
    pass

get_filename() abstractmethod

Get the image filename.

Returns:

Type Description
str

Filename string.

Source code in src/design_patterns/structural/proxy.py
@abstractmethod
def get_filename(self) -> str:
    """Get the image filename.

    Returns:
        Filename string.
    """
    pass

ImageProxy

Bases: Image

Virtual proxy for lazy loading images.

Source code in src/design_patterns/structural/proxy.py
class ImageProxy(Image):
    """Virtual proxy for lazy loading images."""

    def __init__(self, filename: str) -> None:
        """Initialize proxy without loading image.

        Args:
            filename: Image filename.
        """
        self.filename = filename
        self._real_image: Optional[RealImage] = None

    def display(self) -> str:
        """Display image, loading it if necessary.

        Returns:
            Display message.
        """
        if self._real_image is None:
            self._real_image = RealImage(self.filename)
        return self._real_image.display()

    def get_filename(self) -> str:
        """Get filename without loading image.

        Returns:
            Filename.
        """
        return self.filename

    def is_loaded(self) -> bool:
        """Check if real image is loaded.

        Returns:
            True if loaded, False otherwise.
        """
        return self._real_image is not None

__init__(filename)

Initialize proxy without loading image.

Parameters:

Name Type Description Default
filename str

Image filename.

required
Source code in src/design_patterns/structural/proxy.py
def __init__(self, filename: str) -> None:
    """Initialize proxy without loading image.

    Args:
        filename: Image filename.
    """
    self.filename = filename
    self._real_image: Optional[RealImage] = None

display()

Display image, loading it if necessary.

Returns:

Type Description
str

Display message.

Source code in src/design_patterns/structural/proxy.py
def display(self) -> str:
    """Display image, loading it if necessary.

    Returns:
        Display message.
    """
    if self._real_image is None:
        self._real_image = RealImage(self.filename)
    return self._real_image.display()

get_filename()

Get filename without loading image.

Returns:

Type Description
str

Filename.

Source code in src/design_patterns/structural/proxy.py
def get_filename(self) -> str:
    """Get filename without loading image.

    Returns:
        Filename.
    """
    return self.filename

is_loaded()

Check if real image is loaded.

Returns:

Type Description
bool

True if loaded, False otherwise.

Source code in src/design_patterns/structural/proxy.py
def is_loaded(self) -> bool:
    """Check if real image is loaded.

    Returns:
        True if loaded, False otherwise.
    """
    return self._real_image is not None

Internet

Bases: ABC

Abstract interface for internet access.

Source code in src/design_patterns/structural/proxy.py
class Internet(ABC):
    """Abstract interface for internet access."""

    @abstractmethod
    def connect(self, url: str) -> str:
        """Connect to a URL.

        Args:
            url: URL to connect to.

        Returns:
            Connection result.
        """
        pass

connect(url) abstractmethod

Connect to a URL.

Parameters:

Name Type Description Default
url str

URL to connect to.

required

Returns:

Type Description
str

Connection result.

Source code in src/design_patterns/structural/proxy.py
@abstractmethod
def connect(self, url: str) -> str:
    """Connect to a URL.

    Args:
        url: URL to connect to.

    Returns:
        Connection result.
    """
    pass

ProtectedDocumentProxy

Bases: Document

Protection proxy that requires authentication.

Source code in src/design_patterns/structural/proxy.py
class ProtectedDocumentProxy(Document):
    """Protection proxy that requires authentication."""

    def __init__(self, filename: str, user_role: str) -> None:
        """Initialize protected document proxy.

        Args:
            filename: Document filename.
            user_role: User's role (admin or user).
        """
        self._real_document = RealDocument(filename)
        self.user_role = user_role

    def read(self) -> str:
        """Read document (allowed for all users).

        Returns:
            Document content.
        """
        return self._real_document.read()

    def write(self, content: str) -> str:
        """Write to document (requires admin role).

        Args:
            content: Content to write.

        Returns:
            Write result or access denied message.
        """
        if self.user_role == "admin":
            return self._real_document.write(content)
        return "Access denied: admin role required for writing"

__init__(filename, user_role)

Initialize protected document proxy.

Parameters:

Name Type Description Default
filename str

Document filename.

required
user_role str

User's role (admin or user).

required
Source code in src/design_patterns/structural/proxy.py
def __init__(self, filename: str, user_role: str) -> None:
    """Initialize protected document proxy.

    Args:
        filename: Document filename.
        user_role: User's role (admin or user).
    """
    self._real_document = RealDocument(filename)
    self.user_role = user_role

read()

Read document (allowed for all users).

Returns:

Type Description
str

Document content.

Source code in src/design_patterns/structural/proxy.py
def read(self) -> str:
    """Read document (allowed for all users).

    Returns:
        Document content.
    """
    return self._real_document.read()

write(content)

Write to document (requires admin role).

Parameters:

Name Type Description Default
content str

Content to write.

required

Returns:

Type Description
str

Write result or access denied message.

Source code in src/design_patterns/structural/proxy.py
def write(self, content: str) -> str:
    """Write to document (requires admin role).

    Args:
        content: Content to write.

    Returns:
        Write result or access denied message.
    """
    if self.user_role == "admin":
        return self._real_document.write(content)
    return "Access denied: admin role required for writing"

ProxyInternet

Bases: Internet

Protection proxy that filters internet access.

Source code in src/design_patterns/structural/proxy.py
class ProxyInternet(Internet):
    """Protection proxy that filters internet access."""

    def __init__(self) -> None:
        """Initialize proxy with real internet and banned sites."""
        self._real_internet = RealInternet()
        self._banned_sites = ["banned.com", "restricted.net", "blocked.org"]

    def connect(self, url: str) -> str:
        """Connect to URL if not banned.

        Args:
            url: URL to connect to.

        Returns:
            Connection result or blocked message.
        """
        for banned in self._banned_sites:
            if banned in url:
                return f"Access denied to {url}"
        return self._real_internet.connect(url)

    def add_banned_site(self, site: str) -> None:
        """Add a site to the banned list.

        Args:
            site: Site to ban.
        """
        if site not in self._banned_sites:
            self._banned_sites.append(site)

__init__()

Initialize proxy with real internet and banned sites.

Source code in src/design_patterns/structural/proxy.py
def __init__(self) -> None:
    """Initialize proxy with real internet and banned sites."""
    self._real_internet = RealInternet()
    self._banned_sites = ["banned.com", "restricted.net", "blocked.org"]

add_banned_site(site)

Add a site to the banned list.

Parameters:

Name Type Description Default
site str

Site to ban.

required
Source code in src/design_patterns/structural/proxy.py
def add_banned_site(self, site: str) -> None:
    """Add a site to the banned list.

    Args:
        site: Site to ban.
    """
    if site not in self._banned_sites:
        self._banned_sites.append(site)

connect(url)

Connect to URL if not banned.

Parameters:

Name Type Description Default
url str

URL to connect to.

required

Returns:

Type Description
str

Connection result or blocked message.

Source code in src/design_patterns/structural/proxy.py
def connect(self, url: str) -> str:
    """Connect to URL if not banned.

    Args:
        url: URL to connect to.

    Returns:
        Connection result or blocked message.
    """
    for banned in self._banned_sites:
        if banned in url:
            return f"Access denied to {url}"
    return self._real_internet.connect(url)

RealDatabase

Bases: Database

Real database implementation.

Source code in src/design_patterns/structural/proxy.py
class RealDatabase(Database):
    """Real database implementation."""

    def query(self, sql: str) -> str:
        """Execute query on real database.

        Args:
            sql: SQL query.

        Returns:
            Query result.
        """
        return f"Executed: {sql}"

query(sql)

Execute query on real database.

Parameters:

Name Type Description Default
sql str

SQL query.

required

Returns:

Type Description
str

Query result.

Source code in src/design_patterns/structural/proxy.py
def query(self, sql: str) -> str:
    """Execute query on real database.

    Args:
        sql: SQL query.

    Returns:
        Query result.
    """
    return f"Executed: {sql}"

RealDocument

Bases: Document

Real document implementation.

Source code in src/design_patterns/structural/proxy.py
class RealDocument(Document):
    """Real document implementation."""

    def __init__(self, filename: str) -> None:
        """Initialize document.

        Args:
            filename: Document filename.
        """
        self.filename = filename
        self._content = ""

    def read(self) -> str:
        """Read document content.

        Returns:
            Document content.
        """
        return f"Reading {self.filename}: {self._content}"

    def write(self, content: str) -> str:
        """Write content to document.

        Args:
            content: Content to write.

        Returns:
            Write confirmation.
        """
        self._content = content
        return f"Written to {self.filename}"

__init__(filename)

Initialize document.

Parameters:

Name Type Description Default
filename str

Document filename.

required
Source code in src/design_patterns/structural/proxy.py
def __init__(self, filename: str) -> None:
    """Initialize document.

    Args:
        filename: Document filename.
    """
    self.filename = filename
    self._content = ""

read()

Read document content.

Returns:

Type Description
str

Document content.

Source code in src/design_patterns/structural/proxy.py
def read(self) -> str:
    """Read document content.

    Returns:
        Document content.
    """
    return f"Reading {self.filename}: {self._content}"

write(content)

Write content to document.

Parameters:

Name Type Description Default
content str

Content to write.

required

Returns:

Type Description
str

Write confirmation.

Source code in src/design_patterns/structural/proxy.py
def write(self, content: str) -> str:
    """Write content to document.

    Args:
        content: Content to write.

    Returns:
        Write confirmation.
    """
    self._content = content
    return f"Written to {self.filename}"

RealImage

Bases: Image

Real image that is expensive to load.

Source code in src/design_patterns/structural/proxy.py
class RealImage(Image):
    """Real image that is expensive to load."""

    def __init__(self, filename: str) -> None:
        """Initialize and load the image.

        Args:
            filename: Image filename.
        """
        self.filename = filename
        self._load_from_disk()

    def _load_from_disk(self) -> None:
        """Simulate expensive loading operation."""
        pass

    def display(self) -> str:
        """Display the image.

        Returns:
            Display message.
        """
        return f"Displaying {self.filename}"

    def get_filename(self) -> str:
        """Get filename.

        Returns:
            Filename.
        """
        return self.filename

__init__(filename)

Initialize and load the image.

Parameters:

Name Type Description Default
filename str

Image filename.

required
Source code in src/design_patterns/structural/proxy.py
def __init__(self, filename: str) -> None:
    """Initialize and load the image.

    Args:
        filename: Image filename.
    """
    self.filename = filename
    self._load_from_disk()

display()

Display the image.

Returns:

Type Description
str

Display message.

Source code in src/design_patterns/structural/proxy.py
def display(self) -> str:
    """Display the image.

    Returns:
        Display message.
    """
    return f"Displaying {self.filename}"

get_filename()

Get filename.

Returns:

Type Description
str

Filename.

Source code in src/design_patterns/structural/proxy.py
def get_filename(self) -> str:
    """Get filename.

    Returns:
        Filename.
    """
    return self.filename

RealInternet

Bases: Internet

Real internet connection.

Source code in src/design_patterns/structural/proxy.py
class RealInternet(Internet):
    """Real internet connection."""

    def connect(self, url: str) -> str:
        """Connect to URL.

        Args:
            url: URL to connect to.

        Returns:
            Connection success message.
        """
        return f"Connected to {url}"

connect(url)

Connect to URL.

Parameters:

Name Type Description Default
url str

URL to connect to.

required

Returns:

Type Description
str

Connection success message.

Source code in src/design_patterns/structural/proxy.py
def connect(self, url: str) -> str:
    """Connect to URL.

    Args:
        url: URL to connect to.

    Returns:
        Connection success message.
    """
    return f"Connected to {url}"