Add all 23 GoF design pattern implementations (2026-06-13)
This commit is contained in:
38
02-structural/adapter/Main.java
Normal file
38
02-structural/adapter/Main.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package adapter;
|
||||
|
||||
/**
|
||||
* Adapter Design Pattern — Runnable Demo
|
||||
*
|
||||
* Demonstrates wrapping an incompatible StripeClient behind
|
||||
* the PaymentGateway interface your application expects.
|
||||
*
|
||||
* Run: javac adapter/*.java && java adapter.Main
|
||||
* Article: https://ankurm.com/adapter-design-pattern-java/
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== Adapter Design Pattern Demo ===\n");
|
||||
|
||||
// --- Object Adapter: Stripe ---
|
||||
System.out.println("-- Using Stripe via Adapter --");
|
||||
StripeClient stripeClient = new StripeClient("sk_test_4eC39HqLyjWDarjtT1zdp7dc");
|
||||
PaymentGateway stripeGateway = new StripePaymentAdapter(stripeClient);
|
||||
|
||||
OrderService orderService = new OrderService(stripeGateway);
|
||||
orderService.processOrder("ORD-001", "cus_abc123", 99.99);
|
||||
|
||||
System.out.println("\n-- Testing refund --");
|
||||
boolean refunded = stripeGateway.refund("ch_ORD-001", 99.99);
|
||||
System.out.println("Refund issued: " + refunded);
|
||||
|
||||
// --- JDK Example: InputStreamReader as Adapter ---
|
||||
System.out.println("\n-- JDK Adapter: InputStreamReader --");
|
||||
// InputStreamReader adapts InputStream (byte-based) to Reader (char-based)
|
||||
// The client (BufferedReader) only knows about Reader, not InputStream
|
||||
System.out.println("InputStreamReader wraps System.in (InputStream) as a Reader.");
|
||||
System.out.println("Your code reads chars; the adapter handles byte-to-char conversion.");
|
||||
|
||||
System.out.println("\n=== Demo complete ===");
|
||||
}
|
||||
}
|
||||
31
02-structural/adapter/OrderService.java
Normal file
31
02-structural/adapter/OrderService.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package adapter;
|
||||
|
||||
/**
|
||||
* The Client — uses only the PaymentGateway interface.
|
||||
* It has no idea whether it's talking to Stripe, PayPal, or Braintree.
|
||||
* This is the point: the client is completely isolated from the vendor.
|
||||
*/
|
||||
public class OrderService {
|
||||
|
||||
private final PaymentGateway gateway;
|
||||
|
||||
// Receives a PaymentGateway — could be Stripe, PayPal, anything
|
||||
public OrderService(PaymentGateway gateway) {
|
||||
this.gateway = gateway;
|
||||
}
|
||||
|
||||
public void processOrder(String orderId, String customerId, double total) {
|
||||
System.out.printf("%nProcessing order %s for customer %s, total: $%.2f%n",
|
||||
orderId, customerId, total);
|
||||
|
||||
boolean charged = gateway.charge(customerId, total, "USD");
|
||||
|
||||
if (charged) {
|
||||
System.out.println("Payment accepted. Order confirmed.");
|
||||
String status = gateway.getStatus("ch_" + orderId);
|
||||
System.out.println("Transaction status: " + status);
|
||||
} else {
|
||||
System.out.println("Payment failed. Order rejected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
12
02-structural/adapter/PaymentGateway.java
Normal file
12
02-structural/adapter/PaymentGateway.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package adapter;
|
||||
|
||||
/**
|
||||
* The Target interface — what YOUR application's code expects.
|
||||
* Every payment gateway in your system must implement this.
|
||||
* Written to handle modern async-style payment flows.
|
||||
*/
|
||||
public interface PaymentGateway {
|
||||
boolean charge(String customerId, double amount, String currency);
|
||||
boolean refund(String transactionId, double amount);
|
||||
String getStatus(String transactionId);
|
||||
}
|
||||
33
02-structural/adapter/README.md
Normal file
33
02-structural/adapter/README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Adapter Design Pattern — Java Example
|
||||
|
||||
**Pattern:** Structural → Adapter
|
||||
**Article:** https://ankurm.com/adapter-design-pattern-java/
|
||||
|
||||
## What this example shows
|
||||
|
||||
Wraps a third-party `StripeClient` (with its own API) behind a `PaymentGateway` interface that your application code expects. The `OrderService` client never touches `StripeClient` directly — it only sees `PaymentGateway`. Swapping payment providers requires changing one line.
|
||||
|
||||
## How to run
|
||||
|
||||
```bash
|
||||
# From this folder:
|
||||
javac adapter/*.java
|
||||
java adapter.Main
|
||||
```
|
||||
|
||||
Requires Java 11+.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `PaymentGateway.java` | Target interface (what your app expects) |
|
||||
| `StripeClient.java` | Adaptee (third-party SDK you can't modify) |
|
||||
| `StripePaymentAdapter.java` | Adapter (bridges the two) |
|
||||
| `OrderService.java` | Client (only uses PaymentGateway) |
|
||||
| `Main.java` | Demo entry point |
|
||||
|
||||
## See Also
|
||||
|
||||
- Full article: https://ankurm.com/adapter-design-pattern-java/
|
||||
- All design patterns: https://ankurm.com/design-patterns-java/
|
||||
51
02-structural/adapter/StripeClient.java
Normal file
51
02-structural/adapter/StripeClient.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package adapter;
|
||||
|
||||
/**
|
||||
* The Adaptee — a third-party payment SDK with a completely different interface.
|
||||
* Imagine this is Stripe's actual SDK: you cannot modify this class,
|
||||
* and it doesn't implement PaymentGateway.
|
||||
*
|
||||
* In real projects, this would be a JAR you depend on.
|
||||
*/
|
||||
public class StripeClient {
|
||||
|
||||
private final String apiKey;
|
||||
|
||||
public StripeClient(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
System.out.println("[Stripe] Initialized with key: " + apiKey.substring(0, 8) + "...");
|
||||
}
|
||||
|
||||
// Stripe uses cents, not decimal amounts
|
||||
public StripeChargeResult createCharge(String customerId, long amountInCents, String currency) {
|
||||
System.out.printf("[Stripe] Charging customer=%s, amount=%d cents, currency=%s%n",
|
||||
customerId, amountInCents, currency);
|
||||
// Simulate success
|
||||
return new StripeChargeResult("ch_" + System.currentTimeMillis(), true, null);
|
||||
}
|
||||
|
||||
// Stripe's refund method takes a charge ID and uses different naming
|
||||
public boolean issueRefund(String chargeId, long amountInCents) {
|
||||
System.out.printf("[Stripe] Refunding charge=%s, amount=%d cents%n", chargeId, amountInCents);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Stripe uses 'retrieve' not 'getStatus', and returns an object
|
||||
public StripeChargeResult retrieveCharge(String chargeId) {
|
||||
System.out.printf("[Stripe] Retrieving charge=%s%n", chargeId);
|
||||
return new StripeChargeResult(chargeId, true, "succeeded");
|
||||
}
|
||||
|
||||
// Stripe-specific result object — nothing in common with your domain
|
||||
public static class StripeChargeResult {
|
||||
public final String chargeId;
|
||||
public final boolean success;
|
||||
public final String status;
|
||||
|
||||
public StripeChargeResult(String chargeId, boolean success, String status) {
|
||||
this.chargeId = chargeId;
|
||||
this.success = success;
|
||||
this.status = status;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
02-structural/adapter/StripePaymentAdapter.java
Normal file
44
02-structural/adapter/StripePaymentAdapter.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package adapter;
|
||||
|
||||
/**
|
||||
* The Adapter — bridges StripeClient (Adaptee) to PaymentGateway (Target).
|
||||
*
|
||||
* This is the Object Adapter variant: it holds a StripeClient instance
|
||||
* via composition (not inheritance), so it can adapt any StripeClient
|
||||
* including subclasses.
|
||||
*
|
||||
* The key responsibility: translate YOUR interface's methods into
|
||||
* calls that Stripe understands — data conversion included.
|
||||
*/
|
||||
public class StripePaymentAdapter implements PaymentGateway {
|
||||
|
||||
private final StripeClient stripe;
|
||||
|
||||
public StripePaymentAdapter(StripeClient stripe) {
|
||||
this.stripe = stripe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean charge(String customerId, double amount, String currency) {
|
||||
// Translation: your code uses decimal dollars; Stripe wants integer cents
|
||||
long amountInCents = Math.round(amount * 100);
|
||||
StripeClient.StripeChargeResult result =
|
||||
stripe.createCharge(customerId, amountInCents, currency.toLowerCase());
|
||||
return result.success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean refund(String transactionId, double amount) {
|
||||
// Translation: your "transactionId" is Stripe's "chargeId"
|
||||
long amountInCents = Math.round(amount * 100);
|
||||
return stripe.issueRefund(transactionId, amountInCents);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStatus(String transactionId) {
|
||||
// Translation: map Stripe's object to your simple status string
|
||||
StripeClient.StripeChargeResult result = stripe.retrieveCharge(transactionId);
|
||||
if (!result.success) return "FAILED";
|
||||
return result.status != null ? result.status.toUpperCase() : "UNKNOWN";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user