Introducción

El patrón Proxy es un patrón estructural que proporciona un sustituto o marcador de posición para otro objeto. Un proxy controla el acceso a un objeto, permitiendo realizar ciertas acciones antes o después de que la solicitud llegue al objeto real. Este patrón es útil cuando se necesita agregar una capa de control sobre el acceso a un objeto.

Objetivos del Patrón Proxy:

  • Controlar el acceso a un objeto.
  • Reducir la carga de trabajo de un objeto costoso.
  • Proveer una capa adicional de seguridad.
  • Implementar un mecanismo de carga diferida (lazy initialization).

Tipos de Proxy

Existen varios tipos de proxies, cada uno con un propósito específico:

  1. Proxy Virtual: Retrasa la creación y carga de un objeto costoso hasta que sea necesario.
  2. Proxy Remoto: Representa un objeto que reside en un espacio de direcciones diferente (por ejemplo, en una máquina remota).
  3. Proxy de Protección: Controla el acceso a un objeto, permitiendo diferentes niveles de acceso.
  4. Proxy de Caché: Proporciona almacenamiento temporal de resultados costosos para mejorar el rendimiento.

Estructura del Patrón Proxy

La estructura del patrón Proxy incluye los siguientes componentes:

  • Subject: Define la interfaz común para RealSubject y Proxy.
  • RealSubject: El objeto real que el proxy representa.
  • Proxy: Mantiene una referencia al RealSubject y controla el acceso a él.

Diagrama UML

+-----------+       +-----------+
|   Client  |------>|   Proxy   |
+-----------+       +-----------+
                       |
                       v
                  +-----------+
                  |RealSubject|
                  +-----------+

Implementación en Código

A continuación, se presenta un ejemplo de implementación del patrón Proxy en Python.

Ejemplo: Proxy Virtual

Supongamos que tenemos una clase ExpensiveObject que es costosa de crear. Utilizaremos un proxy para retrasar su creación hasta que sea realmente necesario.

class ExpensiveObject:
    def __init__(self):
        self._load_data()

    def _load_data(self):
        print("Loading expensive data...")

    def process(self):
        print("Processing data...")

class Proxy:
    def __init__(self):
        self._real_subject = None

    def process(self):
        if self._real_subject is None:
            self._real_subject = ExpensiveObject()
        self._real_subject.process()

# Uso del Proxy
proxy = Proxy()
print("Proxy creado.")
proxy.process()  # Aquí se crea el ExpensiveObject y se llama a su método process.
proxy.process()  # Aquí no se crea de nuevo el ExpensiveObject, solo se llama a su método process.

Explicación del Código

  1. ExpensiveObject: Esta es la clase costosa cuya creación queremos retrasar.
  2. Proxy: Esta clase contiene una referencia a ExpensiveObject y controla su creación y acceso.
  3. Uso del Proxy: Creamos una instancia de Proxy y llamamos al método process. La primera vez que se llama a process, el ExpensiveObject se crea y se carga. En llamadas subsecuentes, el objeto ya está creado y solo se llama a su método process.

Ejercicio Práctico

Ejercicio

Implementa un proxy de protección que controle el acceso a un objeto sensible basado en roles de usuario. El objeto sensible es una clase SensitiveData que tiene un método access_data.

class SensitiveData:
    def access_data(self):
        print("Accessing sensitive data...")

class ProtectionProxy:
    def __init__(self, user_role):
        self._user_role = user_role
        self._real_subject = SensitiveData()

    def access_data(self):
        if self._user_role == "admin":
            self._real_subject.access_data()
        else:
            print("Access denied. Insufficient permissions.")

# Uso del ProtectionProxy
proxy_admin = ProtectionProxy("admin")
proxy_admin.access_data()  # Debe permitir el acceso.

proxy_user = ProtectionProxy("user")
proxy_user.access_data()  # Debe denegar el acceso.

Solución

class SensitiveData:
    def access_data(self):
        print("Accessing sensitive data...")

class ProtectionProxy:
    def __init__(self, user_role):
        self._user_role = user_role
        self._real_subject = SensitiveData()

    def access_data(self):
        if self._user_role == "admin":
            self._real_subject.access_data()
        else:
            print("Access denied. Insufficient permissions.")

# Uso del ProtectionProxy
proxy_admin = ProtectionProxy("admin")
proxy_admin.access_data()  # Debe permitir el acceso.

proxy_user = ProtectionProxy("user")
proxy_user.access_data()  # Debe denegar el acceso.

Conclusión

El patrón Proxy es una herramienta poderosa para controlar el acceso a objetos y mejorar el rendimiento y la seguridad de una aplicación. Al entender y aplicar este patrón, los desarrolladores pueden crear sistemas más eficientes y seguros.

Resumen

  • El patrón Proxy proporciona un sustituto para otro objeto para controlar el acceso a él.
  • Hay varios tipos de proxies, incluyendo virtual, remoto, de protección y de caché.
  • La implementación de un proxy puede mejorar el rendimiento y la seguridad de una aplicación.
  • Los proxies son útiles en situaciones donde la creación de objetos es costosa o donde se requiere control de acceso.

En el siguiente módulo, exploraremos el patrón Chain of Responsibility, que permite pasar una solicitud a lo largo de una cadena de manejadores.

© Copyright 2024. Todos los derechos reservados