Sync Adapter to Java 25: add StripePaymentClassAdapter, README mapping
This commit is contained in:
@@ -1,14 +1,5 @@
|
|||||||
package adapter;
|
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 class Main {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
@@ -28,8 +19,6 @@ public class Main {
|
|||||||
|
|
||||||
// --- JDK Example: InputStreamReader as Adapter ---
|
// --- JDK Example: InputStreamReader as Adapter ---
|
||||||
System.out.println("\n-- JDK Adapter: InputStreamReader --");
|
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("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("Your code reads chars; the adapter handles byte-to-char conversion.");
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,17 @@
|
|||||||
package adapter;
|
package adapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Client — uses only the PaymentGateway interface.
|
* The Client — uses only the PaymentGateway interface.
|
||||||
* It has no idea whether it's talking to Stripe, PayPal, or Braintree.
|
* 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 {
|
public class OrderService {
|
||||||
|
|
||||||
private final PaymentGateway gateway;
|
private final PaymentGateway gateway;
|
||||||
|
|
||||||
// Receives a PaymentGateway — could be Stripe, PayPal, anything
|
|
||||||
public OrderService(PaymentGateway gateway) {
|
public OrderService(PaymentGateway gateway) {
|
||||||
this.gateway = gateway;
|
this.gateway = gateway;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void processOrder(String orderId, String customerId, double total) {
|
public void processOrder(String orderId, String customerId, double total) {
|
||||||
System.out.printf("%nProcessing order %s for customer %s, total: $%.2f%n",
|
System.out.printf("%nProcessing order %s for customer %s, total: $%.2f%n",
|
||||||
orderId, customerId, total);
|
orderId, customerId, total);
|
||||||
|
|
||||||
boolean charged = gateway.charge(customerId, total, "USD");
|
boolean charged = gateway.charge(customerId, total, "USD");
|
||||||
|
|
||||||
if (charged) {
|
if (charged) {
|
||||||
System.out.println("Payment accepted. Order confirmed.");
|
System.out.println("Payment accepted. Order confirmed.");
|
||||||
String status = gateway.getStatus("ch_" + orderId);
|
String status = gateway.getStatus("ch_" + orderId);
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package adapter;
|
package adapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Target interface — what YOUR application's code expects.
|
* The Target interface — what YOUR application's code expects.
|
||||||
* Every payment gateway in your system must implement this.
|
* Every payment gateway in your system must implement this.
|
||||||
* Written to handle modern async-style payment flows.
|
|
||||||
*/
|
*/
|
||||||
public interface PaymentGateway {
|
public interface PaymentGateway {
|
||||||
boolean charge(String customerId, double amount, String currency);
|
boolean charge(String customerId, double amount, String currency);
|
||||||
|
|||||||
@@ -15,17 +15,20 @@ javac adapter/*.java
|
|||||||
java adapter.Main
|
java adapter.Main
|
||||||
```
|
```
|
||||||
|
|
||||||
Requires Java 11+.
|
Requires Java 25.
|
||||||
|
|
||||||
## Files
|
## Files
|
||||||
|
|
||||||
| File | Role |
|
| Post Section | File(s) |
|
||||||
|---|---|
|
|---|---|
|
||||||
| `PaymentGateway.java` | Target interface (what your app expects) |
|
| Step 1 — Define the Target Interface | `PaymentGateway.java` |
|
||||||
| `StripeClient.java` | Adaptee (third-party SDK you can't modify) |
|
| Step 2 — The Adaptee (What You're Wrapping) | `StripeClient.java` |
|
||||||
| `StripePaymentAdapter.java` | Adapter (bridges the two) |
|
| Step 3 — The Object Adapter | `StripePaymentAdapter.java` |
|
||||||
| `OrderService.java` | Client (only uses PaymentGateway) |
|
| Step 4 — The Client (Knows Nothing About Stripe) | `OrderService.java` |
|
||||||
| `Main.java` | Demo entry point |
|
| 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
|
## See Also
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +1,35 @@
|
|||||||
package adapter;
|
package adapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Adaptee — a third-party payment SDK with a completely different interface.
|
* The Adaptee — a third-party payment SDK with a completely different interface.
|
||||||
* Imagine this is Stripe's actual SDK: you cannot modify this class,
|
* Imagine this is Stripe's actual SDK: you cannot modify this class,
|
||||||
* and it doesn't implement PaymentGateway.
|
* and it doesn't implement PaymentGateway.
|
||||||
*
|
|
||||||
* In real projects, this would be a JAR you depend on.
|
|
||||||
*/
|
*/
|
||||||
public class StripeClient {
|
public class StripeClient {
|
||||||
|
|
||||||
private final String apiKey;
|
private final String apiKey;
|
||||||
|
|
||||||
public StripeClient(String apiKey) {
|
public StripeClient(String apiKey) {
|
||||||
this.apiKey = apiKey;
|
this.apiKey = apiKey;
|
||||||
System.out.println("[Stripe] Initialized with key: " + apiKey.substring(0, 8) + "...");
|
System.out.println("[Stripe] Initialized with key: " + apiKey.substring(0, 8) + "...");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stripe uses cents, not decimal amounts
|
// Stripe uses cents, not decimal amounts
|
||||||
public StripeChargeResult createCharge(String customerId, long amountInCents, String currency) {
|
public StripeChargeResult createCharge(String customerId, long amountInCents, String currency) {
|
||||||
System.out.printf("[Stripe] Charging customer=%s, amount=%d cents, currency=%s%n",
|
System.out.printf("[Stripe] Charging customer=%s, amount=%d cents, currency=%s%n",
|
||||||
customerId, amountInCents, currency);
|
customerId, amountInCents, currency);
|
||||||
// Simulate success
|
|
||||||
return new StripeChargeResult("ch_" + System.currentTimeMillis(), true, null);
|
return new StripeChargeResult("ch_" + System.currentTimeMillis(), true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stripe's refund method takes a charge ID and uses different naming
|
// Stripe's refund method takes a charge ID and uses different naming
|
||||||
public boolean issueRefund(String chargeId, long amountInCents) {
|
public boolean issueRefund(String chargeId, long amountInCents) {
|
||||||
System.out.printf("[Stripe] Refunding charge=%s, amount=%d cents%n", chargeId, amountInCents);
|
System.out.printf("[Stripe] Refunding charge=%s, amount=%d cents%n", chargeId, amountInCents);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stripe uses 'retrieve' not 'getStatus', and returns an object
|
// Stripe uses 'retrieve' not 'getStatus', and returns an object
|
||||||
public StripeChargeResult retrieveCharge(String chargeId) {
|
public StripeChargeResult retrieveCharge(String chargeId) {
|
||||||
System.out.printf("[Stripe] Retrieving charge=%s%n", chargeId);
|
System.out.printf("[Stripe] Retrieving charge=%s%n", chargeId);
|
||||||
return new StripeChargeResult(chargeId, true, "succeeded");
|
return new StripeChargeResult(chargeId, true, "succeeded");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stripe-specific result object — nothing in common with your domain
|
|
||||||
public static class StripeChargeResult {
|
public static class StripeChargeResult {
|
||||||
public final String chargeId;
|
public final String chargeId;
|
||||||
public final boolean success;
|
public final boolean success;
|
||||||
public final String status;
|
public final String status;
|
||||||
|
|
||||||
public StripeChargeResult(String chargeId, boolean success, String status) {
|
public StripeChargeResult(String chargeId, boolean success, String status) {
|
||||||
this.chargeId = chargeId;
|
this.chargeId = chargeId;
|
||||||
this.success = success;
|
this.success = success;
|
||||||
|
|||||||
@@ -1,42 +1,34 @@
|
|||||||
package adapter;
|
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
|
* Object Adapter variant: holds a StripeClient instance via composition.
|
||||||
* via composition (not inheritance), so it can adapt any StripeClient
|
* This means it can wrap any StripeClient including subclasses.
|
||||||
* including subclasses.
|
|
||||||
*
|
|
||||||
* The key responsibility: translate YOUR interface's methods into
|
|
||||||
* calls that Stripe understands — data conversion included.
|
|
||||||
*/
|
*/
|
||||||
public class StripePaymentAdapter implements PaymentGateway {
|
public class StripePaymentAdapter implements PaymentGateway {
|
||||||
|
|
||||||
private final StripeClient stripe;
|
private final StripeClient stripe;
|
||||||
|
|
||||||
public StripePaymentAdapter(StripeClient stripe) {
|
public StripePaymentAdapter(StripeClient stripe) {
|
||||||
this.stripe = stripe;
|
this.stripe = stripe;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean charge(String customerId, double amount, String currency) {
|
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);
|
long amountInCents = Math.round(amount * 100);
|
||||||
|
// Translation 2: charge() → createCharge(), lowercase currency
|
||||||
StripeClient.StripeChargeResult result =
|
StripeClient.StripeChargeResult result =
|
||||||
stripe.createCharge(customerId, amountInCents, currency.toLowerCase());
|
stripe.createCharge(customerId, amountInCents, currency.toLowerCase());
|
||||||
|
// Translation 3: StripeChargeResult → boolean
|
||||||
return result.success;
|
return result.success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean refund(String transactionId, double amount) {
|
public boolean refund(String transactionId, double amount) {
|
||||||
// Translation: your "transactionId" is Stripe's "chargeId"
|
|
||||||
long amountInCents = Math.round(amount * 100);
|
long amountInCents = Math.round(amount * 100);
|
||||||
|
// Translation: refund() → issueRefund(), your "transactionId" is Stripe's "chargeId"
|
||||||
return stripe.issueRefund(transactionId, amountInCents);
|
return stripe.issueRefund(transactionId, amountInCents);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getStatus(String transactionId) {
|
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);
|
StripeClient.StripeChargeResult result = stripe.retrieveCharge(transactionId);
|
||||||
if (!result.success) return "FAILED";
|
if (!result.success) return "FAILED";
|
||||||
return result.status != null ? result.status.toUpperCase() : "UNKNOWN";
|
return result.status != null ? result.status.toUpperCase() : "UNKNOWN";
|
||||||
|
|||||||
23
02-structural/adapter/StripePaymentClassAdapter.java
Normal file
23
02-structural/adapter/StripePaymentClassAdapter.java
Normal file
@@ -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";
|
||||||
|
}
|
||||||
|
}
|
||||||
19
README.md
19
README.md
@@ -178,6 +178,25 @@ javac 01-creational/singleton/*.java -d out/singleton
|
|||||||
java -cp out/singleton singleton.Main
|
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
|
## Reference
|
||||||
|
|
||||||
- *Design Patterns: Elements of Reusable Object-Oriented Software* — Gamma, Helm, Johnson, Vlissides
|
- *Design Patterns: Elements of Reusable Object-Oriented Software* — Gamma, Helm, Johnson, Vlissides
|
||||||
|
|||||||
Reference in New Issue
Block a user