YouTip LogoYouTip

Python Builder

The Builder pattern allows us to create different object representations using the same construction process. Imagine the process of building a house: whether building a villa or an apartment, both require laying the foundation, constructing walls, installing electrical and plumbing systems, etc., but the final results are completely different.

Core Idea

The Builder pattern separates the construction process of a complex object from its representation, so that the same construction process can create different representations. This pattern is particularly suitable for creating complex objects that contain multiple components.


Why Do We Need the Builder Pattern?

Problems with Traditional Construction Methods

Let's first look at an example without using the Builder pattern:

Example

class Computer:

    def __init__ (self, cpu, memory, storage, graphics_card, monitor):
        self.cpu= cpu
        self.memory= memory
        self.storage= storage
        self.graphics_card= graphics_card
        self.monitor= monitor

    def __str__ (self):
        return f"Computer: CPU={self.cpu}, Memory={self.memory}GB, Storage={self.storage}GB, Graphics={self.graphics_card}, Monitor={self.monitor}"

# Create computer object
computer = Computer("Intel i7",16,512,"NVIDIA RTX 3060","27inch 4K")
print(computer)

Disadvantages of this method:

  • Constructor has too many parameters, difficult to maintain
  • Must remember the order of all parameters
  • Creation process is not flexible enough
  • Poor code readability

Components of the Builder Pattern

The Builder pattern consists of four main roles:

Image 1

1. Product

The complex object to be created

2. Builder

Defines the abstract interface for creating the various parts of the product

3. ConcreteBuilder

Implements the Builder interface, constructs and assembles the various parts

4. Director

Constructs an object using the Builder interface


Complete Code Implementation

Let's fully implement the Builder pattern through an example of computer configuration:

1. Product Class

Example

class Computer:
    """Computer product class"""
    
    def __init__ (self):
        self.cpu=None
        self.memory=None
        self.storage=None
        self.graphics_card=None
        self.monitor=None
    
    def __str__ (self):
        specs =[]
        if self.cpu:
            specs.append(f"CPU: {self.cpu}")
        if self.memory:
            specs.append(f"RAM: {self.memory}GB")
        if self.storage:
            specs.append(f"Storage: {self.storage}GB")
        if self.graphics_card:
            specs.append(f"GPU: {self.graphics_card}")
        if self.monitor:
            specs.append(f"Monitor: {self.monitor}")
        return"Computer configuration:n" + "n".join(f" - {spec}"for spec in specs)

2. Abstract Builder

Example

from abc import ABC, abstractmethod

class ComputerBuilder(ABC):
    """Abstract computer builder class"""
    
    def __init__ (self):
        self.computer= Computer()
    
    @abstractmethod
    def build_cpu(self):
        pass
    
    @abstractmethod
    def build_memory(self):
        pass
    
    @abstractmethod
    def build_storage(self):
        pass
    
    @abstractmethod
    def build_graphics_card(self):
        pass
    
    @abstractmethod
    def build_monitor(self):
        pass
    
    def get_computer(self):
        return self.computer

3. Concrete Builder

Example

class GamingComputerBuilder(ComputerBuilder):
    """Gaming computer builder"""
    
    def build_cpu(self):
        self.computer.cpu="Intel i9-13900K"
    
    def build_memory(self):
        self.computer.memory=32
    
    def build_storage(self):
        self.computer.storage=2000# 2TB
    
    def build_graphics_card(self):
        self.computer.graphics_card="NVIDIA RTX 4090"
    
    def build_monitor(self):
        self.computer.monitor="32inch 4K 144Hz"

class OfficeComputerBuilder(ComputerBuilder):
    """Office computer builder"""
    
    def build_cpu(self):
        self.computer.cpu="Intel i5-13400"
    
    def build_memory(self):
        self.computer.memory=16
    
    def build_storage(self):
        self.computer.storage=512
    
    def build_graphics_card(self):
        self.computer.graphics_card="Integrated GPU"
    
    def build_monitor(self):
        self.computer.monitor="24inch 1080P"

4. Director

Example

class ComputerDirector:
    """Computer builder director"""
    
    def __init__ (self, builder):
        self.builder= builder
    
    def construct_computer(self):
        """Complete process of building a computer"""
        self.builder.build_cpu()
        self.builder.build_memory()
        self.builder.build_storage()
        self.builder.build_graphics_card()
        self.builder.build_monitor()
    
    def get_computer(self):
        return self.builder.get_computer()

Usage Example

Now let's see how to use the Builder pattern to create different types of computers:

Example

def main():
    print("=== Builder pattern demonstration ===n")
    
    # Create gaming computer
    print("1. Build gaming computer:")
    gaming_builder = GamingComputerBuilder()
    director = ComputerDirector(gaming_builder)
    director.construct_computer()
    gaming_computer = director.get_computer()
    print(gaming_computer)
    
    print("n" + "="*50 + "n")
    
    # Create office computer
    print("2. Build office computer:")
    office_builder = OfficeComputerBuilder()
    director = ComputerDirector(office_builder)
    director.construct_computer()
    office_computer = director.get_computer()
    print(office_computer)
    
    print("n" + "="*50 + "n")
    
    # Create custom computer
    print("3. Custom computer build:")
    custom_builder = CustomComputerBuilder()
    custom_builder.build_cpu("AMD Ryzen 7 7800X3D")
    custom_builder.build_memory(64)
    custom_builder.build_storage(4000)
    custom_builder.build_graphics_card("AMD RX 7900 XTX")
    custom_builder.build_monitor("34inch curved ultrawide monitor")
    custom_computer = custom_builder.get_computer()
    print(custom_computer)

# Custom builder (optional step construction)
class CustomComputerBuilder:
    """Custom computer builder"""
    
    def __init__ (self):
        self.computer= Computer()
    
    def build_cpu(self, cpu):
        self.computer.cpu= cpu
        return self# Returns self, supports method chaining
    
    def build_memory(self, memory):
        self.computer.memory= memory
        return self
    
    def build_storage(self, storage):
        self.computer.storage= storage
        return self
    
    def build_graphics_card(self, graphics_card):
        self.computer.graphics_card= graphics_card
        return self
    
    def build_monitor(self, monitor):
        self.computer.monitor= monitor
        return self
    
    def get_computer(self):
        return self.computer

if __name__ =="__main__":
    main()

Variations of the Builder Pattern

1. Fluent Interface Builder

Example

class Pizza:
    def __init__ (self):
        self.size=None
        self.cheese=False
        self.pepperoni=False
        self.mushrooms=False
    
    def __str__ (self):
        ingredients =[]
        if self.cheese: ingredients.append("Cheese")
        if self.pepperoni: ingredients.append("Italian sausage")
        if self.mushrooms: ingredients.append("Mushroom")
        return f"{self.size}inch pizza, toppings: {', '.join(ingredients)}"

class PizzaBuilder:
    def __init__ (self, size):
        self.pizza= Pizza()
        self.pizza.size= size
    
    def add_cheese(self):
        self.pizza.cheese=True
        return self
    
    def add_pepperoni(self):
        self.pizza.pepperoni=True
        return self
    
    def add_mushrooms(self):
        self.pizza.mushrooms=True
        return self
    
    def build(self):
        return self.pizza

# Using fluent interface
pizza =(PizzaBuilder(12)
         .add_cheese()
         .add_pepperoni()
         .add_mushrooms()
         .build())
print(pizza)

Advantages and Disadvantages of the Builder Pattern

Advantages

Advantage Description
Good encapsulation Construction process is separated from representation, clients don't need to know internal details
Strong extensibility New concrete builders can be easily added
Better control More fine-grained control over the construction process
Code readability Code is clearer when using chained calls

Disadvantages

Disadvantage Description
Increased complexity Requires defining multiple classes, increasing system complexity
Not suitable for large product differences If products differ greatly, the Builder pattern is not very suitable
Builder depends on product Builders need to know the specific details of the product

Applicable Scenarios

Situations suitable for using the Builder pattern:

  1. Creating complex objects: Need to create complex objects containing multiple components
  2. Stable construction process: The object's construction process is relatively stable, but the object's components change frequently
  3. Multiple representations: The object to be created has multiple different representations
  4. Avoiding telescoping constructors: Avoid using constructors with many parameters

Practical application examples:

  • Document converters: Converting documents to different formats such as PDF, Word, HTML
  • Meal preparation: Preparing meals with different combinations (burger, fries, drink)
  • UI component building: Building complex user interface components
  • Report generation: Generating reports containing different parts (header, data, charts)

Practice Exercises

Exercise 1: Implement a Car Builder

Try to implement a Car Builder pattern, containing the following components:

  • Engine (gasoline, electric, hybrid)
  • Body color (red, blue, black, white)
  • Wheel size (16-inch, 18-inch, 20-inch)
  • Interior (leather, fabric)

Exercise 2: Improve the Computer Builder

Add the following features to the computer builder:

  • Optional components (e.g., whether to install an optical drive)
  • Validation logic (e.g., memory cannot be less than 4GB)
  • Price calculation functionality

Summary

The Builder pattern is a powerful creational design pattern. By decomposing the construction process of a complex object into multiple simple steps, it makes the creation process more flexible and controllable. Although it increases system complexity, in scenarios where complex objects need to be created and the construction process is relatively fixed, the Builder pattern can significantly improve code maintainability and extensibility.

Remember the core idea of design patterns: not to use patterns for the sake of using patterns, but to solve specific design problems. In actual development, whether to use the Builder pattern should be decided based on specific requirements.

← Python AdapterPython Factory Pattern β†’