TÓM TẮT

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ó cho phép chúng ta mô tả cấu trúc tĩnh của một hệ thống phần mềm, bao gồm các lớp, thuộc tính, phương thức và mối quan hệ giữa chúng. Khi thiết kế một dự án phần mềm, việc vẽ sơ đồ lớp đúng cách không chỉ giúp các thành viên trong nhóm hiểu rõ kiến trúc hệ thống mà còn hỗ trợ quá trình phát triển, bảo trì và mở rộng phần mềm trong tương lai.

1.1. Vai trò của sơ đồ lớp trong phát triển phần mềm

  • Truyền đạt ý tưởng thiết kế: Khi một kiến trúc sư phần mềm đưa ra ý tưởng, sơ đồ lớp là công cụ trực quan giúp truyền đạt nhanh chóng cho lập trình viên và các bên liên quan.
  • Xác định ranh giới và trách nhiệm: Bằng cách mô tả các lớp và các phương thức, chúng ta có thể phân định rõ ràng trách nhiệm của từng thành phần.
  • Hỗ trợ lập trình hướng đối tượng (OOP): Sơ đồ lớp là bản đồ cho việc cài đặt các khái niệm OOP như kế thừa, đa hình, trừu tượng.
  • Kiểm tra tính toàn vẹn và tối ưu hoá: Khi xem xét toàn bộ cấu trúc, chúng ta có thể phát hiện các lớp thừa, quan hệ vòng lặp không cần thiết hay các phụ thuộc chặt chẽ quá mức.

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

Thành phần Ký hiệu Mô tả
Lớp (Class) Hình chữ nhật chia thành 3 phần Phần trên: tên lớp; phần giữa: thuộc tính; phần dưới: phương thức
Thuộc tính (Attribute) Tên: kiểu Định nghĩa trạng thái của lớp
Phương thức (Operation) Tên(params): kiểu trả về Định nghĩa hành vi của lớp
Quan hệ (Relationship) Đa dạng (association, aggregation, composition, inheritance, dependency) Mô tả cách các lớp tương tác và phụ thuộc lẫn nhau

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

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

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ông việc này thường bao gồm:

  1. Phân tích các Use Case: Xác định các tình huống sử dụng (actors, actions) để biết hệ thống sẽ thực hiện những gì.
  2. Xác định các đối tượng thực tế: Dựa trên yêu cầu, liệt kê các thực thể (entity) như “Khách hàng”, “Đơn hàng”, “Sản phẩm”,…
  3. Xác định các luồng dữ liệu: Hiểu cách dữ liệu di chuyển giữa các đối tượng để xác định quan hệ.

2.2. Chọn công cụ vẽ

Có rất nhiều công cụ hỗ trợ vẽ sơ đồ lớp, từ phần mềm trả phí đến các công cụ mã nguồn mở và trực tuyến:

Cách Vẽ Sơ Đồlớp
Cách Vẽ Sơ Đồlớp
Công cụ Loại Ưu điểm Nhược điểm
Microsoft Visio Trả phí Giao diện quen thuộc, tích hợp Office Giá cao
StarUML Trả phí Hỗ trợ nhiều loại biểu đồ UML, export đa dạng Cần mua bản quyền
PlantUML Mã nguồn mở Viết bằng ngôn ngữ mô tả, dễ tích hợp CI/CD Không có giao diện kéo-thả
draw.io (diagrams.net) Miễn phí Trực tuyến, tích hợp Google Drive Một số tính năng UML không đầy đủ
Lucidchart Trả phí/miễn phí (giới hạn) Dễ dùng, cộng tác thời gian thực Giới hạn số biểu đồ trong bản miễn phí

Lựa chọn công cụ phụ thuộc vào môi trường làm việc, ngân sách và sở thích cá nhân. Đối với người mới bắt đầu, draw.io hoặc Lucidchart là lựa chọn tốt vì tính trực quan và không cần cài đặt.

2.3. Xác định mức độ chi tiết

Không phải mọi sơ đồ lớp đều cần chi tiết ở mức độ thuộc tính và phương thức. Bạn có thể:

  • Sơ đồ lớp cấp cao (high-level): Chỉ mô tả các lớp chính và quan hệ chính, thường dùng trong giai đoạn thiết kế kiến trúc.
  • Sơ đồ lớp chi tiết (detailed): Bao gồm toàn bộ thuộc tính, phương thức, kiểu dữ liệu và các ràng buộc, dùng trong giai đoạn thiết kế chi tiết và lập trình.

3. Các bước vẽ sơ đồ lớp chi tiết

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

Sử dụng danh sách các thực thể đã thu thập ở bước 2.1, hãy viết ra các lớp tiềm năng. Ví dụ, trong một hệ thống bán hàng trực tuyến, chúng ta có thể có:

  • Customer (Khách hàng)
  • Order (Đơn hàng)
  • Product (Sản phẩm)
  • ShoppingCart (Giỏ hàng)
  • Payment (Thanh toán)
  • Invoice (Hóa đơn)

3.2. Bước 2: Định nghĩa thuộc tính và phương thức

3.2.1. Thuộc tính (Attributes)

Mỗi lớp cần xác định các thuộc tính quan trọng, bao gồm:

  • Tên thuộc tính (không nên quá dài)
  • Kiểu dữ liệu (int, String, Date, List<…>, …)
  • Mức độ truy cập (public, private, protected)

Ví dụ:

Class Customer
- customerId : String
- name : String
- email : String
- phoneNumber : String
- address : String

3.2.2. Phương thức (Operations)

Xác định các hành vi mà lớp thực hiện. Đặt tên phương thức sao cho mô tả rõ ràng hành động và truyền đạt ý định:

Cách Vẽ Sơ Đồlớp
Cách Vẽ Sơ Đồlớp
+ register(email: String, password: String): boolean
+ login(email: String, password: String): boolean
+ updateProfile(newInfo: Map<String, String>): void

Lưu ý:

  • Sử dụng +, -, # để biểu thị public, private, protected.
  • Thêm các tham số và kiểu trả về để làm rõ hơn.

3.3. Bước 3: Xác định quan hệ giữa các lớp

3.3.1. Association (Kết nối)

Khi một lớp cần giữ tham chiếu tới một hoặc nhiều đối tượng của lớp khác.

  • Multiplicity (độ đa dạng): 1, 0..1, , 0.., 1..,…
  • Direction (hướng): Mũi tên chỉ hướng sử dụng.

Ví dụ: Customer 1 — 0..Order (một khách hàng có thể có nhiều đơn hàng).

3.3.2. Aggregation (Tập hợp)

Mối quan hệ “có” yếu, nơi lớp chứa có thể tồn tại độc lập với lớp con. Được biểu thị bằng một hình thoi rỗng.

Ví dụ: Order <>— Product (một đơn hàng gồm nhiều sản phẩm, nhưng sản phẩm tồn tại độc lập).

3.3.3. Composition (Thành phần)

Mối quan hệ “có” mạnh, lớp chứa quyết định vòng đời của lớp con. Được biểu thị bằng một hình thoi đen.

Ví dụ: ShoppingCart ■— CartItem (khi giỏ hàng bị xóa, các mục trong giỏ cũng bị xóa).

3.3.4. Inheritance (Kế thừa)

Biểu thị quan hệ “là một” (is-a). Dùng mũi tên rỗng chỉ lên lớp cha.

Ví dụ: Payment có các lớp con CreditCardPayment, PayPalPayment, BankTransferPayment.

3.3.5. Dependency (Phụ thuộc)

Một lớp tạm thời sử dụng lớp khác (thường trong một phương thức). Được biểu thị bằng mũi tên nét đứt.

Ví dụ: Invoice ..> Payment (hóa đơn phụ thuộc vào thanh toán để tạo).

3.4. Bước 4: Thêm các ràng buộc (Constraints)

Bạn có thể thêm các ràng buộc bằng ký hiệu {} hoặc ghi chú (note) để làm rõ:

  • {unique} trên thuộc tính email của Customer.
  • {ordered} trên tập hợp OrderItem trong Order.

3.5. Bước 5: Kiểm tra tính nhất quán và tối ưu hoá

Sau khi hoàn thiện sơ đồ, thực hiện các kiểm tra:

  1. Không có vòng lặp vô nghĩa: Ví dụ, A → B → A chỉ tồn tại khi có nghĩa logic (như bi‑directional association).
  2. Độ đa dạng hợp lý: Đảm bảo multiplicity phản ánh đúng yêu cầu nghiệp vụ.
  3. Không có lớp thừa: Nếu một lớp chỉ có một thuộc tính và không có hành vi, cân nhắc hợp nhất.
  4. Tuân thủ nguyên tắc SOLID: Đặc biệt là Single Responsibility (SRP) và Interface Segregation.

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

4.1. Các lớp chính

Lớp Thuộc tính (rút gọn) Phương thức (rút gọn)
Customer customerId, name, email, password, address register(), login(), updateProfile()
Product productId, name, description, price, stockQuantity updateStock(), applyDiscount()
Order orderId, orderDate, status, totalAmount calculateTotal(), cancelOrder()
OrderItem quantity, unitPrice getSubTotal()
ShoppingCart cartId, createdDate addItem(), removeItem(), clearCart()
CartItem quantity getTotalPrice()
Payment (abstract) paymentId, amount, paymentDate processPayment()
CreditCardPayment cardNumber, expiryDate, cvv processPayment()
PayPalPayment paypalAccount processPayment()
Invoice invoiceId, issueDate, dueDate, isPaid generatePDF(), markAsPaid()

4.2. Quan hệ chi tiết

Cách Vẽ Sơ Đồlớp
Cách Vẽ Sơ Đồlớp
  1. Customer – Order: 1 khách hàng có 0.. đơn hàng (association).
    plaintext Customer "1" -------- "0.." Order

  2. Order – OrderItem: Order composition OrderItem. Mỗi OrderItem không tồn tại nếu không có Order.
    plaintext Order "1" ◼── "0.." OrderItem

  3. OrderItem – Product: OrderItem aggregation Product. Product tồn tại độc lập.
    plaintext OrderItem <>── "1" Product

  4. Customer – ShoppingCart: 1 khách hàng có 0..1 giỏ hàng (association).
    plaintext Customer "1" -------- "0..1" ShoppingCart

  5. ShoppingCart – CartItem: ShoppingCart composition CartItem.
    plaintext ShoppingCart "1" ◼── "0.." CartItem

  6. CartItem – Product: CartItem aggregation Product.
    plaintext CartItem <>── "1" Product

  7. Order – Payment: Order dependency Payment (một order có thể có nhiều phương thức thanh toán).
    plaintext Order ..> Payment

  8. Payment – CreditCardPayment / PayPalPayment: Inheritance.
    plaintext Payment <|-- CreditCardPayment Payment <|-- PayPalPayment

  9. Invoice – Order: Invoice association 1‑1 (một hóa đơn tương ứng một đơn hàng).
    plaintext Invoice "1" -------- "1" Order

4.3. Đồ họa mẫu (được mô tả bằng PlantUML)

@startuml
' Lớp Customer
class Customer { -customerId : String -name : String -email : String {unique} -password : String -address : String +register(email:String,password:String):boolean +login(email:String,password:String):boolean +updateProfile(info:Map<String,String>):void
} ' Lớp Product
class Product { -productId : String -name : String -description : String -price : double -stockQuantity : int +updateStock(qty:int):void +applyDiscount(percent:double):void
} ' Lớp Order và OrderItem
class Order { -orderId : String -orderDate : Date -status : String -totalAmount : double +calculateTotal():double +cancelOrder():void
}
class OrderItem { -quantity : int -unitPrice : double +getSubTotal():double
}
Order "1" ◼-- "0.." OrderItem
OrderItem <>-- "1" Product ' Lớp ShoppingCart và CartItem
class ShoppingCart { -cartId : String -createdDate : Date +addItem(p:Product, qty:int):void +removeItem(p:Product):void +clearCart():void
}
class CartItem { -quantity : int +getTotalPrice():double
}
ShoppingCart "1" ◼-- "0.." CartItem
CartItem <>-- "1" Product ' Quan hệ Customer
Customer "1" -- "0.." Order
Customer "1" -- "0..1" ShoppingCart ' Payment hierarchy
abstract class Payment { -paymentId : String -amount : double -paymentDate : Date +processPayment():boolean
}
class CreditCardPayment { -cardNumber : String -expiryDate : Date -cvv : String
}
class PayPalPayment { -paypalAccount : String
}
Payment <|-- CreditCardPayment
Payment <|-- PayPalPayment Order ..> Payment ' Invoice
class Invoice { -invoiceId : String -issueDate : Date -dueDate : Date -isPaid : boolean +generatePDF():byte +markAsPaid():void
}
Invoice "1" -- "1" Order
@enduml

Lưu ý: Đoạn mã trên có thể copy vào bất kỳ công cụ hỗ trợ PlantUML (như https://plantuml.com/zh/online) để xem sơ đồ thực tế.

5. Các nguyên tắc thiết kế khi vẽ sơ đồ lớp

5.1. Nguyên tắc “Single Responsibility”

Mỗi lớp chỉ nên chịu trách nhiệm cho một khía cạnh duy nhất của nghiệp vụ. Nếu một lớp có quá nhiều phương thức không liên quan, hãy tách ra thành các lớp phụ.

5.2. Nguyên tắc “Open/Closed”

Các lớp nên mở để mở rộng (thêm chức năng) nhưng đóng để sửa đổi. Sử dụng kế thừa hoặc giao diện (interface) để đạt được mục tiêu này.

5.3. Nguyên tắc “Liskov Substitution”

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

Các lớp con phải có thể thay thế lớp cha mà không làm thay đổi tính đúng đắn của chương trình. Khi vẽ inheritance, đảm bảo các phương thức của lớp con tuân thủ hợp đồng (contract) của lớp cha.

5.4. Nguyên tắc “Interface Segregation”

Nếu một lớp phải thực thi nhiều giao diện, hãy chia giao diện thành các phần nhỏ, chỉ chứa các phương thức cần thiết. Trên sơ đồ lớp, giao diện được biểu diễn bằng lớp kiểu <<interface>>.

5.5. Nguyên tắc “Dependency Inversion”

Các module cấp cao không nên phụ thuộc vào các module cấp thấp mà nên phụ thuộc vào abstraction (giao diện). Trên sơ đồ lớp, thể hiện bằng việc lớp cao cấp phụ thuộc vào một interface, còn các lớp thực thi phụ thuộc vào interface đó.

6. Thực hành: Bước chuyển từ sơ đồ lớp sang code

Sau khi có sơ đồ lớp, việc chuyển đổi sang code thực tế thường diễn ra như sau:

  1. Tạo các lớp (class) trong ngôn ngữ lập trình (Java, C#, Python,…). Đặt tên lớp và file sao cho phù hợp.
  2. Định nghĩa thuộc tính: Sử dụng các modifiers (private, protected) và tạo getter/setter nếu cần.
  3. Cài đặt phương thức: Thực hiện logic nghiệp vụ, tuân thủ các ràng buộc đã mô tả.
  4. Áp dụng các quan hệ:
  5. Association: Tạo trường (field) chứa đối tượng hoặc danh sách.
  6. Aggregation: Thường cũng là một trường, nhưng đối tượng có vòng đời độc lập.
  7. Composition: Khi đối tượng con được tạo trong constructor và không có setter để thay đổi.
  8. Inheritance: Sử dụng extends (Java) hoặc : (C++).
  9. Dependency: Tham chiếu qua tham số phương thức hoặc dùng DI (Dependency Injection).

6.1. Ví dụ Java cho lớp Order và OrderItem

public class Order { private String orderId; private LocalDate orderDate; private OrderStatus status; private List<OrderItem> items = new ArrayList<>(); public double calculateTotal() { return items.stream() .mapToDouble(OrderItem::getSubTotal) .sum(); } public void addItem(Product product, int quantity) { OrderItem item = new OrderItem(product, quantity); items.add(item); } // getters, setters, cancelOrder, ...
}
public class OrderItem { private final Product product; private int quantity; private final double unitPrice; public OrderItem(Product product, int quantity) { this.product = product; this.quantity = quantity; this.unitPrice = product.getPrice(); } public double getSubTotal() { return unitPrice quantity; } // getters, setters ...
}

7. Kiểm tra và bảo trì sơ đồ lớp

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

7.1. Kiểm tra tính đúng đắn (Validation)

  • Sử dụng công cụ UML: Hầu hết các công cụ UML có tính năng kiểm tra (validation) để phát hiện lỗi như vòng lặp không hợp lệ, tên trùng lặp.
  • Review đồng đội: Đưa sơ đồ lên nhóm để nhận phản hồi, đặc biệt là các developer và kiến trúc sư.

7.2. Cập nhật khi thay đổi yêu cầu

Khi yêu cầu thay đổi, hãy:

  1. Xác định ảnh hưởng: Lớp nào, quan hệ nào bị ảnh hưởng.
  2. Cập nhật sơ đồ: Thêm, sửa hoặc xóa lớp, thuộc tính, phương thức.
  3. Kiểm tra lại toàn bộ: Đảm bảo không có lỗi mới xuất hiện.

7.3. Tài liệu hoá

  • Ghi chú (notes) trên sơ đồ để giải thích các quyết định thiết kế.
  • Lưu trữ phiên bản: Sử dụng Git hoặc hệ thống quản lý tài liệu để lưu trữ các phiên bản sơ đồ, giúp theo dõi lịch sử thay đổi.

8. Những lỗi thường gặp khi vẽ sơ đồ lớp và cách tránh

Lỗi Mô tả Cách khắc phục
Lớp “God Object” Một lớp chứa quá nhiều thuộc tính và phương thức, làm mất tính modular. Áp dụng Single Responsibility, tách ra các lớp phụ.
Quan hệ vòng lặp không cần thiết A → B → A mà không có lý do nghiệp vụ. Xem xét lại thiết kế, dùng interface hoặc event để giảm phụ thuộc.
Multiplicity không hợp lý Đặt 0.. cho quan hệ mà thực tế luôn có 1. Kiểm tra yêu cầu, đặt đúng giá trị (1, 0..1, …).
Thiếu ràng buộc duy nhất Không chỉ định {unique} cho các trường như email. Thêm ràng buộc vào sơ đồ và cơ sở dữ liệu.
Kế thừa quá sâu Nhiều lớp con kế thừa nhau quá nhiều cấp, gây khó bảo trì. Sử dụng composition thay vì inheritance khi có thể.
Không mô tả kiểu dữ liệu Thuộc tính chỉ có tên, không có kiểu. Luôn kèm kiểu dữ liệu (int, String, List<…>).
Nhầm lẫn Aggregation vs Composition Dùng aggregation cho mối quan hệ “sở hữu” mạnh. Xác định vòng đời của đối tượng con để chọn đúng.

9. Tổng kết

Việc vẽ sơ đồ lớp không chỉ là một hoạt động kỹ thuật mà còn là một nghệ thuật truyền đạt kiến trúc phần mềm một cách rõ ràng, logic và dễ hiểu. Khi thực hiện đúng các bước:

  1. Thu thập yêu cầu và phân tích nghiệp vụ.
  2. Xác định lớp, thuộc tính, phương thức.
  3. Xây dựng quan hệ (association, aggregation, composition, inheritance, dependency).
  4. Kiểm tra tính nhất quán, tuân thủ các nguyên tắc thiết kế (SOLID, GRASP).
  5. Cập nhật và bảo trì khi yêu cầu thay đổi.

Bạn sẽ có được một bản thiết kế vững chắc, giảm thiểu rủi ro trong quá trình lập trình và bảo trì phần mềm. Hãy luôn nhớ rằng sơ đồ lớp là “bản đồ” cho cả đội ngũ phát triển, vì vậy sự đơn giản, chính xác và dễ đọc luôn là tiêu chí hàng đầu.

Nếu bạn mới bắt đầu, hãy thực hành với các dự án nhỏ, dần dần mở rộng độ phức tạp lên các hệ thống doanh nghiệp lớn. Khi đã thành thạo, bạn sẽ thấy việc vẽ sơ đồ lớp không chỉ giúp giảm thời gian phát triển mà còn nâng cao chất lượng, khả năng mở rộng và bảo trì của phần mềm. Chúc bạn thành công trên hành trình thiết kế phần mềm!

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 *