Bird
Raised Fist0
Interview Prepoop-design-patternsmediumAmazonGoogleMicrosoftFlipkartRazorpaySwiggyZepto

Interface Segregation & Dependency Inversion - Fat Interfaces & IoC

Choose your preparation mode3 modes available
▶
Steps
setup

Identify Fat Interface

We start with a single fat interface 'IMultiFunctionDevice' that declares multiple unrelated methods for printing, scanning, and faxing.

💡 Recognizing a fat interface is the first step to applying Interface Segregation Principle (ISP).
Line:interface IMultiFunctionDevice { void print(Document d); void scan(Document d); void fax(Document d); }
💡 Fat interfaces bundle unrelated responsibilities, causing clients to depend on methods they don't use.
📊
Interface Segregation & Dependency Inversion - Fat Interfaces & IoC - Watch the Algorithm Execute, Step by Step
Watching this step-by-step visualization helps you understand how SOLID principles improve design by reducing coupling and increasing flexibility, which is hard to grasp from code alone.
Step 1/10
·Active fill★Answer cell
Shows a fat interface violating Interface Segregation Principle.
«interface»IMultiFunctionDevice
+print()
+scan()
+fax()
⚠ Clients depending on IMultiFunctionDevice must implement all methods, even if unused.
Demonstrates Interface Segregation Principle by splitting responsibilities.
«interface»IPrinter
+print()
«interface»IScanner
+scan()
«interface»IFax
+fax()
Shows class implementing a segregated interface.
«interface»IPrinter
+print()
Printer
+print()
Printer → IPrinter (1:1)
Another example of ISP applied to class design.
«interface»IScanner
+scan()
Scanner
+scan()
Scanner → IScanner (1:1)
Demonstrates Dependency Inversion and IoC by depending on interfaces and composing implementations.
«interface»IPrinter
+print()
«interface»IScanner
+scan()
MultiFunctionMachine
−printer: IPrinter
−scanner: IScanner
+print()
+scan()
MultiFunctionMachine → IPrinter (1:1)MultiFunctionMachine → IScanner (1:1)MultiFunctionMachine ◇ IPrinter (1:1)MultiFunctionMachine ◇ IScanner (1:1)
Constructor injection enables inversion of control.
MultiFunctionMachine
−printer: IPrinter
−scanner: IScanner
+MultiFunctionMachine()
+print()
+scan()
MultiFunctionMachine ◇ IPrinter (1:1)MultiFunctionMachine ◇ IScanner (1:1)
Delegation pattern used to forward calls.
MultiFunctionMachine
−printer: IPrinter
−scanner: IScanner
+print()
+scan()
Delegation pattern continued.
MultiFunctionMachine
−printer: IPrinter
−scanner: IScanner
+print()
+scan()
Dependency Inversion Principle illustrated by depending on interfaces.
«interface»IPrinter
«interface»IScanner
Printer
Scanner
MultiFunctionMachine
−printer: IPrinter
−scanner: IScanner
Printer → IPrinter (1:1)Scanner → IScanner (1:1)MultiFunctionMachine ◇ IPrinter (1:1)MultiFunctionMachine ◇ IScanner (1:1)
Final design combining ISP and DIP with IoC.
«interface»IPrinter
«interface»IScanner
«interface»IFax
Printer
Scanner
Fax
MultiFunctionMachine
−printer: IPrinter
−scanner: IScanner
−fax: IFax
Printer → IPrinter (1:1)Scanner → IScanner (1:1)Fax → IFax (1:1)MultiFunctionMachine ◇ IPrinter (1:1)MultiFunctionMachine ◇ IScanner (1:1)MultiFunctionMachine ◇ IFax (1:1)

Key Takeaways

✓ Fat interfaces force clients to depend on methods they don't use, violating Interface Segregation Principle.

This is hard to see from code alone because the problem is conceptual and relates to coupling, not syntax.

✓ Splitting interfaces into smaller, focused ones allows classes to implement only what they need, improving modularity.

Visualizing the interfaces and implementations side-by-side clarifies how responsibilities are separated.

✓ Dependency Inversion Principle and Inversion of Control decouple high-level modules from low-level modules by depending on abstractions and injecting dependencies.

Seeing the relationships and constructor injection visually makes the abstract DIP concept concrete.

Practice

(1/5)
1. You need to add multiple optional features to a core object dynamically at runtime without creating a subclass for every possible combination. Which design approach best supports this requirement?
easy
A. Apply memoization to cache feature combinations for reuse
B. Create a large inheritance hierarchy with subclasses for each feature combination
C. Use a greedy algorithm to select features based on priority
D. Use composition to wrap the core object with feature objects that add behavior dynamically

Solution

  1. Step 1: Understand the problem constraints

    The problem requires adding features dynamically without subclass explosion.
  2. Step 2: Identify the design pattern that supports dynamic behavior wrapping

    The Decorator Pattern uses composition to wrap objects and add behavior at runtime, avoiding subclassing for every combination.
  3. Final Answer:

    Option D -> Option D
  4. Quick Check:

    Dynamic wrapping via composition avoids subclass explosion [OK]
Hint: Dynamic wrapping avoids subclass explosion [OK]
Common Mistakes:
  • Thinking subclassing is the only way to add features
  • Confusing greedy algorithms with design patterns
2. In which scenario is encapsulation most beneficial when designing a class?
easy
A. When you want to expose all internal data directly for easy access
B. When you want to bundle data and methods while restricting direct access to internal state
C. When you want to avoid using any access modifiers and rely on global variables
D. When you want to implement multiple inheritance to reuse code

Solution

  1. Step 1: Understand encapsulation purpose

    Encapsulation bundles data and methods and restricts direct access to internal state to protect object integrity.
  2. Step 2: Analyze options

    When you want to expose all internal data directly for easy access exposes internal data directly, violating encapsulation. When you want to avoid using any access modifiers and rely on global variables ignores access control, risking data corruption. When you want to implement multiple inheritance to reuse code relates to inheritance, not encapsulation.
  3. Final Answer:

    Option B -> Option B
  4. Quick Check:

    Encapsulation is about bundling and controlled access, not exposing all data or inheritance.
Hint: Encapsulation bundles and hides, not exposes [OK]
Common Mistakes:
  • Confusing encapsulation with inheritance
  • Thinking encapsulation means no access at all
  • Believing global variables are encapsulated
3. Which of the following statements about handling concurrency in the Library Management System's LoanManager is INCORRECT?
medium
A. Optimistic locking is unsuitable because it always leads to deadlocks in high contention scenarios.
B. Using pessimistic locking on book records ensures no two users can borrow the same book simultaneously.
C. Implementing atomic check-and-update operations prevents race conditions during loan processing.
D. Using version numbers or timestamps can help detect conflicting updates in optimistic concurrency control.

Solution

  1. Step 1: Understand pessimistic locking

    Pessimistic locking prevents concurrent conflicting access, so Using pessimistic locking on book records ensures no two users can borrow the same book simultaneously. is correct.
  2. Step 2: Why optimistic locking is not always deadlock?

    Optimistic locking is unsuitable because it always leads to deadlocks in high contention scenarios. is incorrect; optimistic locking reduces deadlocks by retrying on conflicts.
  3. Step 3: Atomic operations prevent race conditions

    Implementing atomic check-and-update operations prevents race conditions during loan processing. is correct and essential for concurrency safety.
  4. Step 4: Versioning aids optimistic concurrency

    Using version numbers or timestamps can help detect conflicting updates in optimistic concurrency control. correctly describes conflict detection mechanisms.
  5. Final Answer:

    Option A -> Option A
  6. Quick Check:

    Optimistic locking reduces deadlocks; it does not always cause them.
Hint: Optimistic locking avoids deadlocks by retrying, pessimistic locking blocks access [OK]
Common Mistakes:
  • Believing optimistic locking always causes deadlocks
  • Confusing locking types and their effects
  • Ignoring atomicity in concurrency control
4. Which of the following statements about the Diamond Problem and its resolution is INCORRECT?
medium
A. The Diamond Problem occurs when a class inherits from two classes that both inherit from the same base class.
B. The Diamond Problem can be resolved by using virtual inheritance in languages like C++.
C. Method Resolution Order (MRO) is irrelevant to resolving the Diamond Problem.
D. Without proper resolution, the Diamond Problem can cause duplicate base class instances.

Solution

  1. Step 1: Understand the Diamond Problem

    It arises when a class inherits from two classes that share a common ancestor, causing ambiguity and duplication.
  2. Step 2: Virtual inheritance role

    Virtual inheritance in C++ ensures only one instance of the base class exists, resolving duplication.
  3. Step 3: Role of MRO

    MRO is critical in languages like Python to determine method lookup order and resolve ambiguity.
  4. Step 4: Identify incorrect statement

    Method Resolution Order (MRO) is irrelevant to resolving the Diamond Problem. incorrectly claims MRO is irrelevant, which is false.
  5. Final Answer:

    Option C -> Option C
  6. Quick Check:

    MRO is essential for resolving method lookup in diamond inheritance scenarios.
Hint: MRO is key to diamond resolution in dynamic languages
Common Mistakes:
  • Ignoring MRO's role in method lookup
  • Confusing virtual inheritance with method resolution
  • Assuming diamond problem only causes ambiguity, not duplication
5. Consider the following buggy code snippet from a thread-safe observer pattern implementation. Which line contains the subtle bug that can cause unsubscribed observers to still receive notifications?
def unsubscribe(self, observer, event_type):
    with self.lock:
        if event_type in self.observers:
            self.observers[event_type].remove(observer)
            if not self.observers[event_type]:
                del self.observers[event_type]
Assume that observer may not be subscribed to event_type when unsubscribe is called.
medium
A. Line 5: No handling of empty observer dictionary
B. Line 2: Lock is not acquired properly
C. Line 3: Missing check if observer is in the set before remove()
D. Line 4: Deleting event_type key prematurely

Solution

  1. Step 1: Analyze unsubscribe logic

    Line 3 calls remove() on the set without checking if observer exists, which raises KeyError if observer not subscribed.
  2. Step 2: Identify fix

    Check if observer in self.observers[event_type] before remove() to avoid errors and ensure proper removal.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    remove() without membership check causes runtime error [OK]
Hint: remove() without membership check can raise error [OK]
Common Mistakes:
  • Assuming remove() silently ignores missing elements
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