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.
💡 The product class holds the components that will be assembled by the builder.
setup
Define Builder Interface: HouseBuilder
The HouseBuilder class is defined as the builder interface specifying methods to build parts of the House. It initializes a House instance to assemble.
💡 The builder interface abstracts the construction steps, allowing different builders to implement them differently.
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 fill★Answer 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. Trace the sequence of events when a player rolls the dice and lands on a ladder in a well-designed Snake and Ladder game. Which of the following best describes the correct order of internal state changes?
easy
A. Player rolls dice -> GameController calculates new position -> Board checks for ladder -> GameController updates Player position accordingly
B. Player rolls dice -> Player position updated -> Board checks for ladder -> Player position updated again if ladder found
C. Player rolls dice -> Board updates Player position directly if ladder found -> Player notified of new position
D. Player rolls dice -> Dice notifies Board -> Board updates Player position -> GameController confirms move
Solution
Step 1: Dice roll triggers GameController
Dice generates number; GameController uses it to calculate tentative position.
Step 2: Board checks for ladder or snake
GameController queries Board to see if new position has ladder or snake.
Step 3: GameController updates Player position
Based on Board's info, GameController updates Player's position accordingly.
Step 4: Player position is finalized
Player's position reflects ladder climb if applicable.
Final Answer:
Option A -> Option A
Quick Check:
Only GameController manages state changes; Board is queried but does not update Player directly.
Hint: GameController mediates all state changes after dice roll [OK]
Common Mistakes:
Assuming Player updates position before Board check
Believing Board directly updates Player position
Thinking Dice notifies Board
2. In a large software system, when would applying the Interface Segregation Principle (ISP) be most beneficial?
easy
A. When clients depend on interfaces that contain methods they do not use, causing unnecessary implementation burden.
B. When all clients require the exact same set of methods, so a single fat interface simplifies design.
C. When you want to enforce a strict inheritance hierarchy with minimal interfaces.
D. When you want to reduce the number of interfaces to simplify the codebase.
Solution
Step 1: Understand ISP's goal
ISP aims to prevent clients from depending on methods they don't use, avoiding fat interfaces that force unnecessary implementations.
Step 2: Analyze options
When clients depend on interfaces that contain methods they do not use, causing unnecessary implementation burden. correctly identifies the scenario where ISP helps by splitting fat interfaces. When all clients require the exact same set of methods, so a single fat interface simplifies design. describes a scenario where ISP is less needed. When you want to enforce a strict inheritance hierarchy with minimal interfaces. confuses inheritance hierarchy with interface segregation. When you want to reduce the number of interfaces to simplify the codebase. incorrectly assumes fewer interfaces always simplify design, ignoring interface misuse.
Final Answer:
Option A -> Option A
Quick Check:
ISP is about splitting interfaces to avoid forcing clients to implement unused methods.
Hint: ISP splits interfaces so clients only depend on what they use.
Common Mistakes:
Believing fewer interfaces always mean better design
Thinking ISP applies when all clients use all methods
Confusing ISP with inheritance hierarchy rules
3. Examine the following buggy CompositeIterator code snippet. Which line contains the subtle bug that causes incorrect traversal order?
medium
A. Line initializing self.stack without reversing children.
B. Line checking hasNext() before popping from stack.
C. Line popping component from stack.
D. Line returning the component after processing.
Solution
Step 1: Identify stack initialization issue
Stack is initialized with children in original order, not reversed, causing traversal order reversal.
Step 2: Confirm impact on traversal order
Without reversing, popping from stack yields children in reverse order, breaking expected traversal.
Final Answer:
Option A -> Option A
Quick Check:
Reversing children on stack initialization fixes traversal order [OK]
Hint: Stack must reverse children to preserve traversal order [OK]
Common Mistakes:
Forgetting to reverse children on stack push
Misplacing hasNext() check
4. Suppose you have a legacy system that violates the Open/Closed Principle by frequently modifying core classes for new features. What is the best strategy to refactor it to comply with OCP without breaking existing functionality?
hard
A. Replace all core classes with interfaces and rewrite all implementations from scratch
B. Use global variables to control behavior changes instead of modifying classes
C. Continue modifying core classes but add feature flags to isolate changes
D. Introduce abstract base classes or interfaces and create new subclasses for new features, leaving existing classes unchanged
Solution
Step 1: Understand refactoring goals
Refactor to enable extension via new subclasses without modifying existing tested classes.
Step 2: Why introducing abstract base classes or interfaces and creating new subclasses for new features, leaving existing classes unchanged fits
Introducing abstractions and subclassing allows adding features by extension, preserving existing code.
Step 3: Why other options fail
Replace all core classes with interfaces and rewrite all implementations from scratch is costly and risky; Continue modifying core classes but add feature flags to isolate changes continues violating OCP; Use global variables to control behavior changes instead of modifying classes introduces global state, increasing complexity and bugs.
Final Answer:
Option D -> Option D
Quick Check:
Refactor by abstraction and extension, not rewrite or global state hacks.
Hint: Refactor with abstractions and subclasses, keep existing code intact [OK]
Common Mistakes:
Thinking rewrite is the only way to comply with OCP
Believing feature flags solve OCP violations
Using global variables as a workaround
5. In a legacy system, a class handles both business logic and logging. You want to refactor it following SRP, but the logging code is tightly intertwined with business logic. What is the best approach to refactor this while respecting SRP?
hard
A. Ignore SRP in this case because legacy code should not be refactored.
B. Leave logging inside the class because extracting it would break existing functionality and increase risk.
C. Merge business logic and logging into a utility class to centralize all cross-cutting concerns.
D. Extract logging into a separate class and replace logging calls with calls to this new class, even if it requires modifying many places.
Solution
Step 1: Identify responsibilities
Business logic and logging are separate reasons to change.
Step 2: Refactor strategy
Extract logging into its own class to isolate changes related to logging.
Step 3: Address tight coupling
Though intertwined, refactoring calls to the new logging class improves maintainability and respects SRP.
Step 4: Evaluate other options
Options B and D avoid refactoring, risking future issues; C increases coupling by merging unrelated concerns.
Final Answer:
Option D -> Option D
Quick Check:
Extract and isolate responsibilities even if it requires effort -> SRP compliance.
Hint: Separate concerns even if intertwined; refactor stepwise.
Common Mistakes:
Avoiding refactoring due to perceived risk.
Merging unrelated concerns for convenience.
Ignoring SRP in legacy code.
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.