Katkat

Java and Object-Oriented Design Fundamentals

AntraSoftware Engineer · Technical Screen

Last updated

This problem tests your understanding of interface design, the Liskov Substitution Principle, and proper usage of Java Collections in a Spring-managed context.

Task: Implement a Payment Strategy Pattern in Spring

You are building a payment processing system for an e-commerce platform. Your task is to design a system that supports multiple payment methods (e.g., Credit Card, PayPal, Crypto) while ensuring the code is extensible and adheres to SOLID principles.

Requirements:

  1. Define a generic PaymentStrategy interface with a processPayment(double amount) method.
  2. Create two concrete implementations: CreditCardPayment and CryptoPayment.
  3. Create a PaymentService class that uses a Map to store strategies. Use Spring's dependency injection to automatically populate this map with all available strategy beans.
  4. Add a method executePayment(String type, double amount) to the PaymentService that retrieves the correct strategy from the map and processes the payment. If the type is unknown, throw an IllegalArgumentException.

Considerations:

  • How do you ensure that adding a new payment method does not require modifying the existing PaymentService logic (Open/Closed Principle)?
  • Why might you choose a Map over a long if-else or switch statement?
  • How does Spring's @Qualifier or bean autowiring facilitate this pattern?

Example Usage:

// Assume these beans are managed by Spring
PaymentService service = ...; 

// Should look up the 'creditCard' bean and call processPayment(100.0)
service.executePayment("creditCard", 100.0);

Hints

Hint 1

Use Spring's ability to inject all implementations of an interface into a List or Map by adding them to the constructor of the service.

Hint 2

Think about how to map specific identifiers (like strings) to bean instances to avoid hard-coding logic.

Hint 3

Review the Liskov Substitution Principle: ensure your concrete implementations do not change the expected behavior or throw unexpected exceptions that the base interface doesn't account for.

Follow-up questions

  • How would you handle the scenario where one payment method requires a different set of input parameters than another?
  • What are the trade-offs of using field injection vs. constructor injection for the strategy map?
  • How would you implement thread safety if the payment strategies were required to hold state during the transaction?