From 8471913dbded41ba9edd1d2d8fdea0dc42e3a552 Mon Sep 17 00:00:00 2001 From: Ankur Date: Wed, 24 Jun 2026 14:19:36 +0530 Subject: [PATCH] Sync Adapter to Java 25: add StripePaymentClassAdapter, README mapping --- 02-structural/adapter/Main.java | 11 --------- 02-structural/adapter/OrderService.java | 8 ------- 02-structural/adapter/PaymentGateway.java | 2 -- 02-structural/adapter/README.md | 17 +++++++------ 02-structural/adapter/StripeClient.java | 12 ---------- .../adapter/StripePaymentAdapter.java | 24 +++++++------------ .../adapter/StripePaymentClassAdapter.java | 23 ++++++++++++++++++ README.md | 19 +++++++++++++++ 8 files changed, 60 insertions(+), 56 deletions(-) create mode 100644 02-structural/adapter/StripePaymentClassAdapter.java diff --git a/02-structural/adapter/Main.java b/02-structural/adapter/Main.java index b41cfca..523efe6 100644 --- a/02-structural/adapter/Main.java +++ b/02-structural/adapter/Main.java @@ -1,14 +1,5 @@ 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) { @@ -28,8 +19,6 @@ public class Main { // --- 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."); diff --git a/02-structural/adapter/OrderService.java b/02-structural/adapter/OrderService.java index 640371d..934b92e 100644 --- a/02-structural/adapter/OrderService.java +++ b/02-structural/adapter/OrderService.java @@ -1,25 +1,17 @@ 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); diff --git a/02-structural/adapter/PaymentGateway.java b/02-structural/adapter/PaymentGateway.java index 29223bc..f0fa7c3 100644 --- a/02-structural/adapter/PaymentGateway.java +++ b/02-structural/adapter/PaymentGateway.java @@ -1,9 +1,7 @@ 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); diff --git a/02-structural/adapter/README.md b/02-structural/adapter/README.md index 0ea8249..445f9a8 100644 --- a/02-structural/adapter/README.md +++ b/02-structural/adapter/README.md @@ -15,17 +15,20 @@ javac adapter/*.java java adapter.Main ``` -Requires Java 11+. +Requires Java 25. ## Files -| File | Role | +| Post Section | File(s) | |---|---| -| `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 | +| Step 1 — Define the Target Interface | `PaymentGateway.java` | +| Step 2 — The Adaptee (What You're Wrapping) | `StripeClient.java` | +| Step 3 — The Object Adapter | `StripePaymentAdapter.java` | +| Step 4 — The Client (Knows Nothing About Stripe) | `OrderService.java` | +| Putting It Together: The Main Demo | `Main.java` | +| Object Adapter vs Class Adapter | `StripePaymentClassAdapter.java` | + +Note: the `InputStreamReader` JDK-adapter snippet is illustrative only — it is not part of this repository's runnable example. ## See Also diff --git a/02-structural/adapter/StripeClient.java b/02-structural/adapter/StripeClient.java index 3db71da..72b893b 100644 --- a/02-structural/adapter/StripeClient.java +++ b/02-structural/adapter/StripeClient.java @@ -1,47 +1,35 @@ 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; diff --git a/02-structural/adapter/StripePaymentAdapter.java b/02-structural/adapter/StripePaymentAdapter.java index 7f33ca6..5a586dd 100644 --- a/02-structural/adapter/StripePaymentAdapter.java +++ b/02-structural/adapter/StripePaymentAdapter.java @@ -1,42 +1,34 @@ package adapter; - /** - * The Adapter — bridges StripeClient (Adaptee) to PaymentGateway (Target). + * The Adapter — bridges StripeClient to PaymentGateway. * - * 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. + * Object Adapter variant: holds a StripeClient instance via composition. + * This means it can wrap any StripeClient including subclasses. */ 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 + // Translation 1: dollars → cents long amountInCents = Math.round(amount * 100); + // Translation 2: charge() → createCharge(), lowercase currency StripeClient.StripeChargeResult result = stripe.createCharge(customerId, amountInCents, currency.toLowerCase()); + // Translation 3: StripeChargeResult → boolean return result.success; } - @Override public boolean refund(String transactionId, double amount) { - // Translation: your "transactionId" is Stripe's "chargeId" long amountInCents = Math.round(amount * 100); + // Translation: refund() → issueRefund(), your "transactionId" is Stripe's "chargeId" return stripe.issueRefund(transactionId, amountInCents); } - @Override public String getStatus(String transactionId) { - // Translation: map Stripe's object to your simple status string + // Translation: getStatus() → retrieveCharge(), StripeChargeResult → String StripeClient.StripeChargeResult result = stripe.retrieveCharge(transactionId); if (!result.success) return "FAILED"; return result.status != null ? result.status.toUpperCase() : "UNKNOWN"; diff --git a/02-structural/adapter/StripePaymentClassAdapter.java b/02-structural/adapter/StripePaymentClassAdapter.java new file mode 100644 index 0000000..28324b3 --- /dev/null +++ b/02-structural/adapter/StripePaymentClassAdapter.java @@ -0,0 +1,23 @@ +package adapter; +// Class Adapter: extends Adaptee, implements Target +// Can only be used when you CAN extend the Adaptee (it's not final) +public class StripePaymentClassAdapter extends StripeClient implements PaymentGateway { + public StripePaymentClassAdapter(String apiKey) { + super(apiKey); + } + @Override + public boolean charge(String customerId, double amount, String currency) { + long cents = Math.round(amount * 100); + return createCharge(customerId, cents, currency.toLowerCase()).success; + // Note: calls inherited method directly — no 'stripe.' prefix needed + } + @Override + public boolean refund(String transactionId, double amount) { + return issueRefund(transactionId, Math.round(amount * 100)); + } + @Override + public String getStatus(String transactionId) { + StripeChargeResult r = retrieveCharge(transactionId); + return r.success ? (r.status != null ? r.status.toUpperCase() : "UNKNOWN") : "FAILED"; + } +} diff --git a/README.md b/README.md index 2f80d8c..97dfda6 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,25 @@ javac 01-creational/singleton/*.java -d out/singleton java -cp out/singleton singleton.Main ``` +### Adapter (`02-structural/adapter/`) + +| Post Section | File(s) | +|---|---| +| Step 1 — Define the Target Interface | `PaymentGateway.java` | +| Step 2 — The Adaptee (What You're Wrapping) | `StripeClient.java` | +| Step 3 — The Object Adapter | `StripePaymentAdapter.java` | +| Step 4 — The Client (Knows Nothing About Stripe) | `OrderService.java` | +| Putting It Together: The Main Demo | `Main.java` | +| Object Adapter vs Class Adapter | `StripePaymentClassAdapter.java` | + +Note: the `InputStreamReader` JDK-adapter snippet is illustrative only — it is not part of this repository's runnable example. + +Run it: +```bash +javac 02-structural/adapter/*.java -d out/adapter +java -cp out/adapter adapter.Main +``` + ## Reference - *Design Patterns: Elements of Reusable Object-Oriented Software* — Gamma, Helm, Johnson, Vlissides