Giới thiệu
Trong bài viết trước, chúng ta đã khám phá một số mẫu thiết kế như Singleton, Factory và Abstract Factory. Hôm nay, chúng ta sẽ tiếp tục với hai mẫu thiết kế còn lại: Builder và Prototype. Những mẫu thiết kế này không chỉ giúp cải thiện cấu trúc của mã nguồn mà còn tối ưu hóa quy trình phát triển phần mềm.
Mẫu Builder
Định nghĩa
Mẫu Builder xây dựng các đối tượng phức tạp từng bước một, cho phép bạn tạo ra các biểu diễn khác nhau bằng cách sử dụng cùng một quy trình xây dựng. Mẫu này rất hữu ích khi bạn cần tạo ra các đối tượng với nhiều tham số tùy chọn mà không muốn tạo ra quá nhiều constructor.
Ví dụ Thực Tế
Dưới đây là ví dụ về cách sử dụng mẫu Builder để tạo ra một yêu cầu API trong Python:
python
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("Gửi yêu cầu:")
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__":
# Ví dụ 1: POST đến 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": "Làm cho việc gọi API trở nên dễ dàng hơn",
"userId": 42
})
.send()
)
print("Trạng thái:", response.status_code)
print("Phản hồi JSON:", response.json())
print("=" * 80)
# Ví dụ 2: GET các repo trên GitHub sử dụng 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("Các repo GitHub cho octocat:")
for repo in response.json()[:3]: # chỉ hiển thị 3 repo đầu tiên
print("-", repo["name"])
Các Tình Huống Sử Dụng
- Trình tạo truy vấn SQL
- Trình tạo cấu hình
- Trình tạo tài liệu (PDF, HTML)
- Tạo nhân vật trong game
- Trình tạo yêu cầu API
Mẫu Prototype
Định nghĩa
Mẫu Prototype tạo ra các đối tượng bằng cách sao chép các thể hiện hiện có thay vì tạo mới từ đầu. Mẫu này rất hữu ích khi việc tạo đối tượng tốn kém và bạn cần tạo ra nhiều đối tượng tương tự từ một mẫu.
Ví dụ Thực Tế
Dưới đây là ví dụ về cách sử dụng mẫu Prototype để gửi yêu cầu API:
python
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"Gửi yêu cầu {self.method} đến {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__":
# Bước 1: Tạo mẫu cho yêu cầu POST
post_template = APIRequest(
method="POST",
url="https://jsonplaceholder.typicode.com/posts",
headers={"Content-Type": "application/json"},
json={"userId": 1}
)
# Bước 2: Sao chép mẫu và tùy chỉnh từng yêu cầu
post1 = post_template.clone()
post1.json["title"] = "Bài viết 1"
post1.json["body"] = "Nội dung cho bài viết 1"
post2 = post_template.clone()
post2.json["title"] = "Bài viết 2"
post2.json["body"] = "Nội dung cho bài viết 2"
# Bước 3: Gửi yêu cầu
for post in [post1, post2]:
print("=" * 40)
print("Yêu cầu:")
print(post)
print("-" * 40)
response = post.send()
print("Trạng thái:", response.status_code)
print("Phản hồi:", response.json())
Các Tình Huống Sử Dụng
- Mẫu tài liệu
- Tạo đối tượng trong game
- Mẫu cấu hình
- Mẫu thành phần giao diện người dùng
- Tạo dữ liệu thử nghiệm
Khi Nào Sử Dụng Mỗi Mẫu
- Builder: Khi xây dựng các đối tượng phức tạp với nhiều tham số tùy chọn, hoặc khi quy trình xây dựng cần cho phép các biểu diễn khác nhau.
- Prototype: Khi việc tạo đối tượng đắt đỏ và bạn có các đối tượng tương tự, hoặc khi bạn cần tạo các đối tượng dựa trên mẫu.
Lưu Ý Khi Sử Dụng
- Mẫu Builder: Giúp quản lý cấu trúc mã tốt hơn, nhưng có thể phức tạp hơn khi chỉ cần một đối tượng đơn giản.
- Mẫu Prototype: Giúp tiết kiệm tài nguyên nhưng cần cẩn thận với việc sao chép để đảm bảo không tạo ra sai lệch dữ liệu.
Hỏi Đáp
Mẫu thiết kế nào tốt hơn khi tôi cần tạo nhiều đối tượng tương tự?
Mẫu Prototype sẽ là lựa chọn tốt hơn khi bạn cần tạo nhiều đối tượng tương tự vì nó giúp tiết kiệm tài nguyên.
Có nên sử dụng mẫu Builder cho các đối tượng đơn giản không?
Nếu đối tượng của bạn đơn giản và không có nhiều tham số, việc sử dụng mẫu Builder có thể là quá mức cần thiết.
Kết luận
Mẫu thiết kế Builder và Prototype đều là những công cụ mạnh mẽ trong lập trình Python, giúp bạn xây dựng mã nguồn dễ bảo trì và mở rộng. Hãy thử nghiệm và áp dụng chúng trong các dự án của bạn để nâng cao hiệu quả phát triển phần mềm. Đừng ngần ngại chia sẻ ý kiến và phê bình bài viết này để chúng ta cùng nhau học hỏi và phát triển!