π§© Proxy Pattern
β Intentβ
- Introduce a surrogate or placeholder to control access to a real object
- The proxy implements the same interface as the original and adds behavior such as authorization, logging, caching, or lazy loading
β Motivationβ
- Control access or precondition checks before delegating to the actual service
- Add cross-cutting concerns without changing the original implementation
β When to Useβ
- You need to add authentication, caching, logging, etc. around a service
- You want to delay resource-heavy initialization
- You want to wrap external services for uniform access
β Code Exampleβ
- TypeScript
- PHP
- Python
interface UserProfile {
load(userId: string): object;
}
// ζ¬δ½
class UserProfileService implements UserProfile {
load(userId: string): object {
console.log(`γγγγ£γΌγ«εεΎ: ${userId}`);
return { name: "Hiroshi", id: userId };
}
}
// ProxyοΌθͺθ¨Όγ¨η£ζ»γζ
ε½οΌ
class UserProfileProxy implements UserProfile {
private real = new UserProfileService();
private auth = new AuthService();
private audit = new AuditService();
load(userId: string): object {
if (!this.auth.authenticate(userId)) {
throw new Error("θͺθ¨Όε€±ζ");
}
const profile = this.real.load(userId);
this.audit.log("γγγγ£γΌγ«θ‘¨η€Ί");
return profile;
}
}
// ε©η¨δΎ
const proxy = new UserProfileProxy();
const profile = proxy.load("user-123");
console.log(profile);
<?php
interface UserProfile {
public function load(string $userId): array;
}
class UserProfileService implements UserProfile {
public function load(string $userId): array {
echo "γγγγ£γΌγ«εεΎ: {$userId}\n";
return ["name" => "Hiroshi", "id" => $userId];
}
}
class UserProfileProxy implements UserProfile {
private UserProfileService $real;
private AuthService $auth;
private AuditService $audit;
public function __construct() {
$this->real = new UserProfileService();
$this->auth = new AuthService();
$this->audit = new AuditService();
}
public function load(string $userId): array {
if (!$this->auth->authenticate($userId)) {
throw new Exception("θͺθ¨Όε€±ζ");
}
$profile = $this->real->load($userId);
$this->audit->log("γγγγ£γΌγ«θ‘¨η€Ί");
return $profile;
}
}
// ε©η¨δΎ
$proxy = new UserProfileProxy();
$profile = $proxy->load("user-123");
print_r($profile);
from abc import ABC, abstractmethod
class UserProfile(ABC):
@abstractmethod
def load(self, user_id: str) -> dict:
pass
class UserProfileService(UserProfile):
def load(self, user_id: str) -> dict:
print(f"γγγγ£γΌγ«εεΎ: {user_id}")
return {"name": "Hiroshi", "id": user_id}
class UserProfileProxy(UserProfile):
def __init__(self):
self._real = UserProfileService()
self._auth = AuthService()
self._audit = AuditService()
def load(self, user_id: str) -> dict:
if not self._auth.authenticate(user_id):
raise Exception("θͺθ¨Όε€±ζ")
profile = self._real.load(user_id)
self._audit.log("γγγγ£γΌγ«θ‘¨η€Ί")
return profile
# ε©η¨δΎ
proxy = UserProfileProxy()
profile = proxy.load("user-123")
print(profile)
β Explanationβ
This code applies the Proxy
pattern to wrap access to the UserProfileService
behind a UserProfileProxy
, adding authentication and audit logging.
This allows the client to interact with a simplified and safe interface, while the proxy takes care of verifying access and tracking actions.
1. Proxy Pattern Overviewβ
-
Subject: Common interface used by both the real service and the proxy
- Here:
UserProfile
- Here:
-
RealSubject: The actual object that performs the operation
- Here:
UserProfileService
- Here:
-
Proxy: Controls access to the
RealSubject
- Here:
UserProfileProxy
- Here:
-
Client: Uses the
Subject
without needing to know whether itβs the real object or a proxy- Here:
proxy.load("user-123")
- Here:
2. Key Classes and Responsibilitiesβ
-
UserProfile
- Common interface with a
load(userId: string): object
method
- Common interface with a
-
UserProfileService
- Implements actual logic to retrieve user profile data
-
UserProfileProxy
- Checks authorization, calls the real service, and logs the operation
-
AuthService
- Performs authentication checks
-
AuditService
- Logs access attempts
3. UML Class Diagramβ
4. Benefits of the Proxy Patternβ
- Access Control: Prevent unauthorized access to the real object
- Transparent Interception: Add logging, validation, or monitoring without changing client code
- Clean Separation: Keeps real logic separate from operational concerns
This design is ideal when you need to intercept access to sensitive or complex components, and want to keep client code clean and decoupled from infrastructure or policy.