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"])
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())
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)