DEV Community

Maksym
Maksym

Posted on

Creational Design Patterns in Python. Part II

In previous article, we looked at patterns such as Singleton, Factory and Abstract Factory.
Today we would focus on remaining 2: Builder and Prototype.

Builder Pattern

The Builder pattern constructs complex objects step by step, allowing you to create different representations using the same construction process.

import requests class APIRequest: def __init__(self): self.method = "GET" self.url = "" self.headers = {} self.params = {} self.json = None def __str__(self): return ( f"{self.method} {self.url}\n" f"Headers: {self.headers}\n" f"Params: {self.params}\n" f"Body: {self.json}" ) class APIRequestBuilder: def __init__(self): self.request = APIRequest() def set_method(self, method: str): self.request.method = method.upper() return self def set_url(self, url: str): self.request.url = url return self def add_header(self, key: str, value: str): self.request.headers[key] = value return self def add_query_param(self, key: str, value: str): self.request.params[key] = value return self def set_json_body(self, data: dict): self.request.json = data return self def build(self): return self.request def send(self): print("Sending request:") print(self.request) print("-" * 50) response = requests.request( method=self.request.method, url=self.request.url, headers=self.request.headers, params=self.request.params, json=self.request.json ) return response class GitHubRequestDirector: def __init__(self, builder: APIRequestBuilder): self.builder = builder def get_user_repos(self, username): return ( self.builder.set_method("GET") .set_url(f"https://api.github.com/users/{username}/repos") .add_header("Accept", "application/vnd.github.v3+json") .build() ) if __name__ == "__main__": # Example 1: POST to JSONPlaceholder  post_builder = APIRequestBuilder() response = ( post_builder.set_method("POST") .set_url("https://jsonplaceholder.typicode.com/posts") .add_header("Content-Type", "application/json") .set_json_body({ "title": "Builder Pattern", "body": "Making API calls easier", "userId": 42 }) .send() ) print("Status:", response.status_code) print("Response JSON:", response.json()) print("=" * 80) # Example 2: GET GitHub repos using a director  github_builder = APIRequestBuilder() director = GitHubRequestDirector(github_builder) github_request = director.get_user_repos("octocat") response = requests.request( method=github_request.method, url=github_request.url, headers=github_request.headers ) print("GitHub Repos for octocat:") for repo in response.json()[:3]: # show only first 3  print("-", repo["name"]) 
Enter fullscreen mode Exit fullscreen mode

Use Cases

  • SQL query builders
  • Configuration builders
  • Document builders (PDF, HTML)
  • Game character creation
  • API request builders

Prototype Pattern

The Prototype pattern creates objects by cloning existing instances rather than creating new ones from scratch.

import copy import requests class Prototype: def clone(self): return copy.deepcopy(self) class APIRequest(Prototype): def __init__(self, method="GET", url="", headers=None, params=None, json=None): self.method = method self.url = url self.headers = headers or {} self.params = params or {} self.json = json def send(self): print(f"Sending {self.method} request to {self.url}") response = requests.request( method=self.method, url=self.url, headers=self.headers, params=self.params, json=self.json ) return response def __str__(self): return ( f"{self.method} {self.url}\n" f"Headers: {self.headers}\n" f"Params: {self.params}\n" f"Body: {self.json}" ) if __name__ == "__main__": # Step 1: Create a prototype for a POST request  post_template = APIRequest( method="POST", url="https://jsonplaceholder.typicode.com/posts", headers={"Content-Type": "application/json"}, json={"userId": 1} ) # Step 2: Clone the template and customize each  post1 = post_template.clone() post1.json["title"] = "Post 1" post1.json["body"] = "Body for post 1" post2 = post_template.clone() post2.json["title"] = "Post 2" post2.json["body"] = "Body for post 2" # Step 3: Send requests  for post in [post1, post2]: print("=" * 40) print("Request:") print(post) print("-" * 40) response = post.send() print("Status:", response.status_code) print("Response:", response.json()) 
Enter fullscreen mode Exit fullscreen mode

Use Cases

  • Document templates
  • Game object spawning
  • Configuration templates
  • UI component templates
  • Test data creation

When to Use Each Pattern

  • Builder: When constructing complex objects with many optional parameters, or when the construction process must allow different representations.
  • Prototype: When object creation is expensive and you have similar objects, or when you need to create objects based on templates.

Feel free to share your opinion and criticize this article.

Top comments (0)