Skip to content

Commit 8df3ca1

Browse files
Proxy Pattern Blog Added
1 parent 25511f7 commit 8df3ca1

File tree

4 files changed

+352
-0
lines changed

4 files changed

+352
-0
lines changed
65.5 KB
Loading

content/Images/remote_proxy.png

70.8 KB
Loading
92.1 KB
Loading

content/Proxy Pattern.md

Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,355 @@
11
---
22
title: Proxy Pattern
3+
tags:
4+
- structural
5+
created: 2025-12-12
36
---
7+
## Definition
8+
9+
**Proxy Pattern** provides a surrogate or placeholder for another object to control access to it.
10+
11+
---
12+
## Real-World Analogy
13+
14+
In the previous section on the [[State Pattern]], we built a Gumball Machine example. In this pattern, we will reuse the same Gumball Machine and extend it with remote monitoring capabilities.
15+
16+
Now, imagine that you want to control or monitor a Gumball Machine remotely, for example, checking its current state, the number of gumballs available, or its location. This can be achieved using a **Remote Proxy**.
17+
18+
A Remote Proxy acts as a local representative for an object that exists in another Java Virtual Machine (JVM). Internally, this is implemented using **Remote Method Invocation (RMI)**.
19+
20+
![[remote_proxy.png]]
21+
22+
The diagram above shows the Remote Proxy setup for the Gumball Monitor.
23+
24+
Here, `GumballMonitor` acts as the client and communicates with the `GumballMachine` through a Remote Proxy. When the monitor invokes a method, the call is forwarded to the proxy. The proxy serializes the method call and sends it to the remote server. The server then invokes the method on the real `GumballMachine` and sends the result back to the client. This indirection is the essence of the Proxy Pattern.
25+
### How This Works
26+
![[remote_proxy_working.png]]
27+
28+
The diagram above illustrates the runtime behavior of a Remote Proxy. The flow is as follows:
29+
30+
1. The client calls a method on the remote object, for example `doSomething()`, as if it were a local method call.
31+
2. The `RemoteProxy` intercepts the call, serializes the method name, arguments, and related metadata, and sends it to the `RemoteServer`.
32+
3. The `RemoteServer` deserializes the request and invokes the corresponding method on the real `GumballMachine`.
33+
4. Once the method execution is complete, the result is returned to the server layer.
34+
5. The server packages the result and sends it back to the `RemoteProxy`.
35+
6. The proxy deserializes the response and returns it to the client (`GumballMonitor`).
36+
### Java RMI
37+
38+
RMI stands for **Remote Method Invocation**. It allows a client to invoke methods on an object that resides in a different JVM. Although RMI is considered an older technology and is not commonly used in modern systems, it is still very useful for learning and understanding the Remote Proxy pattern.
39+
40+
In RMI terminology, the client-side helper is called a _stub_, and the server-side helper is called a _skeleton_. The diagram below illustrates this relationship.
41+
42+
![[RMI_nomenclature.png]]
43+
44+
Next, we will implement the Gumball Monitor using the code from the [[State Pattern]].
45+
46+
---
47+
## Design
48+
49+
The class diagram for the Proxy Pattern is straightforward. The `Subject` interface defines the operations that are implemented by both the `RealSubject` and the `Proxy`. The client interacts only with the `Subject` interface, remaining unaware of whether it is working with the real object or the proxy.
50+
51+
```mermaid
52+
classDiagram
53+
direction LR
54+
55+
class Subject {
56+
<<interface>>
57+
+request()
58+
}
59+
60+
class RealSubject {
61+
+request()
62+
}
63+
64+
class Proxy {
65+
-realSubject : RealSubject
66+
+request()
67+
}
68+
69+
Subject <|.. RealSubject
70+
Subject <|.. Proxy
71+
Proxy *-- RealSubject
72+
```
73+
74+
---
75+
## Implementation in Java
76+
77+
We start by creating the `ProxyRemote` interface, which will be implemented by both the proxy and the `GumballMachine`. This interface plays the same role as the `Subject` interface shown in the design section.
78+
79+
Extending `Remote` marks this interface as a remote interface in RMI.
80+
```java title="ProxyRemote.java"
81+
import proxy.state.State;
82+
import java.rmi.Remote;
83+
import java.rmi.RemoteException;
84+
85+
public interface ProxyRemote extends Remote {
86+
int getCount() throws RemoteException;
87+
String getLocation() throws RemoteException;
88+
State getState() throws RemoteException;
89+
}
90+
```
91+
92+
The `getState()` method returns a custom `State` object. For RMI to work correctly, all non-primitive return types must be serializable. Primitive types and standard Java types are already handled by the RMI framework.
93+
94+
To make `State` serializable, we extend the `Serializable` interface from the `java.io` package.
95+
```java title="State.java"
96+
import java.io.Serializable;
97+
98+
public interface State extends Serializable {
99+
void insertQuarter();
100+
void ejectQuarter();
101+
void dispense();
102+
void turncrank();
103+
}
104+
```
105+
106+
At this point, the custom data type can be transferred across the network.
107+
108+
However, each concrete `State` implementation holds a reference to the `GumballMachine` so that it can trigger state transitions. We do not want the entire `GumballMachine` object to be serialized and sent over the network along with the state.
109+
110+
To prevent this, the reference to `GumballMachine` is marked as `transient` in each state class.
111+
112+
```java title="HasQuarterState.java"
113+
class HasQuarterState implements State {
114+
private static final long serialVersionUID = 2L;
115+
private final transient GumballMachine gumballMachine;
116+
// remaining code
117+
}
118+
```
119+
120+
```java title="NoQuarterState.java"
121+
class NoQuarterState implements State {
122+
private static final long serialVersionUID = 2L;
123+
private final transient GumballMachine gumballMachine;
124+
// remaining code
125+
}
126+
```
127+
128+
```java title="SoldOutState.java"
129+
class SoldOutState implements State {
130+
private static final long serialVersionUID = 2L;
131+
private final transient GumballMachine gumballMachine;
132+
// remaining code
133+
}
134+
```
135+
136+
```java title="SoldState.java"
137+
class SoldState implements State {
138+
private static final long serialVersionUID = 2L;
139+
private final transient GumballMachine gumballMachine;
140+
// remaining code
141+
}
142+
```
143+
144+
```java title="WinnerState.java"
145+
class WinnerState implements State {
146+
private static final long serialVersionUID = 2L;
147+
private final transient GumballMachine gumballMachine;
148+
// remaining code
149+
}
150+
```
151+
152+
The `transient` keyword tells the JVM not to include this field during serialization. When the object is deserialized, the transient field is initialized with its default value, avoiding unnecessary data transfer.
153+
154+
Next, the `GumballMachine` is exposed as a remote service. To do this, it implements the `ProxyRemote` interface and extends `UnicastRemoteObject`, which provides the RMI infrastructure.
155+
156+
```java title="GumballMachine.java"
157+
public class GumballMachine extends UnicastRemoteObject implements ProxyRemote {
158+
private static final long serialVersionUID = 2L;
159+
160+
// other private variables
161+
162+
public GumballMachine(String location, int numberOfGumballs) throws RemoteException {
163+
// initialization and state setup
164+
}
165+
}
166+
```
167+
168+
Here, extending `UnicastRemoteObject` allows the object to receive remote method calls, while implementing `ProxyRemote` exposes the methods that clients are allowed to invoke.
169+
### Registering with the RMI Registry
170+
171+
The next step is to register the `GumballMachine` with the RMI Registry so that clients can discover it.
172+
```java title="RemoteServer.java"
173+
public class RemoteServer {
174+
public static void main(String[] args) {
175+
Remote remote;
176+
int count = 5;
177+
String location = "localhost";
178+
179+
try {
180+
remote = new GumballMachine(location, count);
181+
Naming.rebind("//" + location + "/gumballmachine", remote);
182+
} catch (Exception e) {
183+
e.printStackTrace();
184+
}
185+
}
186+
}
187+
```
188+
189+
This makes the remote object available at `rmi://localhost/gumballmachine`.
190+
Before running the server, the RMI Registry must be started.
191+
```bash
192+
start rmiregistry 1099
193+
```
194+
This command starts the RMI Registry on port `1099`.
195+
After starting the registry, run `RemoteServer.java` to register the `GumballMachine`.
196+
### Creating the Monitor (Client-Side Proxy)
197+
198+
The monitor class allows us to observe the `GumballMachine` through the exposed remote methods. It interacts only with the `ProxyRemote` interface, which makes it act as a proxy client.
199+
```java title="RemoteMonitor.java"
200+
public class RemoteMonitor {
201+
202+
private final ProxyRemote gumballMachine;
203+
204+
public RemoteMonitor(ProxyRemote machine) {
205+
this.gumballMachine = machine;
206+
}
207+
208+
public void report() {
209+
try {
210+
System.out.println("Gumball Machine: " + gumballMachine.getLocation());
211+
System.out.println("Gumball Machine Inventory: " + gumballMachine.getCount() + " gumballs");
212+
System.out.println("Current State: " + gumballMachine.getState());
213+
} catch (RemoteException re) {
214+
re.printStackTrace();
215+
}
216+
}
217+
}
218+
```
219+
This class typically runs on a different machine from the server.
220+
### Creating the Client (RMI Stub User)
221+
222+
The client looks up the remote object from the RMI Registry and uses it through the `ProxyRemote` interface.
223+
```java title="RemoteTest.java"
224+
public class RemoteTest {
225+
public static void main(String[] args) {
226+
227+
String location = "rmi://localhost/gumballmachine";
228+
RemoteMonitor monitor;
229+
230+
try {
231+
ProxyRemote remote = (ProxyRemote) Naming.lookup(location);
232+
monitor = new RemoteMonitor(remote);
233+
} catch (Exception e) {
234+
throw new RuntimeException(e);
235+
}
236+
237+
monitor.report();
238+
}
239+
}
240+
```
241+
This code retrieves the proxy reference and allows the client to interact with the remote `GumballMachine` as if it were local.
242+
243+
_Output:_
244+
```txt
245+
Gumball Machine: localhost
246+
Gumball Machine Inventory: 5 gumballs
247+
Current State: NoQuarterState
248+
```
249+
250+
---
251+
## Implementation in Java – 2
252+
253+
In the previous implementation, we used **Remote Method Invocation (RMI)** to demonstrate the Proxy Pattern. In this section, we will implement a simpler proxy based on the [[#Design]] shown earlier. This example demonstrates a **Cache (Virtual) Proxy**.
254+
255+
```java title="Subject.java"
256+
interface Subject {
257+
void request();
258+
}
259+
```
260+
261+
This interface defines the common contract that both the real object and the proxy must follow.
262+
```java title="RealSubject.java"
263+
class RealSubject implements Subject {
264+
private String resource;
265+
266+
public RealSubject(String resource) {
267+
this.resource = resource;
268+
this.loadResource();
269+
}
270+
271+
private void loadResource() {
272+
System.out.println("Loading the Resource: " + this.resource);
273+
}
274+
275+
@Override
276+
public void request() {
277+
System.out.println("Requested: " + this.resource);
278+
}
279+
}
280+
```
281+
The `RealSubject` represents the actual object that performs the expensive operation of loading a resource.
282+
```java title="Proxy.java"
283+
class Proxy implements Subject {
284+
private Subject subject;
285+
private String resource;
286+
287+
public Proxy(String resource) {
288+
this.resource = resource;
289+
}
290+
291+
@Override
292+
public void request() {
293+
if (subject == null) {
294+
subject = new RealSubject(resource);
295+
}
296+
subject.request();
297+
}
298+
}
299+
```
300+
The proxy delays the creation of the `RealSubject` until it is actually needed and then delegates subsequent calls directly to it.
301+
```java title="ProxyPattern.java"
302+
public class ProxyPattern {
303+
public static void main(String[] args) {
304+
Subject subject = new Proxy("new pdf download");
305+
subject.request();
306+
subject.request();
307+
}
308+
}
309+
```
310+
_Output:_
311+
```txt
312+
Loading the Resource: new pdf download
313+
Requested: new pdf download
314+
Requested: new pdf download
315+
```
316+
On the first request, the resource is loaded. On subsequent requests, the already-created object is reused.
317+
318+
---
319+
## Types of Proxy
320+
321+
There are several commonly used types of proxies:
322+
### Remote Proxy:
323+
With the Remote Proxy, the proxy acts as a local representative for an object that lives in a different JVM. A method call on the proxy results in the call being transferred over the wire and invoked remotely, and the result being returned back to the proxy and then to the client.
324+
### Virtual Proxy:
325+
The Virtual Proxy acts as a representative for an object that may be expensive to create. The virtual Proxy often defers the creation of the object until it is needed; the Virtual Proxy also acts as a surrogate for the object before and while it is being created. After that, the proxy delegates requests directly to the RealSubject.
326+
**Example**: Loading a Large Image only when it is displayed on the Screen
327+
### Protection Proxy:
328+
A Protection Proxy is used to control access to an object. Before forwarding a request to the Real Subject, the proxy checks whether the user has the required permissions. If the user is authorized, the request is allowed; otherwise it is blocked.
329+
### Cache Proxy:
330+
A Cache Proxy is used to store the results of expensive operations. When a request is made, the proxy first checks whether the result is already stored in the cache. If it is, the cached result is returned immediately. If not, the request is sent to the real object, and the result is saved for future use.
331+
**Example:** Caching API responses or database query results.
332+
333+
Other variants include firewall proxies, synchronization proxies, and smart proxies.
334+
335+
---
336+
## Real-World Examples
337+
338+
Proxy patterns are widely used in modern frameworks and applications, often without us explicitly realizing it.
339+
340+
Object-Relational Mapping (ORM) frameworks such as Entity Framework Core in .NET and Hibernate in Java use proxies to manage database interactions and lazy loading of entities.
341+
342+
Proxies are also heavily used in Dependency Injection frameworks, where objects are wrapped to add cross-cutting concerns such as logging, security, or transaction management.
343+
344+
---
345+
## Design Principles:
346+
347+
- **Encapsulate What Varies** - Identify the parts of the code that are going to change and encapsulate them into separate class just like the Strategy Pattern.
348+
- **Favor Composition Over Inheritance** - Instead of using inheritance on extending functionality, rather use composition by delegating behavior to other objects.
349+
- **Program to Interface not Implementations** - Write code that depends on Abstractions or Interfaces rather than Concrete Classes.
350+
- **Strive for Loosely coupled design between objects that interact** - When implementing a class, avoid tightly coupled classes. Instead, use loosely coupled objects by leveraging abstractions and interfaces. This approach ensures that the class does not heavily depend on other classes.
351+
- **Classes Should be Open for Extension But closed for Modification** - Design your classes so you can extend their behavior without altering their existing, stable code.
352+
- **Depend on Abstractions, Do not depend on concrete class** - Rely on interfaces or abstract types instead of concrete classes so you can swap implementations without altering client code.
353+
- **Talk Only To Your Friends** - An object may only call methods on itself, its direct components, parameters passed in, or objects it creates.
354+
- **Don't call us, we'll call you** - This means the framework controls the flow of execution, not the user’s code (Inversion of Control).
355+
- **A class should have only one reason to change** - This emphasizes the Single Responsibility Principle, ensuring each class focuses on just one functionality.

0 commit comments

Comments
 (0)