Bird
Raised Fist0

DRY, KISS, YAGNI - Principles That Prevent Over-Engineering in Interviews

LLDmediumInterview freq: medium
Choose your preparation mode4 modes available
▶
Class Trace
Step 1/4
PrincipleEnforcer abstract class

PrincipleEnforcer is an abstract class holding a CodeModule reference via composition, enabling shared state and behavior for all enforcers.

🎯 Tests whether candidate knows abstract class is chosen over interface because it holds instance state (CodeModule) which interfaces cannot hold in Java.
public abstract class PrincipleEnforcer {
    protected final CodeModule codeModule; // composition: lifecycle bound
    public PrincipleEnforcer(CodeModule module) {
        this.codeModule = module;
    }
    public abstract void enforce();
}
CodeBase composition of CodeModules

CodeBase composes multiple CodeModule instances with synchronized addModule method to ensure thread-safe mutation.

🎯 Tests whether candidate explains synchronized addModule to prevent concurrent modification race conditions on modules list.
public class CodeBase {
    private final List<CodeModule> modules = new ArrayList<>();
    public synchronized void addModule(CodeModule m) {
        modules.add(m);
    }
}
Inheritance hierarchy of DryEnforcer, KissEnforcer, YagniEnforcer

DryEnforcer, KissEnforcer, and YagniEnforcer extend PrincipleEnforcer realizing enforce() method, demonstrating Open-Closed Principle by extending without modifying base class.

🎯 Tests whether candidate understands OCP via inheritance and why abstract class is used to share state and force method implementation.
public class DryEnforcer extends PrincipleEnforcer {
    public DryEnforcer(CodeModule module) {
        super(module);
    }
    @Override
    public void enforce() {
        // Dry principle enforcement logic
    }
}
Association between CodeBase and CodeModule

CodeBase has association to CodeModule, indicating CodeModule can exist independently outside CodeBase lifecycle.

🎯 Tests whether candidate distinguishes association (independent lifecycle) from composition and explains design choice.
public class CodeModule {
    private final String name;
    public CodeModule(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}
📐
Class Diagram
Class Diagram
       +------------------+
       |  <<Abstract>>    |
       | PrincipleEnf.    |
       +------------------+
       | -codeModule: CodeModule (comp.) |
       +------------------+
       | +enforce(): void  |
       +------------------+
               │ extends
       +------------------+
       |    DryEnforcer    |
       +------------------+
       | +enforce(): void  |
       +------------------+
               │ extends
       +------------------+
       |   KissEnforcer   |
       +------------------+
       | +enforce(): void  |
       +------------------+
               │ extends
       +------------------+
       |   YagniEnforcer  |
       +------------------+
       | +enforce(): void  |
       +------------------+
       +------------------+
       |    CodeBase      |
       +------------------+
       | ◆ modules: List<CodeModule> (comp.) |
       +------------------+
       | +addModule(m: CodeModule): void [sync] |
       +------------------+
               │
               ──►
       +------------------+
       |   CodeModule     |
       +------------------+
       | +getName(): String|
       +------------------+
       +------------------+
       |  DesignReview    |
       +------------------+
       | +review(): void  |
       +------------------+
               │
               ──►
       +------------------+
       | ExtensionPoint   |
       +------------------+
       | +extend(): void  |
       +------------------+

This class diagram illustrates key Java design decisions enforcing DRY, KISS, and YAGNI principles. PrincipleEnforcer is an abstract class holding shared state (CodeModule) via composition, which interfaces cannot hold in Java. DryEnforcer, KissEnforcer, and YagniEnforcer extend PrincipleEnforcer, demonstrating Open-Closed Principle by allowing extension without modification. CodeBase composes CodeModules with synchronized addModule method to ensure thread-safe mutation, showing correct concurrency control. The association between CodeBase and CodeModule indicates independent lifecycles, clarifying ownership. These design choices prevent over-engineering by enforcing SOLID principles and Java-specific concurrency and inheritance mechanisms.

Primary Focus

✓ Abstract class state vs interface realization + Composition lifecycle + Singleton volatile + OCP via inheritance

Practice

(1/5)
1. In implementing a chain of handlers in Java, should the Handler interface be an abstract class or an interface, and why?
easy
A. Interface, because handlers only define a contract without shared state, enabling flexible implementations.
B. Interface, because it enforces inheritance and prevents multiple implementations.
C. Abstract class, because Java interfaces cannot have default method implementations.
D. Abstract class, because it allows sharing common state and behavior among handlers.

Solution

  1. Step 1: Understand Handler role

    Handlers define a contract to process or pass requests; they typically do not share mutable state.
  2. Step 2: Evaluate Java features

    Interfaces allow multiple inheritance and default methods (since Java 8), promoting flexibility; abstract classes share state but restrict inheritance.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Interface defines contract without forcing shared state [check]
Hint: Interface for contract, abstract class for shared state [check]
Common Mistakes:
2. In a plugin loader, the loadPlugin() method checks if a plugin is already loaded before loading it. Without synchronizing this method, what exact thread interleaving causes the same plugin to be loaded twice?
medium
A. Thread 1 checks isLoaded() -> false, context switch; Thread 2 checks isLoaded() -> false; both proceed to load the plugin.
B. Thread 1 and Thread 2 both wait on a lock, causing deadlock.
C. Thread 1 loads plugin, but Thread 2 never sees the updated loaded state due to caching.
D. Thread 1 and Thread 2 both call loadPlugin() simultaneously and both throw exceptions.

Solution

  1. Step 1: Trace the check-then-act sequence without synchronization

    Thread 1 calls isLoaded() and sees false, then context switches before loading.
  2. Step 2: Thread 2 also calls isLoaded() and sees false, both threads proceed to load plugin concurrently, causing duplicate loading.

  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Unsynchronized check-then-act allows double loading due to race [check]
Hint: Check-then-act without sync -> double load race condition [check]
Common Mistakes:
3. You want to add a WeekendBonusRule that gives players extra moves on weekends without modifying existing classes. Which SOLID principle guides the minimal change, and what is the correct approach?
medium
A. Single Responsibility Principle: add weekend logic inside the Player class.
B. Open/Closed Principle: create a new class implementing a MoveRule interface and inject it into the game engine.
C. Liskov Substitution Principle: subclass Player to WeekendPlayer with bonus logic.
D. Dependency Inversion Principle: make Player depend on concrete WeekendBonusRule.

Solution

  1. Step 1: Identify SOLID principle for extension without modification

    Open/Closed Principle (OCP) states software entities should be open for extension but closed for modification.
  2. Step 2: Evaluate approaches

    Subclassing Player to WeekendPlayer allows extension without modifying Player; injecting new MoveRule class also respects OCP but requires interface design.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Subclassing extends behavior without modifying base class [check]
Hint: Extend via subclass or interface, don't modify existing classes [check]
Common Mistakes:
4. Adding a WeekendFeeStrategy to the parking lot pricing requires which minimum change to comply with SOLID principles?
medium
A. Add a boolean flag isWeekend to ParkingLot and handle fees internally.
B. Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without modifying existing classes.
C. Extend ParkingLot with a subclass WeekendParkingLot that overrides fee calculation.
D. Modify the existing FeeStrategy class to add weekend logic with an if-statement.

Solution

  1. Step 1: Identify the SOLID principle involved

    The Open/Closed Principle (OCP) requires that classes be open for extension but closed for modification.
  2. Step 2: Evaluate options against OCP

    Add a boolean flag isWeekend to ParkingLot and handle fees internally. adds weekend logic inside ParkingLot, modifying existing code and violating OCP. Create a new WeekendFeeStrategy class implementing FeeStrategy and inject it without modifying existing classes. creates a new strategy class and injects it, adhering to OCP. Modify the existing FeeStrategy class to add weekend logic with an if-statement. modifies FeeStrategy directly, violating OCP. Extend ParkingLot with a subclass WeekendParkingLot that overrides fee calculation. subclasses ParkingLot, which can cause class explosion and violates single responsibility.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Modifying ParkingLot internals violates OCP -> not minimal change [check]
Hint: OCP: extend with new class, don't modify existing code [check]
Common Mistakes:
5. The notification system must support ElectricCar vehicles that require ChargingSpot notifications. What is the minimum design change to support this without modifying existing classes?
hard
A. Modify Vehicle class to add isElectric flag and add charging logic in existing Spot classes.
B. Create a new ElectricCar class and ChargingSpot class, then modify NotificationFactory to handle them.
C. Add ELECTRIC_CAR to VehicleType enum and CHARGING to SpotType enum; create ElectricCar subclass and update SpotCompatibility map.
D. Add charging logic inside existing NotificationChannel implementations with conditional checks for ElectricCar.

Solution

  1. Step 1: Identify extensibility requirements

    New vehicle and spot types must be added without modifying existing classes to respect OCP.
  2. Step 2: Apply minimal changes

    Adding new enum values and creating new subclasses allows extension without modifying existing class code; updating compatibility map centralizes new logic.
  3. Step 3: Eliminate other options

    Options B, C, and D modify existing classes or factory logic, violating OCP and risking class explosion or god-class anti-pattern.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    New enums and subclasses extend design without modifying existing classes [check]
Hint: Add enums + subclasses, avoid modifying existing classes [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