Add all 23 GoF design pattern implementations
This commit is contained in:
11
02-structural/proxy/DatabaseConnection.java
Normal file
11
02-structural/proxy/DatabaseConnection.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package proxy;
|
||||
|
||||
/**
|
||||
* Subject interface — defines what both the real object and proxy expose.
|
||||
* Clients depend on this, not on the concrete class.
|
||||
*/
|
||||
public interface DatabaseConnection {
|
||||
void connect();
|
||||
String executeQuery(String sql);
|
||||
void disconnect();
|
||||
}
|
||||
52
02-structural/proxy/LazyConnectionProxy.java
Normal file
52
02-structural/proxy/LazyConnectionProxy.java
Normal file
@@ -0,0 +1,52 @@
|
||||
package proxy;
|
||||
|
||||
/**
|
||||
* Virtual Proxy — delays creating the RealDatabaseConnection until
|
||||
* the first actual query is made. If no query is ever made (e.g.,
|
||||
* the service is initialized but never used in this request),
|
||||
* the expensive connection is never opened.
|
||||
*
|
||||
* This is exactly how Hibernate proxies work: entities are not
|
||||
* loaded from the database until you access a field on them.
|
||||
*/
|
||||
public class LazyConnectionProxy implements DatabaseConnection {
|
||||
|
||||
private final String url;
|
||||
private RealDatabaseConnection real; // null until first use
|
||||
|
||||
public LazyConnectionProxy(String url) {
|
||||
this.url = url;
|
||||
System.out.println("[Proxy] Created for " + url + " (real connection NOT opened yet)");
|
||||
}
|
||||
|
||||
// Lazy initialization — create and connect only on first real need
|
||||
private void initIfNeeded() {
|
||||
if (real == null) {
|
||||
System.out.println("[Proxy] First access — initializing real connection...");
|
||||
real = new RealDatabaseConnection(url);
|
||||
real.connect();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() {
|
||||
// Proxy absorbs the connect() call — real connection opened lazily
|
||||
System.out.println("[Proxy] connect() called — deferring to first query");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String executeQuery(String sql) {
|
||||
initIfNeeded(); // NOW we actually need the connection
|
||||
return real.executeQuery(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
if (real != null) {
|
||||
real.disconnect();
|
||||
real = null;
|
||||
} else {
|
||||
System.out.println("[Proxy] disconnect() called but connection was never opened");
|
||||
}
|
||||
}
|
||||
}
|
||||
41
02-structural/proxy/LoggingProxy.java
Normal file
41
02-structural/proxy/LoggingProxy.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package proxy;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* Logging Proxy — adds timing and audit logging around every query
|
||||
* without touching RealDatabaseConnection or any of its callers.
|
||||
*
|
||||
* This is the "cross-cutting concern" use case of Proxy,
|
||||
* the same mechanism behind Spring AOP's @Around advice.
|
||||
*/
|
||||
public class LoggingProxy implements DatabaseConnection {
|
||||
|
||||
private final DatabaseConnection target;
|
||||
|
||||
public LoggingProxy(DatabaseConnection target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() {
|
||||
System.out.println("[Log] connect() at " + Instant.now());
|
||||
target.connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String executeQuery(String sql) {
|
||||
long start = System.currentTimeMillis();
|
||||
System.out.println("[Log] QUERY START: " + sql);
|
||||
String result = target.executeQuery(sql);
|
||||
long elapsed = System.currentTimeMillis() - start;
|
||||
System.out.println("[Log] QUERY END: " + elapsed + "ms | result: " + result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
System.out.println("[Log] disconnect() at " + Instant.now());
|
||||
target.disconnect();
|
||||
}
|
||||
}
|
||||
51
02-structural/proxy/Main.java
Normal file
51
02-structural/proxy/Main.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package proxy;
|
||||
|
||||
/**
|
||||
* Proxy Design Pattern — Runnable Demo
|
||||
*
|
||||
* Shows two proxy types:
|
||||
* 1. Virtual Proxy (lazy connection)
|
||||
* 2. Logging Proxy (cross-cutting concern)
|
||||
* 3. Proxy chaining (both together)
|
||||
*
|
||||
* Run: javac proxy/*.java && java proxy.Main
|
||||
* Article: https://ankurm.com/proxy-design-pattern-java/
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) throws InterruptedException {
|
||||
System.out.println("=== Proxy Design Pattern Demo ===\n");
|
||||
|
||||
// --- Virtual Proxy: lazy connection ---
|
||||
System.out.println("-- Virtual Proxy (lazy loading) --");
|
||||
DatabaseConnection lazy = new LazyConnectionProxy("jdbc:postgresql://localhost/mydb");
|
||||
lazy.connect(); // absorbed by proxy, no real connection yet
|
||||
System.out.println("(no real connection yet — saved startup time)");
|
||||
System.out.println("Result: " + lazy.executeQuery("SELECT * FROM users WHERE id=1"));
|
||||
System.out.println("Result: " + lazy.executeQuery("SELECT COUNT(*) FROM orders"));
|
||||
lazy.disconnect();
|
||||
|
||||
System.out.println();
|
||||
|
||||
// --- Logging Proxy: wraps the real connection ---
|
||||
System.out.println("-- Logging Proxy --");
|
||||
DatabaseConnection real = new RealDatabaseConnection("jdbc:mysql://localhost/shopdb");
|
||||
real.connect();
|
||||
DatabaseConnection logged = new LoggingProxy(real);
|
||||
logged.executeQuery("SELECT * FROM products LIMIT 10");
|
||||
logged.disconnect();
|
||||
|
||||
System.out.println();
|
||||
|
||||
// --- Proxy chaining: lazy + logging ---
|
||||
System.out.println("-- Chained Proxies: Lazy + Logging --");
|
||||
DatabaseConnection chain =
|
||||
new LoggingProxy(
|
||||
new LazyConnectionProxy("jdbc:oracle://localhost/warehouse"));
|
||||
chain.connect();
|
||||
chain.executeQuery("SELECT SUM(quantity) FROM inventory");
|
||||
chain.disconnect();
|
||||
|
||||
System.out.println("\n=== Demo complete ===");
|
||||
}
|
||||
}
|
||||
32
02-structural/proxy/README.md
Normal file
32
02-structural/proxy/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Proxy Design Pattern — Java Example
|
||||
|
||||
**Pattern:** Structural → Proxy
|
||||
**Article:** https://ankurm.com/proxy-design-pattern-java/
|
||||
|
||||
## What this example shows
|
||||
|
||||
A `DatabaseConnection` interface is implemented by a real, expensive connection (`RealDatabaseConnection`) and by two proxies that sit in front of it: `LazyConnectionProxy` defers opening the real connection until the first query actually runs (virtual proxy), and `LoggingProxy` wraps any `DatabaseConnection` — real or proxied — and adds timing/audit logging around every call (cross-cutting concern proxy). Because both proxies implement the same interface as the real subject, they compose: `Main` chains `LoggingProxy` around a `LazyConnectionProxy` to get lazy loading and logging together.
|
||||
|
||||
## How to run
|
||||
|
||||
```bash
|
||||
javac proxy/*.java -d out/proxy
|
||||
java -cp out/proxy proxy.Main
|
||||
```
|
||||
|
||||
Requires Java 25.
|
||||
|
||||
## Post Section ↔ File Mapping
|
||||
|
||||
| Post Section | File(s) |
|
||||
|---|---|
|
||||
| The Subject Interface | `DatabaseConnection.java` |
|
||||
| The Real Subject | `RealDatabaseConnection.java` |
|
||||
| Virtual Proxy: Lazy Initialisation | `LazyConnectionProxy.java` |
|
||||
| Logging Proxy: Cross-Cutting Concerns | `LoggingProxy.java` |
|
||||
| Wiring It Together: Proxy Chaining | `Main.java` |
|
||||
|
||||
Note: the "Dynamic Proxy with java.lang.reflect.Proxy" snippet is illustrative only — it is not part of this repository's runnable example.
|
||||
|
||||
Article: https://ankurm.com/proxy-design-pattern-java/
|
||||
All patterns: https://ankurm.com/design-patterns-java/
|
||||
33
02-structural/proxy/RealDatabaseConnection.java
Normal file
33
02-structural/proxy/RealDatabaseConnection.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package proxy;
|
||||
|
||||
/**
|
||||
* Real Subject — the actual, expensive database connection.
|
||||
* Opening it takes time. We want to delay this until truly needed.
|
||||
*/
|
||||
public class RealDatabaseConnection implements DatabaseConnection {
|
||||
|
||||
private final String url;
|
||||
|
||||
public RealDatabaseConnection(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() {
|
||||
System.out.println("[Real DB] Connecting to " + url + " (expensive operation)...");
|
||||
// Simulate connection setup time
|
||||
try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
|
||||
System.out.println("[Real DB] Connected.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String executeQuery(String sql) {
|
||||
System.out.println("[Real DB] Executing: " + sql);
|
||||
return "ResultSet{rows=42}"; // simulated result
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
System.out.println("[Real DB] Disconnecting from " + url);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user