# Proxy Pattern

> Provide a surrogate or placeholder for another object to control access to it.

## Problem

You need to control how an object is accessed. Common scenarios:
1. **Virtual proxy** — Delay expensive object creation until actually needed (lazy loading).
2. **Protection proxy** — Restrict access based on permissions.
3. **Logging proxy** — Log all interactions transparently.

## Solution

Create a proxy class with the same interface as the real object. The proxy holds a reference to the real object and adds control logic (lazy init, access checks, logging) before/after delegating calls.

```
┌────────┐      ┌──────────────────┐
│ Client  │─────▶│ Service (intf)   │
└────────┘      │ + request()      │
                └──────┬───────────┘
                       │
               ┌───────┴───────┐
               │               │
          ┌────┴────┐    ┌─────┴─────┐
          │RealServ │    │   Proxy   │
          │         │    │ + real    │
          └─────────┘    │ + request│
                         └──────────┘
```

## When to Use

- **Virtual proxy**: The real object is expensive to create and may not always be needed.
- **Protection proxy**: You need to check permissions before granting access.
- **Logging proxy**: You want to record interactions without modifying the real object.
- **Caching proxy**: You want to cache results of expensive operations.

## When to Avoid

- The added indirection is unnecessary for simple, inexpensive objects.
- The proxy would duplicate logic that belongs in the real object.
- You need to proxy many methods and a language-level mechanism (e.g., Python `__getattr__`, JS `Proxy`) would be cleaner.

## Pseudocode

```
interface Database:
    method query(sql): string

class RealDatabase implements Database:
    constructor():
        output "Connecting to database... (expensive)"
    method query(sql):
        return "Result of: " + sql

// Virtual proxy — lazy init
class LazyDatabaseProxy implements Database:
    field real: Database = null
    method query(sql):
        if real is null:
            real = new RealDatabase()
        return real.query(sql)

// Protection proxy
class ProtectedDatabaseProxy implements Database:
    field real: Database
    field user_role: string
    constructor(real, role):
        this.real = real; this.user_role = role
    method query(sql):
        if user_role != "admin":
            output "ACCESS DENIED for role: " + user_role
            return null
        return real.query(sql)

// Logging proxy
class LoggingDatabaseProxy implements Database:
    field real: Database
    constructor(real): this.real = real
    method query(sql):
        output "[LOG] query: " + sql
        result = real.query(sql)
        output "[LOG] result: " + result
        return result
```

## Python

```python
from abc import ABC, abstractmethod


class Database(ABC):
    """Service interface."""

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


class RealDatabase(Database):
    """Expensive real service."""

    def __init__(self) -> None:
        print("  [RealDB] Connecting to database... (expensive)")

    def query(self, sql: str) -> str:
        return f"Result of: {sql}"


# --- Virtual Proxy (lazy loading) ---

class LazyDatabaseProxy(Database):
    """Delays creation of RealDatabase until first use."""

    def __init__(self) -> None:
        self._real: RealDatabase | None = None

    def query(self, sql: str) -> str:
        if self._real is None:
            print("  [LazyProxy] First access — initializing real database")
            self._real = RealDatabase()
        return self._real.query(sql)


# --- Protection Proxy ---

class ProtectedDatabaseProxy(Database):
    """Checks user role before allowing queries."""

    def __init__(self, real: Database, user_role: str) -> None:
        self._real = real
        self._role = user_role

    def query(self, sql: str) -> str:
        if self._role != "admin":
            print(f"  [ProtectionProxy] ACCESS DENIED for role '{self._role}'")
            return "ACCESS DENIED"
        return self._real.query(sql)


# --- Logging Proxy ---

class LoggingDatabaseProxy(Database):
    """Logs every query transparently."""

    def __init__(self, real: Database) -> None:
        self._real = real

    def query(self, sql: str) -> str:
        print(f"  [LogProxy] Executing: {sql}")
        result = self._real.query(sql)
        print(f"  [LogProxy] Got: {result}")
        return result


if __name__ == "__main__":
    print("=== Virtual Proxy (lazy loading) ===")
    lazy_db = LazyDatabaseProxy()
    print("Proxy created, no DB connection yet.")
    print(lazy_db.query("SELECT * FROM users"))
    print(lazy_db.query("SELECT * FROM orders"))

    print("\n=== Protection Proxy ===")
    real_db = RealDatabase()
    admin_db = ProtectedDatabaseProxy(real_db, "admin")
    guest_db = ProtectedDatabaseProxy(real_db, "guest")
    print(admin_db.query("SELECT * FROM secrets"))
    print(guest_db.query("SELECT * FROM secrets"))

    print("\n=== Logging Proxy ===")
    logged_db = LoggingDatabaseProxy(real_db)
    print(logged_db.query("SELECT count(*) FROM users"))
```

**Output:**
```
=== Virtual Proxy (lazy loading) ===
Proxy created, no DB connection yet.
  [LazyProxy] First access — initializing real database
  [RealDB] Connecting to database... (expensive)
Result of: SELECT * FROM users
Result of: SELECT * FROM orders

=== Protection Proxy ===
  [RealDB] Connecting to database... (expensive)
Result of: SELECT * FROM secrets
  [ProtectionProxy] ACCESS DENIED for role 'guest'
ACCESS DENIED

=== Logging Proxy ===
  [LogProxy] Executing: SELECT count(*) FROM users
  [LogProxy] Got: Result of: SELECT count(*) FROM users
Result of: SELECT count(*) FROM users
```

## Go

```go
package main

import "fmt"

// Database is the service interface.
type Database interface {
	Query(sql string) string
}

// RealDatabase is the expensive real service.
type RealDatabase struct{}

func NewRealDatabase() *RealDatabase {
	fmt.Println("  [RealDB] Connecting to database... (expensive)")
	return &RealDatabase{}
}

func (db *RealDatabase) Query(sql string) string {
	return "Result of: " + sql
}

// --- Virtual Proxy ---

type LazyDatabaseProxy struct {
	real *RealDatabase
}

func (p *LazyDatabaseProxy) Query(sql string) string {
	if p.real == nil {
		fmt.Println("  [LazyProxy] First access — initializing real database")
		p.real = NewRealDatabase()
	}
	return p.real.Query(sql)
}

// --- Protection Proxy ---

type ProtectedDatabaseProxy struct {
	real Database
	role string
}

func NewProtectedProxy(real Database, role string) *ProtectedDatabaseProxy {
	return &ProtectedDatabaseProxy{real: real, role: role}
}

func (p *ProtectedDatabaseProxy) Query(sql string) string {
	if p.role != "admin" {
		fmt.Printf("  [ProtectionProxy] ACCESS DENIED for role '%s'\n", p.role)
		return "ACCESS DENIED"
	}
	return p.real.Query(sql)
}

// --- Logging Proxy ---

type LoggingDatabaseProxy struct {
	real Database
}

func NewLoggingProxy(real Database) *LoggingDatabaseProxy {
	return &LoggingDatabaseProxy{real: real}
}

func (p *LoggingDatabaseProxy) Query(sql string) string {
	fmt.Printf("  [LogProxy] Executing: %s\n", sql)
	result := p.real.Query(sql)
	fmt.Printf("  [LogProxy] Got: %s\n", result)
	return result
}

func main() {
	fmt.Println("=== Virtual Proxy (lazy loading) ===")
	lazyDB := &LazyDatabaseProxy{}
	fmt.Println("Proxy created, no DB connection yet.")
	fmt.Println(lazyDB.Query("SELECT * FROM users"))
	fmt.Println(lazyDB.Query("SELECT * FROM orders"))

	fmt.Println("\n=== Protection Proxy ===")
	realDB := NewRealDatabase()
	adminDB := NewProtectedProxy(realDB, "admin")
	guestDB := NewProtectedProxy(realDB, "guest")
	fmt.Println(adminDB.Query("SELECT * FROM secrets"))
	fmt.Println(guestDB.Query("SELECT * FROM secrets"))

	fmt.Println("\n=== Logging Proxy ===")
	loggedDB := NewLoggingProxy(realDB)
	fmt.Println(loggedDB.Query("SELECT count(*) FROM users"))
}
```

**Output:**
```
=== Virtual Proxy (lazy loading) ===
Proxy created, no DB connection yet.
  [LazyProxy] First access — initializing real database
  [RealDB] Connecting to database... (expensive)
Result of: SELECT * FROM users
Result of: SELECT * FROM orders

=== Protection Proxy ===
  [RealDB] Connecting to database... (expensive)
Result of: SELECT * FROM secrets
  [ProtectionProxy] ACCESS DENIED for role 'guest'
ACCESS DENIED

=== Logging Proxy ===
  [LogProxy] Executing: SELECT count(*) FROM users
  [LogProxy] Got: Result of: SELECT count(*) FROM users
Result of: SELECT count(*) FROM users
```

## JavaScript

```javascript
class RealDatabase {
  constructor() {
    console.log("  [RealDB] Connecting to database... (expensive)");
  }

  query(sql) {
    return `Result of: ${sql}`;
  }
}

// --- Virtual Proxy (lazy loading) ---

class LazyDatabaseProxy {
  constructor() {
    this._real = null;
  }

  query(sql) {
    if (this._real === null) {
      console.log(
        "  [LazyProxy] First access — initializing real database"
      );
      this._real = new RealDatabase();
    }
    return this._real.query(sql);
  }
}

// --- Protection Proxy ---

class ProtectedDatabaseProxy {
  constructor(real, role) {
    this._real = real;
    this._role = role;
  }

  query(sql) {
    if (this._role !== "admin") {
      console.log(
        `  [ProtectionProxy] ACCESS DENIED for role '${this._role}'`
      );
      return "ACCESS DENIED";
    }
    return this._real.query(sql);
  }
}

// --- Logging Proxy ---

class LoggingDatabaseProxy {
  constructor(real) {
    this._real = real;
  }

  query(sql) {
    console.log(`  [LogProxy] Executing: ${sql}`);
    const result = this._real.query(sql);
    console.log(`  [LogProxy] Got: ${result}`);
    return result;
  }
}

// --- Main ---
console.log("=== Virtual Proxy (lazy loading) ===");
const lazyDB = new LazyDatabaseProxy();
console.log("Proxy created, no DB connection yet.");
console.log(lazyDB.query("SELECT * FROM users"));
console.log(lazyDB.query("SELECT * FROM orders"));

console.log("\n=== Protection Proxy ===");
const realDB = new RealDatabase();
const adminDB = new ProtectedDatabaseProxy(realDB, "admin");
const guestDB = new ProtectedDatabaseProxy(realDB, "guest");
console.log(adminDB.query("SELECT * FROM secrets"));
console.log(guestDB.query("SELECT * FROM secrets"));

console.log("\n=== Logging Proxy ===");
const loggedDB = new LoggingDatabaseProxy(realDB);
console.log(loggedDB.query("SELECT count(*) FROM users"));
```

**Output:**
```
=== Virtual Proxy (lazy loading) ===
Proxy created, no DB connection yet.
  [LazyProxy] First access — initializing real database
  [RealDB] Connecting to database... (expensive)
Result of: SELECT * FROM users
Result of: SELECT * FROM orders

=== Protection Proxy ===
  [RealDB] Connecting to database... (expensive)
Result of: SELECT * FROM secrets
  [ProtectionProxy] ACCESS DENIED for role 'guest'
ACCESS DENIED

=== Logging Proxy ===
  [LogProxy] Executing: SELECT count(*) FROM users
  [LogProxy] Got: Result of: SELECT count(*) FROM users
Result of: SELECT count(*) FROM users
```

## Related Patterns

- **Decorator** — Both wrap objects with the same interface. Proxy controls access; Decorator adds behavior. Combined: Decorator wraps a Proxy to add both behavior and access control.
- **Adapter** — Adapter wraps to change the interface; Proxy wraps to control access while keeping the same interface.
- **Facade** — Facade simplifies a subsystem interface; Proxy keeps the same interface but adds control.
- **Memoization** — Caching Proxy implements memoization transparently.
- **Guard Clause** — Protection Proxy implements Guard Clause logic at the wrapper level.
