Card 03/ 09
Proxy Template in Python, Java and TypeScript
A virtual proxy that creates the real object on the first request and reuses it after.
The Proxy template is a virtual proxy in its pure form, with the participant names from the class diagram. The real subject announces its own creation, so the output shows exactly when it happens. The steps add each participant in Python, and the tabs give the complete program in three languages, each with its output.
Building the template one participant at a time
Subject
The interface both the proxy and the real subject implement.
class Subject(ABC):
@abstractmethod
def request(self) -> str: ...Real subject
The object whose creation is worth putting off.
class RealSubject(Subject):
def __init__(self) -> None:
print("RealSubject created, an expensive step")Proxy
Creates the real subject on the first request, then forwards.
class Proxy(Subject):
def __init__(self) -> None:
self._real: RealSubject | None = None
def request(self) -> str:
if self._real is None:
self._real = RealSubject()
return self._real.request()Client
Uses the subject interface and is given the proxy.
proxy = Proxy()
client(proxy)
client(proxy)The complete template
from abc import ABC, abstractmethod
class Subject(ABC):
@abstractmethod
def request(self) -> str: ...
class RealSubject(Subject):
def __init__(self) -> None:
print("RealSubject created, an expensive step")
def request(self) -> str:
return "RealSubject handled the request"
class Proxy(Subject):
def __init__(self) -> None:
self._real: RealSubject | None = None
def request(self) -> str:
if self._real is None:
self._real = RealSubject()
return self._real.request()
def client(subject: Subject) -> None:
print(subject.request())
proxy = Proxy()
print("proxy created, nothing loaded yet")
client(proxy)
client(proxy)proxy created, nothing loaded yet
RealSubject created, an expensive step
RealSubject handled the request
RealSubject handled the requestpublic class PxTemplate {
static void client(Subject subject) {
System.out.println(subject.request());
}
public static void main(String[] args) {
Subject proxy = new Proxy();
System.out.println("proxy created, nothing loaded yet");
client(proxy);
client(proxy);
}
}
interface Subject {
String request();
}
class RealSubject implements Subject {
RealSubject() {
System.out.println("RealSubject created, an expensive step");
}
public String request() {
return "RealSubject handled the request";
}
}
class Proxy implements Subject {
private RealSubject real;
public String request() {
if (real == null) {
real = new RealSubject();
}
return real.request();
}
}proxy created, nothing loaded yet
RealSubject created, an expensive step
RealSubject handled the request
RealSubject handled the requestinterface Subject {
request(): string;
}
class RealSubject implements Subject {
constructor() {
console.log("RealSubject created, an expensive step");
}
request(): string {
return "RealSubject handled the request";
}
}
class SubjectProxy implements Subject {
private real: RealSubject | null = null;
request(): string {
this.real ??= new RealSubject();
return this.real.request();
}
}
function client(subject: Subject): void {
console.log(subject.request());
}
const proxy: Subject = new SubjectProxy();
console.log("proxy created, nothing loaded yet");
client(proxy);
client(proxy);proxy created, nothing loaded yet
RealSubject created, an expensive step
RealSubject handled the request
RealSubject handled the requestIn each output the proxy exists before the real subject does, the real subject is created once, during the first request, and both requests are handled. The client's code is identical to what it would be with a RealSubject, which is what lets a proxy be added in front of an existing object without editing its callers.
The template is not safe for threads: two first requests at once could both find nothing and create two real subjects. A proxy used from several threads needs a lock around the check, the same race a lazily created singleton has.
← → move through sections and cards