Open In App

Design Patterns & Best Practices Interview Questions - OOPs

Last Updated : 01 Sep, 2025
Suggest changes
Share
Like Article
Like
Report

Design Patterns & Best Practices in OOP provide proven solutions to recurring software design problems and guidelines for writing clean, maintainable code. Patterns like Singleton, Factory, Observer and Strategy help structure code efficiently, while best practices such as SOLID principles, DRY (Don’t Repeat Yourself) and favoring composition over inheritance ensure scalability and readability. Together, they enable developers to build flexible, reusable and robust object-oriented systems.

1. Why is the Singleton pattern sometimes considered an anti-pattern despite being widely used?

The Singleton pattern ensures only one instance of a class exists globally. While useful in scenarios like logging, configuration, or caching, it’s often criticized because:

  • Global state introduction -> Makes unit testing harder since dependencies cannot be mocked or replaced easily.
  • SRP violation -> A class manages both its business logic and its own lifecycle.
  • Thread-safety concerns -> Incorrect implementation in multithreaded environments can cause race conditions.
  • Tight coupling -> Overuse leads to multiple parts of an application depending on a single instance, reducing modularity.

Note: Thus, while practical, it must be carefully applied with thread-safe and dependency injection-friendly implementations.

2. How do you decide between using the Strategy pattern and the State pattern, since both encapsulate behavior?

Both Strategy and State encapsulate behavior in separate classes, but they serve different purposes:

  • Strategy Pattern -> Provides interchangeable algorithms that can be swapped independently of the context (e.g., sorting algorithms, payment methods).
  • State Pattern -> Behavior changes depending on the object’s internal state, with transitions handled implicitly (e.g., a TCP connection transitioning between states).

Note: Use Strategy when behaviors are independent and interchangeable; use State when behavior depends on sequential state transitions.

3. Why is “Composition over Inheritance” considered a best practice in OOP design? Give an example.

  • Inheritance -> Creates rigid hierarchies; child classes are tightly coupled to parent classes, and changes in the parent ripple down.
  • Composition -> Delegates behavior to other objects, enabling flexible, modular, and easily testable designs.

Example:
Instead of class Car extends Engine, use:

Java
class Car {  Engine engine; } 

This allows swapping PetrolEngine with ElectricEngine at runtime without changing the Car class.

Note: Thus, composition promotes loose coupling, testability and runtime flexibility.

4. Explain how the Observer pattern can lead to a “memory leak” problem.

In the Observer pattern, subjects maintain references to observers. If an observer is no longer needed but not explicitly unsubscribed, the subject still holds a reference, preventing garbage collection -> memory leak. For example, in GUI frameworks, failing to deregister event listeners can cause hidden memory leaks.

Solutions:

  • Use weak references for observers.
  • Provide automatic unsubscription mechanisms.
  • Use event buses or pub-sub systems with cleanup policies.

Note: The solution is to use weak references, automatic unsubscription mechanisms or event buses that clean up automatically.

5. What is the difference between the Factory Method and Abstract Factory patterns? When would you choose one over the other?

Factory Method

  • Defines a method in a base class to create an object but lets subclasses decide which object is created.
  • Example: DocumentFactory creates either WordDocument or PDFDocument.
  • Use case: One product type with multiple variations.

Abstract Factory

  • Provides an interface to create families of related objects without specifying their concrete classes.
  • Example: GUIFactory creates Button + Checkbox for Windows vs MacOS.
  • Use case: Multiple related products must be created consistently.

Rule of thumb:

  • Use Factory Method -> when varying one product.
  • Use Abstract Factory -> when varying multiple related products.

6. How can misuse of the Adapter pattern harm system performance?

The Adapter pattern bridges incompatible interfaces, but excessive use can:

  • Introduce extra layers of abstraction, slowing performance.
  • Lead to spaghetti dependencies when multiple adapters are chained.
  • Hide bad system design instead of refactoring underlying classes.

Best practice: Use adapters sparingly and only for true interoperability, not as a substitute for good design.

7. Explain how the Dependency Inversion Principle (DIP) relates to the Factory and Dependency Injection (DI) patterns.

The Dependency Inversion Principle (DIP) is one of the five SOLID principles of object-oriented design. It states that: High-level modules (business logic) should not depend on low-level modules (concrete implementations).

How DIP relates to Factory Pattern:

  • The Factory Method or Abstract Factory centralizes object creation. Instead of a high-level module directly instantiating a concrete class (e.g., new PdfReport()), it requests the object from a factory.
  • This ensures the high-level code depends only on an abstraction (Report interface) and not the implementation.
  • If tomorrow the requirement changes to ExcelReport, only the factory changes, not the high-level code.

How DIP relates to Dependency Injection (DI):

  • Dependency Injection pushes object creation outside the class entirely. Instead of a class constructing its own dependencies, they are “injected” at runtime, usually via constructor injection or frameworks like Spring, Guice, Dagger.
  • Example: A PaymentService depends on PaymentProcessor. Instead of instantiating new PaypalProcessor(), the framework injects the required implementation.
  • Both should depend on abstractions (interfaces or abstract classes).

Note: DIP is the principle, while Factory and DI are mechanisms that implement it to reduce coupling and improve testability.

8. How does the Proxy pattern differ from the Decorator pattern, even though both wrap objects?

At first glance, both Proxy and Decorator patterns wrap another object and implement the same interface. However, their intent and purpose are different:

Proxy Pattern:

  • Purpose: Controls access to the real object by acting as a stand-in.
  • Goal: Indirection and access control.
  • Common Uses:

Virtual Proxy: Delays object creation until it is actually needed (lazy loading). Example: An image viewer only loads the image when displayed.
Remote Proxy: Represents an object in another address space (e.g., RMI in Java).
Protection Proxy: Restricts access by checking permissions before delegating.

Decorator Pattern:

  • Purpose: Dynamically adds or modifies behavior of an object without changing its structure.
  • Goal: Augmentation and extension of behavior.
  • Common Uses:

Adding compression, buffering, or encryption to a data stream.
Wrapping GUI components to add borders, scrollbars, or styles dynamically.

Example Difference:

  • Proxy Example: A BankAccountProxy checks user authentication before allowing access to BankAccount.
  • Decorator Example: A LoggingBankAccount adds logging functionality around every transaction.

9. Why can misuse of inheritance in design patterns lead to a violation of the Liskov Substitution Principle (LSP)?

The Liskov Substitution Principle (LSP) states: If S is a subtype of T, then objects of type T should be replaceable with objects of type S without altering correctness.

How misuse of inheritance breaks LSP:

  • When subclasses override methods in ways that contradict expectations of the parent class, they stop being substitutable.
  • This usually happens when inheritance is used for code reuse instead of true “is-a” relationships.

Classic Example:

  • Consider a Rectangle class with setWidth() and setHeight().
  • A Square is implemented as a subclass of Rectangle, but in a square, setting width automatically sets height.
  • Code that expects Rectangle behavior (independent width and height) will fail when passed a Square -> LSP violation.

Implications in Design Patterns:

  • Overusing inheritance (instead of composition) can lead to brittle hierarchies.
  • Many design patterns (e.g., Strategy, State, Decorator) promote composition over inheritance specifically to avoid such pitfalls.

Note: This breaks polymorphism and introduces bugs. Best practice: Favor composition and interfaces to avoid LSP violations.

10. Many developers treat design patterns as “templates to copy.” Why is this a bad practice?

Design patterns were popularized by the Gang of Four (GoF) book as solutions to recurring design problems. However, treating them as rigid templates to copy-paste into projects is a common mistake:

Over-engineering:

  • Applying patterns where simple solutions suffice leads to bloated and unnecessarily complex code.
  • Example: Using a Singleton for a global counter when a static variable would do.

Loss of Context:

  • Each pattern solves a very specific type of problem. Blindly applying them without analyzing requirements often makes the system harder to maintain.
  • Example: Adding multiple Decorators to objects for features that are rarely needed.

Decreased Maintainability: Patterns add layers of abstraction. If not justified, this abstraction makes debugging and onboarding new developers harder.

Misinterpretation of Intent: Developers may use a pattern because “it looks clever” rather than because it actually solves the design problem at hand.

Note: Design patterns should be conceptual guides, not copy-paste solutions. True mastery lies in knowing when not to use them.

11. How can overuse of design patterns lead to “Pattern Obsession” and what are its consequences?

Pattern Obsession occurs when developers force-fit design patterns into every problem, regardless of whether they are needed. This often happens when teams focus more on demonstrating pattern knowledge than solving the actual problem in the simplest way. Consequences:

Over-engineering

  • Simple tasks that could be solved with a straightforward function or class become bloated with multiple patterns.
  • Example: Creating a full-fledged Abstract Factory + Builder just to construct a single configuration object.

Reduced Readability and Maintainability

  • Excessive layers of abstractions (factories, observers, decorators, proxies) make the codebase intimidating for new developers.
  • The intent of the code gets buried under architectural complexity.

Performance Issues

  • Extra indirections from unnecessary wrappers and factories add runtime overhead.
  • This can be critical in systems that require high performance or low latency.

Violation of YAGNI (You Aren’t Gonna Need It): Patterns are introduced to solve problems that don’t exist yet, leading to wasted development effort.

Example:

  • Implementing an Observer + Mediator for a small module where a simple method call would suffice.
  • The code becomes “pattern-heavy” rather than “problem-solving.”

Note: Best practice is pragmatism, use patterns when they clearly solve recurring issues, not as a badge of expertise.

12. Explain how the Builder pattern solves the Telescoping Constructor Problem.

The Telescoping Constructor Problem occurs when a class has multiple optional parameters, leading to constructors like new User("A", "B", null, 0, true, null). This makes code unreadable and error-prone.
The Builder pattern addresses this by:

Separation of Construction from Representation

  • Builder moves the object construction logic into a separate helper (UserBuilder).
  • Each property is set using named methods, which are more descriptive than positional arguments.

Step-by-Step Construction: Allows building objects gradually in a readable way ->

Java
User user = new UserBuilder()  .setFirstName("Alice")  .setAge(30)  .setAddress("NYC")  .build(); 
  • Support for Immutability: The final object (User) is often made immutable, ensuring thread-safety and consistency.
  • Improved Maintainability: If new fields are added, only the Builder needs updating, not multiple overloaded constructors.

Benefits:

  • Readability (clear method names).
  • Flexibility (build different variants of an object easily).
  • Safety (fewer errors with null or wrong parameter order).

This improves readability, maintainability and safety, especially in large systems.

13. In what scenarios is the Prototype pattern preferable over the Factory pattern?

Toth Prototype and Factory are creational patterns, but they shine in different contexts.

Prototype Pattern: Creates new objects by cloning existing prototypes instead of instantiating new ones.

When Prototype is preferable over Factory:

  • Costly Object Creation: If object creation is expensive (due to DB lookups, network calls, or heavy computations), cloning a pre-configured prototype is faster.
  • Complex Object Structures: When objects contain deep hierarchies or nested structures, a prototype can be copied recursively instead of building from scratch each time.
  • Configuration-Heavy Objects: Factories typically build from scratch. If instances differ only slightly, cloning a prototype and tweaking a few fields is more efficient.

Example: A Car Factory needs to create multiple car models with different configurations.

  • With Factory: Each model must be constructed step-by-step.
  • With Prototype: A prebuilt SedanPrototype or SUVPrototype can be cloned and then slightly modified (e.g., color, accessories).

14. How does the Flyweight pattern balance memory optimization with increased complexity?

The Flyweight pattern is a structural pattern that minimizes memory usage by sharing common parts of state between multiple objects.

Key Concept:

  • Intrinsic State: Shared, constant data (e.g., font style, color).
  • Extrinsic State: Context-specific data passed in at runtime (e.g., position of a character).

Example: In a text editor, instead of storing font/color info for every character, you store it once in a shared Flyweight object. Each character then only stores its position (extrinsic state).

Benefits (Pros):

  1. Massive Memory Savings: Especially in systems with millions of similar objects (e.g., text editors, game engines with many sprites, particle systems).
  2. Scalability: Makes large-scale object-heavy systems feasible.

Thus, it’s a trade-off between space efficiency and runtime complexity.

15. Can the Template Method pattern violate the Open/Closed Principle (OCP)? Why or why not?

The Template Method defines the skeleton of an algorithm in a superclass and lets subclasses override specific steps without changing the overall structure. Potential Violation of OCP:

Subclass Fragility:

  • If subclasses need to override too many steps, the base class may need frequent modification to support them.
  • Example: If a base DataParser template defines read(), process(), write(), adding a new requirement forces changes across multiple subclasses.

Evolving Base Template: When the template itself changes (e.g., adding a new mandatory step), all subclasses must adapt. This breaks the OCP, since subclasses should ideally extend without forcing modifications upstream.

How to Avoid Violation:

  • Use hooks (optional methods with default behavior) in the base class to allow flexible extension.
  • Combine with the Strategy Pattern, which favors composition and allows algorithm steps to be swapped without modifying the base class.

Example:

  • In a payment system, PaymentProcessor defines a template with validate() -> process() -> notify().
  • If a new notification step is added, all subclasses (PayPal, CreditCard, UPI) must be modified -> OCP violation.
  • A better design is to delegate the notify() step to a Strategy, making extension easier without modifying existing classes.

Note: To avoid this, it’s often paired with Strategy (favoring composition) or hooks that allow extension without base class modification.

16. Compare Lazy Initialization in Singleton vs Proxy patterns.

Lazy initialization is a technique where object creation is delayed until it is actually required. Both Singleton and Proxy patterns use it, but for different goals:

Singleton with Lazy Initialization:

  • Ensures that the single instance of a class is not created until it is first accessed.
  • This improves startup performance by avoiding unnecessary object creation at program load.
  • Example: A logging service created only when the first log entry is made.
  • Risk: If not implemented with proper synchronization, it can cause race conditions in multithreaded systems.

Proxy with Lazy Initialization:

  • Defers creation of a heavy or resource-intensive object until the client actually needs it.
  • Example: A virtual proxy delaying loading of a high-resolution image until the user views it.
  • Unlike Singleton, it does not enforce uniqueness but optimizes performance by postponing resource allocation.

Key Difference:

  • Singleton lazy init -> controls object uniqueness and lifecycle.
  • Proxy lazy init -> controls object access and resource loading

17. How does the Command pattern improve undo/redo functionality in applications?

The Command pattern encapsulates a request as an object, decoupling the client (invoker) from the receiver (executor). This structure is especially powerful for implementing undo/redo features:

  • Encapsulation of Requests: Each operation (like typing text, deleting, drawing a shape) is represented as a command object with execute() and undo() methods.
  • Storing History: Executed commands are pushed into a stack (or list). For undo, the last command is popped and its undo() is called. For redo, it is re-executed or moved from a redo stack.
  • Reversibility: Since each command contains the state needed to reverse its action, even complex operations can be undone.

Note: This makes applications like IDEs, text editors and graphics software highly modular and flexible.

18. Why is the Mediator pattern often preferred over Observer in complex systems?

Both Mediator and Observer decouple components, but they scale differently:

Observer Pattern:

  • Works well for event-driven systems by letting subjects notify multiple observers.
  • But in large systems, it creates event explosion (too many notifications) and hidden dependencies because components indirectly depend on each other.
  • Debugging becomes difficult since one change may trigger a chain of unexpected updates.

Mediator Pattern:

  • Introduces a central mediator object that controls communication between multiple components.
  • Components (colleagues) do not talk to each other directly but only through the mediator.
  • Example: In an airline booking system, Payment, Flight, Notification, and User services communicate via a BookingMediator.

Why preferred: Mediator reduces chaos, improves maintainability, centralizes complex interactions, and makes the system easier to evolve. Observer is better suited for smaller, lightweight event scenarios, but Mediator dominates in large-scale enterprise systems.

19. How does misuse of the Decorator pattern lead to the “Yo-Yo Problem”?

The Decorator pattern dynamically adds responsibilities to objects without modifying their class. However, excessive use can cause the “Yo-Yo Problem”:

Definition: The Yo-Yo Problem refers to difficulty in tracing program behavior because of too many indirections or layers of abstraction.

In Decorator Misuse:

  • If multiple decorators are chained (e.g., DataSource -> CompressionDecorator -> EncryptionDecorator -> CachingDecorator), the flow of control is spread across many small classes.
  • Developers must jump back and forth between decorators to understand actual behavior, making debugging tedious.
  • This reduces readability and increases maintenance cost.

Solution:

  • Use decorators sparingly and only when dynamic behavior addition is essential.
  • Prefer composition hierarchies or simpler design alternatives if many layers are needed.
  • Good documentation and clear naming conventions help mitigate confusion.

20. In what way does the SOLID principle guide the correct application of design patterns?

  • Single Responsibility Principle -> Encourages separation of concerns, supporting Strategy and Decorator.
  • Open/Closed Principle -> Drives use of Factory, Template Method and Observer for extensibility.
  • Liskov Substitution Principle -> Prevents misuse of inheritance in patterns like Proxy or Factory.
  • Interface Segregation Principle -> Encourages fine-grained interfaces, influencing Adapter and Bridge.
  • Dependency Inversion Principle -> Directly relates to Abstract Factory, DI and Service Locator patterns.

Thus, SOLID is not a pattern itself but the philosophy behind using patterns correctly.


Explore