Bird
Raised Fist0
Interview Prepoop-design-patternsmediumAmazonGoogleMicrosoftFlipkartSwiggyRazorpayZepto

Factory vs Abstract Factory vs Builder - When to Use Each

Choose your preparation mode3 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Steps
setup

Define Product Class: House

The House class is defined to represent the complex object being built. It contains a list of parts and methods to add parts and display the house composition.

💡 Defining the product class is essential because it represents the final object that the builder will construct step-by-step.
Line:class House: def __init__(self): self.parts = []
💡 The product class holds the components that will be assembled by the builder.
📊
Factory vs Abstract Factory vs Builder - When to Use Each - Watch the Algorithm Execute, Step by Step
Watching each step reveals how the Builder pattern separates construction from representation, making complex object creation manageable and flexible.
Step 1/11
·Active fillAnswer cell
Defines the product class that will be built stepwise.
House
parts: List[str]
+__init__()
+add()
+__str__()
Builder interface holds and constructs the product.
House
parts: List[str]
+__init__()
+add()
+__str__()
HouseBuilder
house: House
+__init__()
+build_walls()
+build_roof()
+2 more
HouseBuilder House (1:1)
Builder method adds a specific part to the product.
House
parts: List[str]
+__init__()
+add()
+__str__()
HouseBuilder
house: House
+build_walls()
HouseBuilder House (1:1)
Builder method adds another specific part to the product.
House
parts: List[str]
+__init__()
+add()
+__str__()
HouseBuilder
house: House
+build_roof()
HouseBuilder House (1:1)
Builder method adds an optional part to the product.
House
parts: List[str]
+__init__()
+add()
+__str__()
HouseBuilder
house: House
+build_pool()
HouseBuilder House (1:1)
Builder exposes the final product after construction.
House
parts: List[str]
+__init__()
+add()
+__str__()
HouseBuilder
house: House
+get_result()
HouseBuilder House (1:1)
Director controls the building process using a builder.
House
parts: List[str]
+__init__()
+add()
+__str__()
HouseBuilder
house: House
+build_walls()
+build_roof()
+build_pool()
+1 more
Director
builder: HouseBuilder
+__init__()
+construct_basic_house()
+construct_luxury_house()
HouseBuilder House (1:1)Director HouseBuilder (1:1)
Director sequences builder calls to create a basic product.
House
parts: List[str]
+__init__()
+add()
+__str__()
HouseBuilder
house: House
+build_walls()
+build_roof()
Director
builder: HouseBuilder
+construct_basic_house()
HouseBuilder House (1:1)Director HouseBuilder (1:1)
Director sequences builder calls to create a luxury product.
House
parts: List[str]
+__init__()
+add()
+__str__()
HouseBuilder
house: House
+build_walls()
+build_roof()
+build_pool()
Director
builder: HouseBuilder
+construct_luxury_house()
HouseBuilder House (1:1)Director HouseBuilder (1:1)
Client retrieves the constructed product from the builder.
House
parts: List[str]
+__init__()
+add()
+__str__()
HouseBuilder
house: House
+get_result()
Director
builder: HouseBuilder
HouseBuilder House (1:1)Director HouseBuilder (1:1)
Product displays its assembled parts confirming construction.
House
parts: List[str]
+__init__()
+add()
+__str__()
HouseBuilder
house: House
Director
builder: HouseBuilder
HouseBuilder House (1:1)Director HouseBuilder (1:1)

Key Takeaways

Builder pattern separates complex object construction from its representation.

This separation is hard to grasp from code alone but becomes clear when watching stepwise construction.

Director controls the construction sequence, enabling different product variants.

Seeing the director call different builder methods clarifies how product complexity and customization are managed.

Builder methods modularly add parts, allowing optional features to be included flexibly.

Watching each part addition step reveals how optional components like pools are integrated without changing the product class.

Practice

(1/5)
1. You are designing a system where multiple unrelated classes must guarantee implementation of certain methods but share no common code. Which abstraction mechanism is most appropriate to enforce this contract?
easy
A. Use an abstract class to define the methods and provide partial implementation.
B. Use an abstract class only if all classes share a common ancestor.
C. Use a concrete class and rely on inheritance for code reuse.
D. Use an interface to declare the methods without any implementation.

Solution

  1. Step 1: Identify the need for a contract without shared code

    Interfaces define method signatures without implementation, perfect for unrelated classes needing a common contract.
  2. Step 2: Why abstract classes are less suitable here

    Abstract classes provide partial implementation and require a common ancestor, which unrelated classes lack.
  3. Step 3: Why concrete classes and inheritance don't fit

    Concrete classes imply implementation and inheritance assumes a hierarchy, which is not guaranteed.
  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    Interfaces enforce contracts without imposing inheritance or shared code.
Hint: Use interfaces when only a contract is needed, abstract classes when sharing code.
Common Mistakes:
  • Assuming abstract classes are always better for abstraction.
  • Confusing contract enforcement with code reuse.
  • Believing unrelated classes can share an abstract class.
2. When a class inherits from multiple classes that have a method with the same name, describe the step-by-step process the Method Resolution Order (MRO) uses to determine which method is called.
easy
A. MRO uses a linearization algorithm that merges the order of parents and their ancestors to find the method.
B. MRO searches the first parent class fully before moving to the next parent class.
C. MRO always calls the method from the last parent class listed in the inheritance.
D. MRO randomly picks the method from any parent class that defines it.

Solution

  1. Step 1: Understand naive search

    MRO does not simply search the first parent class fully before moving to the next; it uses a more sophisticated approach.
  2. Step 2: Recognize MRO linearization

    MRO uses a specific linearization (like C3 linearization) that merges parent classes and their ancestors in a consistent order.
  3. Step 3: Eliminate incorrect options

    MRO always calls the method from the last parent class listed in the inheritance is incorrect because the last parent is not always chosen; order and ancestors matter. MRO randomly picks the method from any parent class that defines it is incorrect because MRO is deterministic, not random.
  4. Final Answer:

    Option A -> Option A
  5. Quick Check:

    MRO merges inheritance hierarchies to find the correct method in a predictable order.
Hint: MRO = deterministic linearization of inheritance graph
Common Mistakes:
  • Assuming simple left-to-right search suffices
  • Believing last parent always overrides
  • Thinking method choice is random
3. What is a common trade-off or limitation when strictly applying the Single Responsibility Principle in a large system?
medium
A. It forces all classes to be tightly coupled to maintain cohesion.
B. It always reduces code complexity and never increases the number of classes.
C. It can lead to a proliferation of small classes, increasing the complexity of navigation and understanding.
D. It eliminates the need for interfaces or abstractions in the system.

Solution

  1. Step 1: Understand SRP effect

    SRP encourages splitting responsibilities, which can increase the number of classes.
  2. Step 2: Trade-off analysis

    More classes improve modularity but can make the system harder to navigate and understand due to fragmentation.
  3. Step 3: Evaluate other options

    It always reduces code complexity and never increases the number of classes. is false because SRP can increase class count; It forces all classes to be tightly coupled to maintain cohesion. is false because SRP reduces coupling; It eliminates the need for interfaces or abstractions in the system. is false because SRP does not remove the need for abstractions.
  4. Final Answer:

    Option C -> Option C
  5. Quick Check:

    More classes can complicate system navigation -> trade-off of SRP.
Hint: SRP trades fewer responsibilities for more classes.
Common Mistakes:
  • Believing SRP always simplifies codebase size.
  • Confusing coupling with cohesion effects.
  • Assuming SRP removes need for design patterns.
4. If a class exposes a public getter but no setter for a private field, what is a likely reason an interviewer would probe this design choice?
hard
A. To test if the candidate thinks public fields are better than getters
B. To confirm that the candidate knows getters always require setters
C. To verify that the candidate believes private fields should never be exposed
D. To check if the candidate understands immutability and controlled read-only access

Solution

  1. Step 1: Understand getter-only design

    Getter without setter implies read-only access, often for immutability or controlled exposure.
  2. Step 2: Analyze interviewer intent

    Interviewer probes if candidate recognizes design for immutability or encapsulation control.
  3. Step 3: Evaluate incorrect options

    To confirm that the candidate knows getters always require setters is false; setters are not always required. To verify that the candidate believes private fields should never be exposed is extreme; exposing read-only data is common. To test if the candidate thinks public fields are better than getters is a misconception favoring public fields.
  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    Getter-only design supports immutability and controlled access.
Hint: Getter-only means read-only, not incomplete encapsulation [OK]
Common Mistakes:
  • Assuming getters must have setters
  • Thinking private fields must never be exposed
  • Believing public fields are simpler and better
5. If the Composite pattern iterator is extended to allow reusing leaf components multiple times during traversal (e.g., shared leaves), which modification is necessary to ensure correct iteration without infinite loops?
hard
A. No change needed; the existing iterator handles reuse naturally.
B. Modify the iterator to push children in original order instead of reversed order.
C. Add a visited set to track and skip already visited components during iteration.
D. Convert the iterator to a recursive traversal to handle reuse correctly.

Solution

  1. Step 1: Understand reuse implications

    Reusing leaves means the same component can appear multiple times, risking infinite loops.
  2. Step 2: Identify solution to prevent infinite loops

    Tracking visited components prevents revisiting the same node repeatedly during iteration.
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Visited set avoids infinite loops with shared components [OK]
Hint: Track visited nodes to handle shared components safely [OK]
Common Mistakes:
  • Assuming no changes needed
  • Changing push order does not fix reuse loops