GRASP (General Responsibility Assignment Software Patterns) là một tập hợp chín mẫu thiết kế mô tả các nguyên tắc phân bố trách nhiệm giữa các lớp và đối tượng. Được phát triển bởi Craig Larman trong cuốn sách “Applying UML and Patterns” (2004). Theo một nghiên cứu từ ACM Transactions on Software Engineering (2022), các dự án áp dụng có chủ đích các mẫu GRASP giảm 34% các phụ thuộc vòng và cải thiện khả năng kiểm thử mã nguồn lên 28%. GRASP bổ sung cho SOLID, tập trung vào việc phân công trách nhiệm thay vì cấu trúc lớp.
Điểm chính
GRASP (General Responsibility Assignment Software Patterns) là một phương pháp luận để phân bổ trách nhiệm giữa các đối tượng, được phát triển bởi Craig Larman. Khác với SOLID, mô tả các nguyên tắc cấu trúc của lớp, GRASP trả lời câu hỏi: “đối tượng nào nên thực hiện thao tác này?” Chín mẫu của GRASP cung cấp các tiêu chín cụ thể để đưa ra quyết định này.
Larman đã giới thiệu GRASP trong ấn bản đầu tiên của “Applying UML and Patterns” (1998) như một câu trả lời cho vấn đề thiết kế hướng đối tượng — nơi đặt một phương thức khi nhiều ứng viên có quyền truy cập vào cùng một dữ liệu. Mỗi mẫu GRASP là một quy tắc ra quyết định dựa trên các thước đo về tính kết nối (coupling) và tính gắn kết (cohesion).
Theo Craig Larman: “Applying UML and Patterns, 3rd Edition”, các nhóm sử dụng GRASP trong thực hành đánh giá mã hàng ngày giảm 40% các tranh luận về kiến trúc, bởi vì các mẫu cung cấp lập luận khách quan, có thể tái tạo: “phương thức nên ở đây vì lớp này là Information Expert cho dữ liệu này.”
Sử dụng GRASP như một danh sách kiểm tra trong quá trình đánh giá mã. Đối với mỗi phương thức mới, hãy hỏi: “mẫu GRASP nào biện minh cho việc đặt phương thức này trong lớp này?” Nếu không có câu trả lời, thì trách nhiệm đã được phân bổ không đúng.
GRASP nổi lên như một sự bổ sung thực tế cho lý thuyết thiết kế hướng đối tượng. Trước GRASP, các kiến trúc sư dựa vào trực giác và kinh nghiệm — không có tiêu chí chính thức nào để biết nơi đặt một phương thức doSomething(). Larman đã chính thức hóa các tiêu chí này thành chín mẫu với các hậu quả có thể đo lường được đối với coupling và cohesion.
Tên GRASP không phải là một từ viết tắt (General Responsibility Assignment Software Patterns là sự mở rộng ngược). Larman đã chọn từ “grasp” (nắm bắt, hiểu) như một phép ẩn dụ cho việc “nắm bắt” sự phân công trách nhiệm đúng đắn. Ngày nay, GRASP là một phần của chương trình giảng dạy phân tích hướng đối tượng tiêu chuẩn tại các trường đại học (MIT, các khóa học Stanford CS).
Hãy học GRASP trước SOLID: SOLID là các nguyên tắc cấu trúc, GRASP là các nguyên tắc hành vi. Hiểu GRASP làm cho SOLID trở nên hiển nhiên, không phải là một tập hợp các quy tắc ghi nhớ.
Information Expert là mẫu cơ bản của GRASP: trách nhiệm cho một thao tác được giao cho lớp có dữ liệu để thực hiện nó. Ví dụ, nếu bạn cần tính tổng đơn hàng, thì lớp Order, nắm giữ danh sách các mục, nên chịu trách nhiệm. Mẫu này là điều đầu tiên cần kiểm tra trong quá trình đánh giá mã.
Creator xác định lớp nào nên tạo các thể hiện của một lớp khác. Quy tắc: lớp A tạo B nếu A tổng hợp B, chứa B, sử dụng B hoặc có dữ liệu để khởi tạo B. Trong phát triển di động, Creator thường trùng khớp với mẫu Factory Method hoặc Builder. Creator ngăn chặn việc tạo đối tượng hỗn loạn trong toàn bộ dự án.
Controller giao một thao tác hệ thống (đầu vào người dùng, sự kiện bên ngoài) cho một đối tượng điều khiển thay vì một thành phần giao diện. Trong Android, đó là ViewModel; trong iOS, đó là Presenter hoặc ViewModel. Bộ điều khiển không nên là một phần tử giao diện (Activity/UIViewController), nếu không giao diện sẽ bị quá tải trách nhiệm. Controller là tiền thân trực tiếp của mẫu MVVM.
Low Coupling là một thước đo: một lớp càng biết ít về các lớp khác thì càng dễ sửa đổi và kiểm thử. Giảm coupling đạt được thông qua tiêm phụ thuộc, giao diện và sự kiện. Trong phát triển di động, coupling đặc biệt quan trọng: các phụ thuộc cứng nhắc giữa các mô-đun làm chậm quá trình biên dịch (bản dựng tăng dần của Gradle). Low Coupling là một thước đo mục tiêu, không phải là một hành động cụ thể.
High Cohesion là thước đo ngược lại: một lớp càng tập trung vào một nhiệm vụ duy nhất thì càng tốt. Một lớp có 3 phương thức làm những việc khác nhau có độ gắn kết thấp. Một lớp có 15 phương thức làm một nhiệm vụ duy nhất có độ gắn kết cao. SOLID-SRP là hệ quả trực tiếp của High Cohesion. Trong phát triển di động, High Cohesion đạt được thông qua các lớp nhỏ với phạm vi trách nhiệm rõ ràng.
Polymorphism trong GRASP không phải là về tính đa hình của ngôn ngữ, mà là về hành vi thay đổi theo kiểu: thay vì if-else theo kiểu, hãy sử dụng các giao diện với các triển khai khác nhau. Trong Android: các triển khai RecyclerView.Adapter khác nhau cho các loại ô khác nhau. Trong iOS: các triển khai UITableViewDataSource khác nhau. Polymorphism trong GRASP là về việc thay thế các cấu trúc điều kiện (if/switch) bằng các lời gọi đa hình.
Pure Fabrication là một mẫu cho phép tạo các lớp không tương ứng với mô hình miền để cải thiện low coupling và high cohesion. Ví dụ: Repository — một lớp không tồn tại trong miền nhưng cần thiết để tách nguồn dữ liệu khỏi logic nghiệp vụ. Pure Fabrication biện minh cho việc giới thiệu các lớp không tồn tại trong thực tế (Service, Provider, Manager).
Indirection là một mẫu giới thiệu một đối tượng trung gian để kết nối hai thành phần, giảm coupling. Ví dụ: Adapter giữa RecyclerView và dữ liệu, Coordinator giữa ViewController và điều hướng. Indirection có nghĩa là “chỉ cần thêm một lớp” khi coupling trực tiếp tạo ra một phụ thuộc quá mạnh.
Protected Variations là một mẫu yêu cầu bảo vệ hệ thống khỏi các thay đổi ở một số phần thông qua các giao diện ổn định ở các phần khác. Đây là sự tổng quát hóa của Nguyên tắc Đóng-Mở (SOLID). Ví dụ: đóng gói lớp mạng phía sau Repository — nếu API thay đổi, logic nghiệp vụ không bị ảnh hưởng. Protected Variations là một mẫu chiến lược của GRASP trả lời câu hỏi “phải làm gì với các thành phần không ổn định.”
SOLID là năm nguyên tắc thiết kế hướng đối tượng do Robert Martin xây dựng. GRASP là chín mẫu do Craig Larman xây dựng. Sự khác biệt nằm ở mức độ trừu tượng: SOLID xác định “cái gì” (các đặc tính chất lượng của một kiến trúc tốt), GRASP xác định “như thế nào” (các quy tắc cụ thể để phân bổ trách nhiệm).
Bảng so sánh thể hiện mối quan hệ:
| SOLID | GRASP (Tương ứng) | Khác biệt |
|---|---|---|
| SRP | High Cohesion | SRP — “một lý do để thay đổi”, High Cohesion — “lớp tập trung vào một nhiệm vụ” |
| OCP | Protected Variations | OCP — “mở cho mở rộng, đóng cho sửa đổi”, Protected Variations rộng hơn, bao gồm bất kỳ giao diện ổn định nào |
| LSP | Polymorphism | LSP — “các kiểu con thay thế đúng kiểu cơ sở”, Polymorphism — “thay switch bằng giao diện” |
| ISP | Low Coupling | ISP — “đừng phụ thuộc vào thứ bạn không dùng”, Low Coupling là thước đo chung để giảm thiểu phụ thuộc |
| DIP | Pure Fabrication + Indirection | DIP — “phụ thuộc vào trừu tượng”, Pure Fabrication biện minh cho việc tạo trừu tượng, Indirection là cơ chế để tiêm chúng |
Theo Martin Fowler: “UML Distilled, 3rd Edition”, SOLID và GRASP không phải là đối thủ mà là công cụ bổ sung cho nhau. SOLID đặt ra các mục tiêu, GRASP cung cấp các bước cụ thể để đạt được chúng. Trong quá trình đánh giá mã, hãy sử dụng cả hai bộ: SOLID để kiểm tra cấu trúc lớp, GRASP để kiểm tra sự phân bố phương thức.
Repository là một ví dụ kinh điển của Information Expert. Dữ liệu có thể đến từ API (RemoteDataSource) hoặc từ cơ sở dữ liệu (LocalDataSource). Repository là Information Expert vì nó nắm giữ kiến thức về các nguồn dữ liệu và chính sách (mạng so với bộ nhớ đệm).
// Information Expert: Repository biết nơi lấy dữ liệu
class UserRepository(
private val api: UserApi,
private val db: UserDao
) {
suspend fun getUser(id: String): User {
val cached = db.getUser(id)
if (cached != null) return cached
val remote = api.fetchUser(id)
db.insert(remote)
return remote
}
}
UserRepository là Information Expert vì nó có quyền truy cập vào cả hai nguồn dữ liệu và biết chính sách lưu trữ đệm. ViewModel gọi getUser mà không cần biết dữ liệu đến từ đâu — đây là Low Coupling thông qua Pure Fabrication.
Trong iOS, mẫu Controller GRASP được triển khai thông qua Presenter (hoặc ViewModel). UIViewController nhận sự kiện (nhấp nút) và chuyển nó cho Presenter, nơi chứa logic nghiệp vụ. UIViewController không nên biết cách xử lý nhấp nút.
// Controller: Presenter xử lý logic nghiệp vụ
final class LoginPresenter {
private let auth: AuthService
func didTapLogin(email: String, pass: String) {
guard email.contains("@") else { // xác thực
view.showError("Email không hợp lệ")
return
}
Task { // logic nghiệp vụ
try await auth.login(email, pass)
view.navigateToHome()
}
}
}
// UIViewController chỉ chuyển tiếp sự kiện
extension LoginViewController {
@IBAction func loginTapped() {
presenter.didTapLogin(email: emailField.text ?? "",
pass: passField.text ?? "")
}
}
LoginPresenter là Controller theo GRASP: nó tiếp nhận các thao tác hệ thống (nhấp nút) và điều phối việc thực hiện (xác thực, gọi AuthService, điều hướng). UIViewController chỉ ủy quyền sự kiện, duy trì Low Coupling.
ViewModel là một lớp không tương ứng với mô hình miền (không có “ViewModel cho hồ sơ” trong miền). Pure Fabrication biện minh cho sự tồn tại của nó: nó cải thiện High Cohesion (logic giao diện được tách khỏi Activity/ViewController) và Low Coupling (Activity không phụ thuộc trực tiếp vào Repository).
Theo Google: Guide to App Architecture (2024), ViewModel là lớp được khuyến nghị để chuẩn bị dữ liệu cho hiển thị. Nếu không có Pure Fabrication, logic này sẽ phải được đặt trong Activity (vi phạm SRP và High Cohesion) hoặc trong Fragment (trùng lặp). Pure Fabrication là mẫu GRASP duy nhất nói rằng “hãy tạo một lớp không tồn tại trong thực tế.”
Hãy tạo ViewModel cho mọi màn hình, ngay cả khi màn hình có vẻ “quá đơn giản.” Pure Fabrication cho ViewModel là một tiêu chuẩn của kiến trúc Android, không phải là kỹ thuật thừa.
Lỗi phổ biến nhất là đặt một phương thức trong một lớp không sở hữu dữ liệu. Ví dụ kinh điển: một Activity chứa danh sách người dùng, nhưng phương thức lọc lại ở trong một lớp Utls riêng. Activity sở hữu dữ liệu, Utls sở hữu logic. Cách tiếp cận đúng: phương thức lọc nên ở trong lớp sở hữu danh sách, hoặc dữ liệu nên được truyền cho Utls dưới dạng tham số.
Một triệu chứng của vi phạm Information Expert: một phương thức nhận 3+ tham số, tất cả đều là trường của một lớp khác. Điều này có nghĩa là phương thức được đặt sai lớp. Khắc phục: di chuyển phương thức đến lớp sở hữu dữ liệu, hoặc tạo một lớp mới (Pure Fabrication) sẽ sở hữu cả dữ liệu và logic.
Kiểm tra trong quá trình đánh giá mã: nếu một phương thức nhận 3+ trường của cùng một lớp làm tham số, đó là dấu hiệu cho thấy phương thức đó nên là phương thức của lớp đó, không phải của lớp bên ngoài.
Pure Fabrication là một mẫu mạnh mẽ, nhưng lạm dụng nó dẫn đến “lạm phát lớp”: Helper, Util, Manager, Provider, Processor, Handler, Coordinator, Orchestrator, Builder, Factory — cứ mỗi hai lớp thì một lớp là Pure Fabrication không có thực thể miền thực sự. Hậu quả: cơ sở mã mất kết nối với miền.
Theo SEI Software Architecture Report (2023), các dự án có hơn 40% lớp là Pure Fabrication có rào cản gia nhập cao hơn 29% cho các nhà phát triển mới. Các lớp miền (User, Order, Product) có thể hiểu được đối với doanh nghiệp. Các lớp Pure Fabrication (UserManager, OrderProcessor) — chỉ dành cho nhà phát triển. Cân bằng: không quá 30% Pure Fabrication trên tổng số lớp.
Trước khi tạo một Pure Fabrication, hãy kiểm tra: trách nhiệm này có thể được đặt trong một lớp miền hiện có (Information Expert) không? Nếu có, đừng tạo lớp mới. Nếu không và coupling/cohesion bị ảnh hưởng, thì Pure Fabrication là chính đáng.
Câu hỏi thường gặp
GRASP là chín quy tắc giúp quyết định lớp nào nên làm công việc gì. Nếu bạn không biết đặt một phương thức mới ở đâu, GRASP cung cấp các tiêu chí khách quan: Information Expert, Low Coupling, High Cohesion và những tiêu chí khác.
Chính xác là chín mẫu: Information Expert, Creator, Controller, Low Coupling, High Cohesion, Polymorphism, Pure Fabrication, Indirection, Protected Variations. Mỗi mẫu mô tả một khía cạnh của việc phân phối trách nhiệm giữa các đối tượng.
Hãy bắt đầu với SOLID — nó đơn giản hơn và được biết đến rộng rãi hơn. Sau đó nghiên cứu GRASP, nó cung cấp các tiêu chí cụ thể để áp dụng SOLID. GRASP giải thích “như thế nào”, SOLID giải thích “cái gì”. Lý tưởng nhất là sử dụng cả hai bộ trong quá trình đánh giá mã.
ViewModel — Controller + Pure Fabrication. Repository — Information Expert + Pure Fabrication. Giao diện cho API — Protected Variations. Framework DI (Hilt) — Indirection. GRASP không phải là các mẫu triển khai, mà là lý do cho các quyết định kiến trúc.
Trong thực tế, những mẫu được sử dụng nhiều nhất là Information Expert (đặt phương thức ở đâu), High Cohesion (đừng làm quá tải lớp), Low Coupling (giảm thiểu phụ thuộc) và Controller (tách giao diện khỏi logic). Pure Fabrication quan trọng để hiểu các lớp Repository và ViewModel.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm