-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
304: Practice Exercise: Designing a Plugin System with Interfaces
Why bother with an interface instead of just using a base class?
I get this question a lot. If you're thinking, "I could just make a BasePaymentProcessor and extend it," you're technically right, but you're painting yourself into a corner. In a real-world plugin system—say, for a payment gateway—your plugins might need to inherit from other frameworks or libraries. Since Java doesn't support multiple inheritance for classes, a base class locks the plugin author into your hierarchy.
By using an interface, you're defining a contract. You're telling the plugin developer: "I don't care who your parents are or how you're built internally, as long as you provide a processPayment(double amount) method, I can work with you."
public interface PaymentProcessor {
boolean processPayment(double amount);
String getProcessorName();
}
Now, whether a developer is using a legacy library for Stripe or a modern one for PayPal, they can just implement your interface and it just works.
How do I actually manage these plugins without hardcoding every single one into my main logic?
The magic happens when you stop thinking about specific classes and start thinking about a collection of the interface. You don't want your checkout code to have a giant if-else block checking if the user chose "Credit Card" or "Crypto." That's a maintenance nightmare.
Instead, I usually maintain a list or a map of the interface type. You load the plugins (via a config file, a service loader, or simple registration) and then just loop through them or pull the one you need by key. Look at how clean this keeps the "core" logic:
public class PaymentManager {
private final Map<String, PaymentProcessor> plugins = new HashMap<>();
public void registerPlugin(PaymentProcessor processor) {
plugins.put(processor.getProcessorName(), processor);
}
public void executePayment(String method, double amount) {
PaymentProcessor processor = plugins.get(method);
if (processor == null) {
throw new IllegalArgumentException("Payment method not supported!");
}
processor.processPayment(amount);
}
}
Notice that PaymentManager has zero knowledge of how Stripe or PayPal actually works. It only knows the PaymentProcessor contract.
What if a plugin needs its own specific configuration that the main app doesn't know about?
This is where things get tricky. You can't put a stripeApiKey method in your general PaymentProcessor interface because the PayPal plugin wouldn't know what to do with it. I've seen people try to solve this by passing a Map<String, String> into every method, but that's messy and ruins your type safety.
The cleaner way is to handle configuration during the plugin's instantiation or through a dedicated initialize method that takes a generic configuration object. Let the plugin handle its own secrets; the main app should just be the orchestrator.
public class StripeProcessor implements PaymentProcessor {
private String apiKey;
// The main app doesn't call this; the plugin loader or a factory does
public StripeProcessor(String apiKey) {
this.apiKey = apiKey;
}
@Override
public boolean processPayment(double amount) {
System.out.println("Using API Key " + apiKey + " to charge $" + amount);
return true;
}
@Override
public String getProcessorName() {
return "Stripe";
}
}
By keeping the configuration separate from the interface methods, you keep your core system agnostic. The main app just sees a PaymentProcessor; it doesn't need to know that the Stripe version requires an API key while the "Cash" version requires nothing at all.
📋 Practical Task
Exercise: Implementing a Modular Text-Analysis Plugin System
Your goal is to build a small system that can analyze a string of text using multiple "Analysis Plugins." The main application should be able to run all loaded plugins against a piece of text without knowing what each plugin actually does.
- Create an interface called
TextAnalyzerwith a methodString analyze(String text). - Implement three different plugins:
WordCountAnalyzer: Returns the total number of words.UpperCaseAnalyzer: Returns the text converted to all uppercase.VowelCountAnalyzer: Returns the total number of vowels found in the text.
- Create an
AnalysisEngineclass that holds aList<TextAnalyzer>. It should have a method calledrunAllAnalyses(String text)that iterates through all registered plugins and prints their results to the console. - In your main method, instantiate the engine, register your three plugins, and run the engine on a sample sentence (e.g., "Java interfaces are powerful!").
There are no comments for now.