Bird
Raised Fist0
LLDsystem_design~25 mins

Bridge pattern in LLD - System Design Exercise

Choose your learning style10 modes available
Design: Bridge Pattern Implementation
Design and implement the Bridge pattern for a simple example such as shapes with different drawing APIs. Out of scope: complex UI frameworks or third-party libraries.
Functional Requirements
FR1: Separate an abstraction from its implementation so both can vary independently
FR2: Support multiple implementations for the same abstraction
FR3: Allow adding new abstractions and implementations without changing existing code
FR4: Enable clients to use abstractions without knowing about implementation details
Non-Functional Requirements
NFR1: Design should minimize code duplication
NFR2: Should be easy to extend with new abstractions or implementations
NFR3: Low coupling between abstraction and implementation
NFR4: Code should be maintainable and testable
Think Before You Design
Questions to Ask
❓ Question 1
❓ Question 2
❓ Question 3
❓ Question 4
Key Components
Abstraction interface or abstract class
Implementation interface
Concrete implementations of the implementation interface
Refined abstractions extending the abstraction interface
Design Patterns
Bridge pattern
Dependency Injection
Strategy pattern (for comparison)
Factory pattern (for creating implementations)
Reference Architecture
Client
  |
  v
Abstraction <----> Implementation Interface
  |                    |
  v                    v
Refined Abstraction  Concrete Implementations

Example:
Shape (Abstraction) <----> DrawingAPI (Implementation)
Circle (Refined Abstraction)   DrawingAPI1, DrawingAPI2 (Concrete Implementations)
Components
Abstraction
Abstract class or interface
Defines the abstraction's interface and holds a reference to an implementation
Refined Abstraction
Concrete class extending Abstraction
Implements the abstraction interface and delegates work to the implementation
Implementation Interface
Interface
Defines the interface for implementation classes
Concrete Implementations
Concrete classes implementing Implementation Interface
Provide different implementations of the abstraction's behavior
Client
Any
Uses the abstraction without knowing about the implementation details
Request Flow
1. Client creates an instance of Refined Abstraction and passes a Concrete Implementation
2. Client calls operation on Refined Abstraction
3. Refined Abstraction delegates the call to the Implementation interface
4. Concrete Implementation executes the operation
5. Result is returned back to the client
Database Schema
Not applicable for this design pattern example
Scaling Discussion
Bottlenecks
Tight coupling if abstraction holds concrete implementation directly
Difficulty adding new implementations if interfaces are not well designed
Code duplication if abstractions and implementations are not properly separated
Solutions
Use interfaces and abstract classes to decouple abstraction and implementation
Follow SOLID principles to keep interfaces focused and extendable
Use dependency injection to provide implementations to abstractions
Write unit tests for abstractions and implementations separately
Interview Tips
Time: Spend 5 minutes explaining the problem and motivation, 15 minutes designing the pattern with example, 10 minutes discussing pros, cons, and scaling, 5 minutes for questions
Explain the problem of tight coupling between abstraction and implementation
Describe how Bridge pattern separates abstraction from implementation
Show example with shapes and drawing APIs
Discuss how this pattern improves flexibility and maintainability
Mention trade-offs like added complexity
Explain how to extend with new abstractions or implementations

Practice

(1/5)
1. What is the main purpose of the Bridge pattern in system design?
easy
A. To create a single class that handles all functionalities
B. To separate an abstraction from its implementation so they can vary independently
C. To enforce a strict inheritance hierarchy without flexibility
D. To combine multiple unrelated classes into one

Solution

  1. Step 1: Understand the Bridge pattern concept

    The Bridge pattern is designed to separate an abstraction from its implementation, allowing both to change independently.
  2. Step 2: Compare options with the concept

    To separate an abstraction from its implementation so they can vary independently correctly states this separation. Other options describe different or incorrect design approaches.
  3. Final Answer:

    To separate an abstraction from its implementation so they can vary independently -> Option B
  4. Quick Check:

    Bridge pattern = separation of abstraction and implementation [OK]
Hint: Bridge = separate interface and implementation [OK]
Common Mistakes:
  • Confusing Bridge with Adapter or Decorator patterns
  • Thinking Bridge merges classes instead of separating
  • Assuming Bridge enforces rigid inheritance
2. Which of the following is the correct way to define the Bridge pattern structure in code?
easy
A. class Implementor extends Abstraction {}
B. class Abstraction extends Implementor {}
C. class Abstraction { Implementor impl; }
D. class Abstraction implements Implementor {}

Solution

  1. Step 1: Recall Bridge pattern structure

    Bridge pattern uses composition: the abstraction holds a reference to the implementor interface.
  2. Step 2: Analyze each option

    class Abstraction { Implementor impl; } correctly shows the required composition. The other options use inheritance (extends) or interface implementation (implements), which defeats the purpose of decoupling via composition.
  3. Final Answer:

    class Abstraction { Implementor impl; } -> Option C
  4. Quick Check:

    Bridge uses composition, not inheritance [OK]
Hint: Bridge uses composition, not inheritance [OK]
Common Mistakes:
  • Using inheritance instead of composition
  • Confusing implements keyword with composition
  • Ignoring the implementor interface role
3. Given the following code snippet using the Bridge pattern, what will be the output?
class DrawingAPI {
  drawCircle(x, y, radius) { console.log(`API drawing circle at (${x},${y}) with radius ${radius}`); }
}

class Shape {
  constructor(drawingAPI) { this.drawingAPI = drawingAPI; }
  draw() {}
}

class Circle extends Shape {
  constructor(x, y, radius, drawingAPI) {
    super(drawingAPI);
    this.x = x; this.y = y; this.radius = radius;
  }
  draw() {
    this.drawingAPI.drawCircle(this.x, this.y, this.radius);
  }
}

const api = new DrawingAPI();
const circle = new Circle(5, 10, 7, api);
circle.draw();
medium
A. API drawing circle at (5,10) with radius 7
B. Circle drawn at (5,10) with radius 7
C. Error: drawCircle is not a function
D. No output

Solution

  1. Step 1: Trace the draw() method call

    The Circle's draw method calls drawingAPI.drawCircle with x=5, y=10, radius=7.
  2. Step 2: Check DrawingAPI's drawCircle implementation

    It logs: "API drawing circle at (5,10) with radius 7" to the console.
  3. Final Answer:

    API drawing circle at (5,10) with radius 7 -> Option A
  4. Quick Check:

    drawCircle logs correct message [OK]
Hint: Follow method calls and parameters carefully [OK]
Common Mistakes:
  • Assuming drawCircle is undefined
  • Confusing output message text
  • Ignoring parameter values passed
4. Identify the error in the following Bridge pattern implementation:
class Implementor {
  operation() { console.log('Implementor operation'); }
}

class Abstraction {
  constructor() { this.impl = new Implementor(); }
  operation() { this.impl.operation(); }
}

const abs = new Abstraction();
abs.operation();
medium
A. Abstraction should not create Implementor instance directly
B. Implementor should be an interface or abstract class, not a concrete class
C. operation method should return a value
D. No error, code works fine

Solution

  1. Step 1: Review Bridge pattern best practice

    Abstraction should receive Implementor via constructor injection, not create it internally, to allow flexibility.
  2. Step 2: Analyze code's constructor

    Abstraction creates Implementor directly, which tightly couples them and breaks Bridge pattern intent.
  3. Final Answer:

    Abstraction should not create Implementor instance directly -> Option A
  4. Quick Check:

    Bridge needs abstraction to hold implementor, not create it [OK]
Hint: Inject implementor, don't instantiate inside abstraction [OK]
Common Mistakes:
  • Ignoring tight coupling caused by direct instantiation
  • Thinking concrete class Implementor is always wrong
  • Expecting operation to return value unnecessarily
5. You are designing a graphics system that supports multiple shapes (Circle, Square) and multiple rendering APIs (OpenGL, DirectX). How does the Bridge pattern help in this design?
hard
A. By using inheritance to create a subclass for each shape-API combination
B. By forcing all shapes to use a single rendering API implementation
C. By merging shape and rendering API classes into one large class
D. By allowing shapes and rendering APIs to vary independently without subclass explosion

Solution

  1. Step 1: Understand the problem of subclass explosion

    If each shape-API pair requires a subclass, the number grows quickly (e.g., CircleOpenGL, CircleDirectX).
  2. Step 2: Apply Bridge pattern advantage

    Bridge separates shape abstraction from rendering implementation, so shapes and APIs can be combined dynamically without many subclasses.
  3. Final Answer:

    By allowing shapes and rendering APIs to vary independently without subclass explosion -> Option D
  4. Quick Check:

    Bridge reduces subclass explosion by separating abstraction and implementation [OK]
Hint: Bridge avoids subclass explosion by separating concerns [OK]
Common Mistakes:
  • Thinking Bridge merges classes instead of separating
  • Assuming inheritance must handle all combinations
  • Ignoring dynamic combination of abstraction and implementation
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