Bird
Raised Fist0

Design a Plugin Architecture - Plugin Interface, Registry, Loader, Dependency

LLDhardInterview freq: medium
Choose your preparation mode4 modes available
🏗️
Design a Plugin Architecture - Plugin Interface, Registry, Loader, Dependency
Design a flexible, extensible plugin system with dynamic loading, dependency management, and safe concurrency.
hardAdvanced LLD ChallengesFreq: medium
Prerequisites:OOP FundamentalsJava Concurrency BasicsClass Loading in JavaDesign Patterns (Factory, Singleton, Observer)
⚙️
Functional Requirements
  • Define a Plugin interface that all plugins must implement
  • Maintain a PluginRegistry to register and lookup plugins by unique ID
  • Implement a PluginLoader that loads plugin classes dynamically from JARs or classpath
  • Support plugin dependencies and ensure dependent plugins load first
  • Ensure thread-safe registration and loading of plugins
  • Allow querying plugin metadata such as version and dependencies
  • Detect and prevent circular dependencies among plugins
👤
Use Cases
  • Register a new plugin dynamically at runtime
  • Load plugins and their dependencies in correct order
  • Query plugin registry for available plugins
  • Unload or disable a plugin safely
  • Handle plugin dependency resolution and circular dependency detection
  • Notify clients when plugins are loaded or unloaded
🧑‍💻
Actors
  • Plugin Developer
  • System Administrator
  • Plugin Registry
  • Plugin Loader
  • Client Application
❓
Clarifying Questions to Ask
  • How many concurrent threads will register or load plugins? Is high concurrency expected?
  • Should the registry support plugin unloading or only loading?
  • Are plugins versioned and how should version conflicts be handled?
  • What is the expected plugin dependency graph size and complexity?
  • Should plugin loading be lazy or eager at system startup?
  • How to handle plugin load failures or missing dependencies?
  • Is there a pricing or licensing strategy affecting plugin availability?
Out of Scope
✗ Plugin UI integration
✗ Plugin configuration management
✗ Plugin version upgrade and rollback
✗ Security sandboxing of plugins
🔢
Enumerations
enum PluginState
LOADED | UNLOADED | FAILED | LOADING
Represents plugin lifecycle states. Using enum prevents typo bugs and enables exhaustive switch statements for state handling.
enum DependencyResolutionStatus
RESOLVED | UNRESOLVED | CIRCULAR_DEPENDENCY
Encodes the status of dependency resolution. Enum ensures clear, type-safe handling of dependency states.
📦
Plugin
4 fields
String idString versionList<String> dependenciesvolatile PluginState state
Defines the contract for all plugins including metadata and lifecycle state. Holds plugin identity and dependency info.
Why thisJava interface allows multiple implementations and enforces method contracts.
Why notAbstract class would limit multiple inheritance and flexibility.
public String getId()Returns the unique plugin identifier.
public String getVersion()Returns the plugin version string.
public List<String> getDependencies()Returns list of plugin IDs this plugin depends on.
public abstract void initialize()Abstract method to initialize plugin resources.
public abstract void shutdown()Abstract method to clean up plugin resources.
📦
AbstractPlugin
4 fields
protected final String idprotected final String versionprotected final List<String> dependenciesprotected volatile PluginState state
Provides base implementation for Plugin interface with state management and metadata storage. Enforces volatile for safe publication of state.
Why thisAbstract class allows code reuse and enforces state handling with volatile and synchronized.
Why notInterface alone cannot hold state; concrete classes would duplicate code.
public String getId()Returns plugin ID.
public String getVersion()Returns plugin version.
public List<String> getDependencies()Returns dependencies list.
public PluginState getState()Returns current plugin state.
protected synchronized void setState(PluginState newState)Atomically updates plugin state.
📦
PluginRegistry
2 fields
private final Map<String, Plugin> pluginsprivate final ReadWriteLock registryLock
Manages registration, lookup, and lifecycle of plugins. Ensures thread-safe access to plugin map using ReadWriteLock.
Why thisUsing ReadWriteLock allows concurrent reads and exclusive writes for performance.
Why notSynchronized map alone would limit concurrency and scalability.
public synchronized boolean registerPlugin(Plugin plugin)Registers a plugin if not already present. Returns true if successful.
public Plugin getPlugin(String pluginId)Returns plugin by ID or null if not found.
public synchronized boolean unregisterPlugin(String pluginId)Removes plugin from registry if present.
public List<Plugin> listPlugins()Returns a snapshot list of all registered plugins.
📦
PluginLoader
2 fields
private final PluginRegistry registryprivate final ClassLoader classLoader
Loads plugin classes dynamically, resolves dependencies, and registers plugins in registry. Handles circular dependency detection.
Why thisSynchronized method ensures thread-safe loading; ClassLoader enables dynamic loading.
Why notEager loading or static linking would reduce flexibility and extensibility.
public synchronized Plugin loadPlugin(String pluginClassName) throws ClassNotFoundException, PluginLoadExceptionLoads plugin class by name, resolves dependencies recursively, initializes plugin, and registers it.
private DependencyResolutionStatus resolveDependencies(Plugin plugin, Set<String> visiting)Recursively resolves dependencies and detects circular dependencies.
📦
PluginLoadException
2 fields
private final String messageprivate final Throwable cause
Custom checked exception to indicate plugin loading failures.
Why thisChecked exception forces caller to handle plugin load failures explicitly.
Why notRuntime exceptions would risk unhandled errors during plugin loading.
public String getMessage()Returns error message.
public Throwable getCause()Returns underlying cause.
🔗
Relationships
PluginLoader──►PluginRegistry[1 to 1]
associationPluginLoader uses PluginRegistry but does not own it
PluginRegistry◇──►Plugin[1 to many]
aggregationPluginRegistry aggregates multiple Plugin instances
AbstractPlugin──►Plugin[1 to 1]
realizationAbstractPlugin implements Plugin interface

Practice

(1/5)
1. In a system using the Prototype pattern, should the Vehicle base type be an abstract class or an interface? Why?
easy
A. Abstract class, because it can hold common state and provide default cloning behavior
B. Interface, because it allows multiple inheritance and enforces method signatures without state
C. Interface, because cloning requires no shared implementation or state
D. Abstract class, because interfaces cannot declare clone() methods

Solution

  1. Step 1: Consider cloning and shared state

    Cloning often requires copying internal state; an abstract class can hold this state and provide a default clone() implementation.
  2. Step 2: Evaluate interface limitations

    Interfaces define behavior but cannot hold state or provide default cloning logic (in Java before default methods). Thus, abstract class fits better.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Abstract class holds state + cloning logic -> correct OOP design [check]
Hint: Clone needs shared state -> abstract class better than interface [check]
Common Mistakes:
2. In the Car Rental System, the parkVehicle() method is not synchronized. Which exact sequence of events between Thread 1 and Thread 2 causes a double-booking of the same parking spot?
medium
A. Thread 1 checks spot availability -> true, context switch; Thread 2 checks spot availability -> true; both park vehicles simultaneously.
B. Thread 1 parks vehicle, Thread 2 waits until Thread 1 finishes, then parks another vehicle.
C. Thread 1 parks vehicle, Thread 2 reads spot as occupied and waits, preventing double-booking.
D. Thread 1 and Thread 2 both fail to acquire lock and retry, causing no double-booking.

Solution

  1. Step 1: Identify the check-then-act race condition

    Without synchronization, Thread 1 reads spot as available and is preempted before marking it occupied.
  2. Step 2: Thread 2 also reads spot as available and proceeds to park, causing double-booking.

    Both threads act on stale availability, leading to two vehicles occupying the same spot.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Unsynchronized check-then-act -> double-booking [check]
Hint: Check availability -> context switch -> both park -> double-booking [check]
Common Mistakes:
3. The Elevator System currently calculates fees using a fixed rate. To add a WeekendFeeStrategy without modifying existing classes, which SOLID principle is being applied and what is the minimal required change?
medium
A. Single Responsibility Principle: modify ElevatorController to handle weekend fees directly.
B. Liskov Substitution Principle: replace FeeStrategy with WeekendFeeStrategy everywhere in the codebase.
C. Dependency Inversion Principle: inject WeekendFeeStrategy into Elevator class by modifying its constructor.
D. Open/Closed Principle: create a new WeekendFeeStrategy class implementing FeeStrategy interface; no changes to existing classes.

Solution

  1. Step 1: Identify SOLID principle

    Open/Closed Principle (OCP) states software entities should be open for extension but closed for modification.
  2. Step 2: Apply OCP via new strategy class

    Adding WeekendFeeStrategy as a new implementation of FeeStrategy extends behavior without modifying existing classes.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    New strategy class extends fees without changing existing code [check]
Hint: OCP: extend with new class, don't modify existing code [check]
Common Mistakes:
4. The system needs to support ElectricCar playback devices that require ChargingSpot management. What is the minimum change to the current design to support this without modifying existing classes?
hard
A. Add ELECTRIC_CAR to DeviceType enum, CHARGING to SpotType enum, create ElectricCar extends Device, and update SpotCompatibility map externally.
B. Modify existing Device class to add charging logic and add ElectricCar as a flag inside it.
C. Create a new ElectricCar class unrelated to Device and duplicate playback logic.
D. Add charging logic inside the PlaybackQueue class to handle ElectricCar specifics.

Solution

  1. Step 1: Identify extensibility without modifying existing classes

    Adding new enum values and subclassing allows extension without changing existing class code.
  2. Step 2: Update compatibility externally

    Updating SpotCompatibility map outside existing classes respects OCP and avoids class explosion or god-class logic.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Extend enums + subclass + external map update -> minimal change [check]
Hint: Extend enums + subclass + external map -> minimal change [check]
Common Mistakes:
5. To add an ElectricPlayer who requires ChargingSpot in the Snake and Ladder game, what is the minimum change to the current design to maintain extensibility without modifying existing classes?
hard
A. Add ELECTRIC_PLAYER to PlayerType enum, add CHARGING to SpotType enum, create ElectricPlayer extends Player, update SpotCompatibility map.
B. Modify Player class to include charging logic and add ChargingSpot as a new field.
C. Create a new ElectricPlayer class and modify Board to handle ChargingSpots explicitly.
D. Add charging behavior directly inside Player class and update all existing player types.

Solution

  1. Step 1: Identify extensibility without modifying existing classes

    Adding new enum values and new subclass preserves existing class code unchanged.
  2. Step 2: Update compatibility map for new player and spot types

    SpotCompatibility map extension allows new rules without changing existing logic.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    New enums + subclass + compatibility map extension -> minimal existing code change [check]
Hint: New enums + subclass + map update = extensible addition [check]
Common Mistakes:
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard