1. Giới thiệu chung về sơ đồ lớp

Sơ đồ lớp (Class Diagram) là một trong những biểu đồ quan trọng nhất của UML (Unified Modeling Language). Nó mô tả cấu trúc tĩnh của hệ thống bằng cách thể hiện các lớp, các thuộc tính, các phương thức và mối quan hệ giữa chúng. Khi chúng ta thực hiện phân tích và thiết kế phần mềm, việc vẽ sơ đồ lớp giúp:

  • Hiểu rõ kiến trúc: Nắm bắt được các thành phần chính và cách chúng tương tác.
  • Giao tiếp hiệu quả: Đưa ra một ngôn ngữ chung cho các thành viên trong nhóm, từ nhà phân tích, nhà thiết kế, tới lập trình viên.
  • Kiểm tra tính đầy đủ: Xác định xem các yêu cầu nghiệp vụ đã được hiện thực hóa trong mô hình hay chưa.
  • Hỗ trợ lập trình hướng đối tượng: Khi mô hình đã hoàn thiện, việc chuyển sang code sẽ trở nên suôn sẻ hơn.

Trong bài viết này, chúng ta sẽ đi sâu vào cách vẽ sơ đồ lớp một cách chi tiết, từ việc chuẩn bị, lựa chọn công cụ, xác định các thành phần chính, tới việc biểu diễn các mối quan hệ phức tạp. Nội dung được chia thành các phần rõ ràng, mỗi phần sẽ cung cấp ví dụ thực tế và mẹo thực hành để bạn có thể áp dụng ngay vào dự án của mình.

2. Bước chuẩn bị trước khi vẽ sơ đồ lớp

2.1. Thu thập và phân tích yêu cầu

Trước khi bắt tay vào vẽ sơ đồ lớp, bạn cần nắm vững các yêu cầu chức năng và phi chức năng của hệ thống. Các tài liệu quan trọng bao gồm:

Tài liệu Mô tả
Use Case Diagram Xác định các tác nhân và các trường hợp sử dụng, giúp bạn biết hệ thống cần cung cấp những chức năng gì.
Yêu cầu nghiệp vụ (Business Requirements) Mô tả quy trình, quy tắc nghiệp vụ, các đối tượng quan trọng trong lĩnh vực.
Biểu đồ tuần tự (Sequence Diagram) Cho thấy tương tác thời gian giữa các đối tượng, hỗ trợ xác định các phương thức cần có.
Biểu đồ trạng thái (State Diagram) Nếu có các đối tượng có trạng thái phức tạp, biểu đồ này giúp bạn xác định thuộc tính trạng thái.

2.2. Xác định phạm vi (Scope) của sơ đồ

Một dự án lớn có thể có nhiều sơ đồ lớp (các mô-đun, các tầng, hoặc các miền nghiệp vụ). Bạn cần quyết định:

Cách Vẽ Sơ Đồ Lớp
Cách Vẽ Sơ Đồ Lớp
  • Sơ đồ lớp cấp cao (High‑level Class Diagram): Chỉ hiển thị các lớp chính, các package và mối quan hệ tổng quan.
  • Sơ đồ lớp chi tiết (Detailed Class Diagram): Bao gồm các thuộc tính, phương thức, và các chi tiết kỹ thuật như kiểu dữ liệu, phạm vi truy cập (public, private, protected).

2.3. Lựa chọn công cụ vẽ

Có rất nhiều công cụ hỗ trợ vẽ UML, mỗi công cụ có ưu nhược điểm riêng:

Công cụ Miễn phí / Trả phí Đặc điểm nổi bật
StarUML Trả phí (có bản trial) Giao diện hiện đại, hỗ trợ nhiều loại biểu đồ.
PlantUML Miễn phí (open‑source) Viết mô tả bằng ngôn ngữ text, dễ tích hợp vào CI/CD.
Visual Paradigm Miễn phí (Community) & Trả phí Tích hợp mạnh mẽ với IDE, có tính năng reverse engineering.
Lucidchart Trả phí (có bản trial) Dễ dùng, làm việc nhóm trực tuyến.
draw.io (diagrams.net) Miễn phí Đơn giản, tích hợp Google Drive.

Nếu bạn làm việc trong môi trường Agile, PlantUML là lựa chọn tốt vì bạn có thể viết sơ đồ trong file .md hoặc .puml và tự động sinh ra hình ảnh trong repo.

3. Các thành phần cơ bản của sơ đồ lớp

3.1. Lớp (Class)

Mỗi lớp được biểu diễn bằng một hộp chia thành ba phần:

  1. Tên lớp (được viết ở giữa, thường in đậm và có thể có stereotype như <<interface>>).
  2. Thuộc tính (Attributes) – liệt kê các biến thành viên.
  3. Phương thức (Operations) – liệt kê các hàm thành viên.

Quy tắc đặt tên và ký hiệu

Thành phần Quy tắc đặt tên Ký hiệu truy cập
Thuộc tính tênThuộcTính : KiểuDữLiệu + (public), - (private), # (protected), ~ (package)
Phương thức tênPhươngThức(đốiSố) : KiểuTrảVề Cũng dùng các ký hiệu trên.

Ví dụ:

+---------------------------+
| Customer |
+---------------------------+
| -customerId : int |
| -name : String |
| -email : String |
+---------------------------+
| +getId() : int |
| +setName(name : String) |
| +placeOrder(o : Order) |
+---------------------------+

3.2. Giao diện (Interface)

Giao diện là một lớp đặc biệt chỉ chứa phương thức trừu tượng (không có phần cài đặt). Khi vẽ, bạn thường đặt stereotype <<interface>> phía trên tên.

+---------------------+
| <<interface>> |
| Payable |
+---------------------+
| +pay(amount : double) : void |
+---------------------+

3.3. Gói (Package)

Package dùng để đóng gói các lớp có liên quan. Khi vẽ, bạn vẽ một hình chữ nhật lớn, bên trong là các lớp thuộc gói. Gói giúp giảm độ phức tạp khi sơ đồ có nhiều lớp.

Cách Vẽ Sơ Đồ Lớp
Cách Vẽ Sơ Đồ Lớp

3.4. Các loại quan hệ

Loại quan hệ Ký hiệu Mô tả
Association Đường thẳng Mối quan hệ “có” (has‑a). Có thể có nhãn role và multiplicity (1, 0.., 1..).
Aggregation Đường thẳng + hình thoi rỗng “Có” yếu, phần tử có thể tồn tại độc lập.
Composition Đường thẳng + hình thoi đen “Có” mạnh, phần tử không tồn tại riêng (được tạo và hủy cùng chủ thể).
Generalization Đường thẳng mũi tên rỗng (triangular) Kế thừa (inheritance).
Realization Đường thẳng nét đứt + mũi tên rỗng Lớp thực thi giao diện.
Dependency Đường thẳng nét đứt Một lớp phụ thuộc vào lớp khác (sử dụng tạm thời).

Ví dụ về mối quan hệ:

Customer "1" <>-- "0.." Order
Order "1" -- "1.." OrderItem
Payment "1" ..> "1" Payable (realization)

4. Quy trình vẽ sơ đồ lớp chi tiết

4.1. Bước 1: Xác định các lớp chính

Dựa vào Use Case Diagram và Yêu cầu nghiệp vụ, liệt kê các đối tượng (entity) quan trọng. Thông thường, các đối tượng trong cơ sở dữ liệu (tables) hoặc đối tượng nghiệp vụ sẽ trở thành lớp.

Kỹ thuật “Noun Extraction”: Đọc tài liệu yêu cầu, gạch dưới mọi danh từ (customer, order, invoice, payment…). Sau đó, loại bỏ các danh từ không cần thiết (system, manager) và nhóm lại thành các lớp.

4.2. Bước 2: Xác định thuộc tính và phương thức

  • Thuộc tính: Dựa trên các trường dữ liệu (field) trong bảng hoặc thông tin cần lưu trữ.
  • Phương thức: Dựa trên các hành động (verb) liên quan tới lớp. Ví dụ, “place order” → phương thức placeOrder() trong lớp Customer.

Mẹo: Không nên đưa tất cả các thuộc tính vào sơ đồ lớp chi tiết. Chỉ đưa những thuộc tính quan trọng đối với kiến trúc, các thuộc tính phụ có thể để trong tài liệu thiết kế chi tiết.

Cách Vẽ Sơ Đồ Lớp
Cách Vẽ Sơ Đồ Lớp

4.3. Bước 3: Xác định mối quan hệ

  • Association: Khi một lớp cần tham chiếu tới lớp khác. Ví dụ, Order có một Customer.
  • Aggregation / Composition: Khi một lớp chứa danh sách các đối tượng khác. Quyết định aggregation hay composition dựa vào vòng đời:
  • Nếu đối tượng con có thể tồn tại độc lập → Aggregation.
  • Nếu đối tượng con không tồn tại nếu không có đối tượng cha → Composition.
  • Generalization: Khi có quan hệ “là một” (is‑a). Ví dụ, Employee là một Person.
  • Realization: Khi một lớp thực thi một giao diện. Ví dụ, CreditCardPayment thực thi Payable.

4.4. Bước 4: Thêm multiplicity (số lượng)

Multiplicity cho biết số lượng đối tượng ở mỗi đầu của quan hệ:

Multiplicity Ý nghĩa
1 Một đối tượng duy nhất
0..1 Không hoặc một
hoặc 0.. Không giới hạn
1.. Ít nhất một
n Chính xác n đối tượng

Ví dụ: Một Customer có nhiều Order → Customer "1" <>-- "0.." Order.

4.5. Bước 5: Đánh dấu visibility (phạm vi truy cập)

Sử dụng ký hiệu +, -, #, ~ để chỉ ra public, private, protected, package. Điều này giúp lập trình viên hiểu rõ mức độ truy cập.

4.6. Bước 6: Kiểm tra tính nhất quán và hoàn thiện

  • Kiểm tra vòng lặp: Đảm bảo không có vòng lặp vô nghĩa (ví dụ: A contains B và B contains A mà không có quan hệ aggregation/composition hợp lý).
  • Kiểm tra tên: Tên lớp, thuộc tính, phương thức nên dùng CamelCase (Java) hoặc PascalCase (C#) và phản ánh đúng nghiệp vụ.
  • Đánh giá mức độ chi tiết: Đối với tài liệu thiết kế, sơ đồ lớp chi tiết không nên quá dài (tránh hơn 30‑40 lớp trong một sơ đồ). Nếu quá, hãy chia thành các package hoặc sub‑diagrams.

5. Ví dụ thực tế: Vẽ sơ đồ lớp cho một hệ thống bán hàng trực tuyến

5.1. Phân tích yêu cầu

  • Các tác nhân: Khách hàng, Quản trị viên, Hệ thống thanh toán.
  • Các chức năng chính:
  • Đăng ký / đăng nhập.
  • Duyệt danh mục sản phẩm.
  • Thêm sản phẩm vào giỏ hàng.
  • Tạo đơn hàng.
  • Thanh toán (credit card, PayPal, COD).
  • Quản lý sản phẩm, đơn hàng (admin).

5.2. Xác định lớp chính

Lớp Mô tả
User Thông tin chung của người dùng (username, password).
Customer Kế thừa User, có thông tin cá nhân, địa chỉ.
Admin Kế thừa User, có quyền quản trị.
Product Thông tin sản phẩm (id, name, price, stock).
Category Nhóm sản phẩm.
Cart Giỏ hàng của khách, chứa CartItem.
CartItem Mỗi mục trong giỏ (product, quantity).
Order Đơn hàng, chứa OrderItem.
OrderItem Chi tiết từng mặt hàng trong đơn.
Payment (abstract) Lớp trừu tượng cho các phương thức thanh toán.
CreditCardPayment Thực thi Payment.
PayPalPayment Thực thi Payment.
CODPayment Thực thi Payment.
Invoice Hóa đơn, liên quan tới Order.

5.3. Vẽ sơ đồ lớp (chi tiết)

Cách Vẽ Sơ Đồ Lớp
Cách Vẽ Sơ Đồ Lớp
+------------------------+ +------------------------+
| <<abstract>> Payment |<>------- | CreditCardPayment |
+------------------------+ +------------------------+
| -amount : double | | +process() : boolean |
| -date : Date | +------------------------+
+------------------------+ ^ | +process() : boolean | | +------------------------+ | ^ | | | +------------------------+ +------------------------+
| PayPalPayment | | CODPayment |
+------------------------+ +------------------------+
| +process() : boolean | | +process() : boolean |
+------------------------+ +------------------------+ +------------------------+ +------------------------+
| User |<|----------| Admin |
+------------------------+ Generalization +------------------------+
| -userId : int | | +manageProduct() : void |
| -username : String | | +viewReports() : void |
| -password : String | +------------------------+
+------------------------+
| +login() : boolean |
| +logout() : void |
+------------------------+ User <|-- Customer
+------------------------+ +------------------------+
| Customer | | Category |
+------------------------+ +------------------------+
| -customerId : int | | -categoryId : int |
| -name : String | | -name : String |
| -email : String | +------------------------+
+------------------------+ | +addProduct(p:Product) |
| +placeOrder(o:Order) | +------------------------+
| +addToCart(i:CartItem) |
+------------------------+ Customer "1" <>-- "1" Cart
Cart "1" -- "0.." CartItem
CartItem -- Product CartItem -- Product
Order "1" -- "1.." OrderItem
OrderItem -- Product
Order "1" ..> Payment
Order "1" --> Invoice
Product -- Category
  • Giải thích:
  • Payment là lớp trừu tượng, các lớp con (CreditCardPayment, PayPalPayment, CODPayment) thực thi phương thức process().
  • Customer kế thừa User, có quan hệ composition với Cart.
  • Cart composition với CartItem; mỗi CartItem tham chiếu tới một Product.
  • Order composition với OrderItem và phụ thuộc (dependency) vào Payment để thực hiện thanh toán.
  • Product aggregation với Category (một sản phẩm thuộc một danh mục, nhưng danh mục vẫn tồn tại khi sản phẩm bị xóa).

5.4. Kiểm tra và tinh chỉnh

  • Multiplicity: Đảm bảo rằng một Customer chỉ có một Cart ("1"), nhưng một Cart có thể chứa nhiều CartItem ("0..").
  • Visibility: Thuộc tính nhạy cảm (password) được đánh dấu - (private), các phương thức công cộng (login, placeOrder) được đánh dấu +.
  • Package: Đối với dự án lớn, chúng ta có thể nhóm các lớp vào các package:
  • com.ecommerce.user → User, Customer, Admin.
  • com.ecommerce.product → Product, Category.
  • com.ecommerce.order → Cart, CartItem, Order, OrderItem, Invoice.
  • com.ecommerce.payment → Payment, các lớp con.

6. Các nguyên tắc thiết kế nâng cao trong sơ đồ lớp

6.1. Nguyên tắc SOLID

  • Single Responsibility Principle (SRP): Mỗi lớp chỉ nên có một trách nhiệm duy nhất. Ví dụ, không nên để lớp Order thực hiện cả việc tính thuế và gửi email xác nhận; tạo các lớp TaxCalculator, EmailService.
  • Open/Closed Principle (OCP): Lớp nên mở để mở rộng, đóng để sửa đổi. Việc sử dụng Payment abstract class và các subclass là một ví dụ điển hình.
  • Liskov Substitution Principle (LSP): Các lớp con phải có thể thay thế lớp cha mà không làm hỏng chương trình. Khi thiết kế CreditCardPayment, đảm bảo nó thực hiện đầy đủ hợp đồng process().
  • Interface Segregation Principle (ISP): Không ép buộc lớp phải triển khai các phương thức không cần. Thay vì một giao diện PaymentService chứa processCreditCard, processPayPal, processCOD, chúng ta chia thành các giao diện riêng.
  • Dependency Inversion Principle (DIP): Các module cấp cao không nên phụ thuộc vào module cấp thấp mà nên phụ thuộc vào abstraction. Khi Order phụ thuộc vào Payment (abstraction) chứ không phải trực tiếp vào CreditCardPayment.

6.2. Design Patterns thường gặp trong sơ đồ lớp

Pattern Mô tả ngắn Áp dụng trong sơ đồ lớp
Factory Method Tạo đối tượng mà không xác định lớp cụ thể. PaymentFactory để tạo đối tượng Payment dựa trên loại thanh toán.
Strategy Định nghĩa một họ thuật toán, đóng gói chúng và làm cho chúng có thể hoán đổi. Các lớp Payment là các strategy cho việc thanh toán.
Observer Khi một đối tượng thay đổi, các đối tượng đăng ký sẽ được thông báo. Khi Order thay đổi trạng thái, EmailService (observer) gửi email.
Decorator Thêm hành vi động cho đối tượng. GiftWrapDecorator cho OrderItem.
Composite Trao đổi đối tượng đơn lẻ và tập hợp như nhau. Cart và CartItem (có thể áp dụng Composite nếu muốn xử lý giỏ hàng như một đối tượng duy nhất).

6.3. Tối ưu hoá mối quan hệ

  • Tránh quá nhiều quan hệ “Association”: Nếu một lớp chỉ cần một thuộc tính ID để liên kết, không cần vẽ association chi tiết, chỉ ghi chú “FK”.
  • Sử dụng Interface để giảm coupling: Thay vì Order phụ thuộc trực tiếp vào CreditCardPayment, nó phụ thuộc vào Payment interface.
  • Sử dụng Dependency Injection (DI): Khi mô hình được chuyển sang code, các phụ thuộc sẽ được tiêm (inject) vào thông qua constructor hoặc setter, giảm việc khởi tạo trực tiếp.

7. Thực hành vẽ sơ đồ lớp với PlantUML

Nếu bạn muốn tự động sinh sơ đồ từ mã text, PlantUML là công cụ lý tưởng. Dưới đây là ví dụ code cho sơ đồ lớp ở phần 5:

Cách Vẽ Sơ Đồ Lớp
Cách Vẽ Sơ Đồ Lớp
@startuml
' Định nghĩa các interface và abstract class
abstract class Payment { -amount : double -date : Date +process() : boolean
}
class CreditCardPayment { +process() : boolean
}
class PayPalPayment { +process() : boolean
}
class CODPayment { +process() : boolean
}
Payment <|-- CreditCardPayment
Payment <|-- PayPalPayment
Payment <|-- CODPayment ' Định nghĩa User hierarchy
class User { -userId : int -username : String -password : String +login() : boolean +logout() : void
}
class Customer { -customerId : int -name : String -email : String +placeOrder(o:Order) : void +addToCart(i:CartItem) : void
}
class Admin { +manageProduct() : void +viewReports() : void
}
User <|-- Customer
User <|-- Admin ' Các lớp nghiệp vụ
class Product { -productId : int -name : String -price : double -stock : int
}
class Category { -categoryId : int -name : String
}
class Cart {
}
class CartItem {
}
class Order { -orderId : int -date : Date -status : String
}
class OrderItem {
}
class Invoice {
}
' Quan hệ
Customer "1" <>-- "1" Cart
Cart "1" -- "0.." CartItem
CartItem -- Product
Order "1" -- "1.." OrderItem
OrderItem -- Product
Order "1" ..> Payment
Order "1" --> Invoice
Product -- Category
@enduml

Sau khi lưu vào file ecommerce.puml, chạy lệnh:

plantuml ecommerce.puml

Sẽ tạo ra file ecommerce.png chứa sơ đồ lớp. Việc này giúp đồng bộ hóa tài liệu và tự động cập nhật khi thay đổi mô hình.

8. Lưu ý khi đưa sơ đồ lớp vào tài liệu dự án

  1. Định dạng: Sử dụng PNG hoặc SVG để đảm bảo độ nét khi in và hiển thị trên web.
  2. Phiên bản: Đánh số phiên bản cho mỗi sơ đồ (ví dụ: ClassDiagram_v1.2.pdf). Khi có thay đổi, cập nhật số phiên bản và ghi chú thay đổi (Change Log).
  3. Mô tả ngắn gọn: Mỗi sơ đồ nên có một đoạn mô tả (khoảng 2‑3 câu) giải thích mục đích và phạm vi.
  4. Liên kết tới tài liệu khác: Đặt hyperlink từ lớp tới tài liệu chi tiết (ví dụ: Customer → CustomerSpecification.docx).
  5. Đánh dấu “TODO”: Nếu một lớp chưa hoàn thiện, dùng comment // TODO: add attributes trong PlantUML hoặc chú thích trong hình.

9. Kiểm tra chất lượng sơ đồ lớp bằng checklist

Tiêu chí Kiểm tra
Tên lớp Đúng chuẩn, không trùng, phản ánh nghiệp vụ.
Thuộc tính Đủ, đúng kiểu, có visibility.
Phương thức Đủ, có tham số, trả về giá trị, có visibility.
Quan hệ Đúng loại (association, aggregation, composition, generalization, realization, dependency).
Multiplicity Được đặt ở cả hai đầu của quan hệ, logic với nghiệp vụ.
Package Các lớp được nhóm hợp lý, giảm độ phức tạp.
Sự nhất quán Không có vòng lặp vô nghĩa, không có lớp thừa.
Tuân thủ SOLID Đánh giá nhanh các nguyên tắc SOLID.
Định dạng Đúng định dạng file, có chú thích phiên bản.

Nếu mọi mục trong checklist đều “đạt”, sơ đồ lớp của bạn đã sẵn sàng cho phát triển và đánh giá.

10. Tổng kết

Việc vẽ sơ đồ lớp không chỉ là một nhiệm vụ kỹ thuật mà còn là một nghệ thuật truyền đạt kiến trúc phần mềm. Bằng cách:

  1. Thu thập yêu cầu kỹ lưỡng,
  2. Xác định lớp, thuộc tính, phương thức một cách có hệ thống,
  3. Biểu diễn đúng loại quan hệ và multiplicity,
  4. Áp dụng các nguyên tắc thiết kế (SOLID, Design Patterns),
  5. Sử dụng công cụ phù hợp (PlantUML, StarUML, v.v.) và đảm bảo tính nhất quán,

bạn sẽ tạo ra một sơ đồ lớp đầy đủ, dễ hiểu và dễ bảo trì. Điều này không chỉ giúp đội ngũ phát triển nhanh chóng chuyển từ thiết kế sang code mà còn giảm thiểu rủi ro lỗi thiết kế, tăng tính mở rộng và độ tin cậy của hệ thống.

Hy vọng bài viết chi tiết trên đã cung cấp cho bạn một hướng dẫn toàn diện để vẽ sơ đồ lớp chuẩn, từ khâu chuẩn bị đến kiểm tra cuối cùng. Hãy áp dụng ngay vào dự án hiện tại của bạn, và đừng quên cập nhật sơ đồ mỗi khi có thay đổi kiến trúc – vì một sơ đồ lớp luôn sống động chính là chìa khóa của một dự án phần mềm thành công.

Rate this post
Mục nhập này đã được đăng trong Blog. Đánh dấu trang permalink.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *