

Một số mẫu thiết kế phổ biến
- 20-06-2025
- Toanngo92
- 0 Comments
Mục lục
Nhóm Mẫu Thiết Kế Cấu Trúc (Structural Patterns)
🎯 1.1. Mục tiêu của Structural Patterns
Mẫu thiết kế cấu trúc tập trung vào tổ chức các lớp và object để tạo thành kiến trúc hệ thống dễ hiểu, linh hoạt và dễ mở rộng.
Structural patterns giúp bạn:
- Kết nối các phần tử hệ thống mà không làm rối rắm logic
- Ẩn đi chi tiết cấu trúc
- Tối ưu giao tiếp giữa module và object
🧩 1.2. Hai nguyên lý cốt lõi của Structural Patterns
- Cô lập phần có thể thay đổi trong class
→ Giúp dễ bảo trì và nâng cấp - Tách riêng phần thay đổi đó thành một class riêng
→ Dễ tái sử dụng và kết hợp linh hoạt
Nếu trong class bạn có một đoạn code thay đổi thường xuyên (ví dụ như định dạng hiển thị, xử lý dữ liệu…), thì đó là ứng cử viên tốt để tách ra.
🧱 1.3. Khi nào nên dùng Structural Patterns?
Dấu hiệu | Lý do |
---|---|
Lớp chứa nhiều trách nhiệm (God class) | Tách thành nhiều class nhỏ giúp dễ hiểu |
Giao tiếp giữa nhiều module khác nhau | Dùng adapter hoặc façade |
Cần đơn giản hóa giao diện cho hệ thống phức tạp | Giúp frontend/backend, API dễ tương tác |
Muốn tái sử dụng dữ liệu hoặc object | Dùng Flyweight để tiết kiệm tài nguyên |
📚 1.4. Các mẫu chính thuộc nhóm Structural
Tên mẫu thiết kế | Mục tiêu |
---|---|
MVC | Phân chia rõ Model – View – Controller |
Façade | Cung cấp giao diện đơn giản cho hệ thống phức tạp |
Adapter | Làm cho các class không tương thích làm việc với nhau |
Decorator | Thêm chức năng cho object một cách linh hoạt |
Composite | Làm việc với cấu trúc cây (file, menu…) dễ dàng |
Flyweight | Tối ưu hóa bộ nhớ bằng cách dùng lại object giống nhau |
Bridge, Proxy | Giảm phụ thuộc hoặc kiểm soát giao tiếp object |
🧠 1.5. Lợi ích tổng thể của Structural Patterns
Lợi ích | Giải thích |
---|---|
Giảm phụ thuộc (loose coupling) | Dễ bảo trì và thay đổi |
Tăng tính tái sử dụng | Mỗi class có vai trò riêng biệt |
Tăng khả năng kiểm soát logic | Giao tiếp được định nghĩa rõ ràng |
Tối ưu hóa hiệu suất hệ thống | Như Flyweight làm giảm bộ nhớ |
✅ 1.6. Tư duy áp dụng
Đừng gộp tất cả xử lý vào một lớp – hãy nghĩ xem phần nào là “kiến trúc”, phần nào là “hành vi”, phần nào là “hiển thị”, phần nào là “tài nguyên dùng lại”.
→ Structural patterns là công cụ giúp bạn chia để trị và kết nối hiệu quả.
Model–View–Controller (MVC) – Kiến trúc phân tách rõ ràng giữa dữ liệu, hiển thị và điều khiển
🎯 2.1. Vấn đề: Giao diện và xử lý nghiệp vụ bị trộn lẫn
Trong nhiều ứng dụng, người lập trình thường viết toàn bộ logic xử lý và giao diện trong cùng một class. Ví dụ:
javaCopyEditbutton.onClick(() -> {
int result = model.calculate(x, y);
label.setText("Kết quả: " + result);
});
→ ❌ Gây khó bảo trì,
→ ❌ Giao diện gắn chặt với nghiệp vụ
→ ❌ Không thể tái sử dụng hoặc test riêng từng phần
✅ 2.2. Giải pháp: Mẫu thiết kế MVC
MVC là một kiến trúc tách hệ thống thành 3 phần riêng biệt:
- Model – Dữ liệu và logic nghiệp vụ
- View – Hiển thị giao diện
- Controller – Xử lý sự kiện, tương tác người dùng
🧱 2.3. Cấu trúc MVC
Thành phần | Vai trò chính |
---|---|
Model | Quản lý dữ liệu và xử lý logic (CSDL, xử lý nghiệp vụ) |
View | Giao diện người dùng, chỉ hiển thị trạng thái từ Model |
Controller | Nhận sự kiện từ người dùng → ra lệnh thay đổi Model |
🧠 2.4. Phân tích chi tiết từng phần
🧮 Model
- Là trung tâm chứa dữ liệu và logic
- Không quan tâm giao diện là web hay mobile
- Có thể bao gồm nhiều class (User, Order, Cart…)
👁 View
- Giao diện thể hiện trạng thái Model
- Không được thay đổi dữ liệu trực tiếp
- Chỉ hiển thị dữ liệu đã xử lý (đã được định dạng, sắp xếp…)
🎮 Controller
- Điều khiển luồng giữa người dùng và hệ thống
- Nhận input từ View
- Gọi đúng hàm trong Model
- Cập nhật lại View nếu cần
🧾 2.5. Sơ đồ dòng dữ liệu
cssCopyEdit[User Action] → Controller → Model (xử lý) → View (hiển thị lại)
→ Từng phần chỉ tương tác với phần liên quan, không vượt quyền.
📚 2.6. Ví dụ: Hệ thống tính toán đơn giản
- Người dùng nhập số A, B → nhấn “Tính”
- Controller gọi
Model.sum(a, b)
- Model trả kết quả
- View hiển thị kết quả
→ Không ai làm việc thay ai. Mỗi phần chuyên trách một việc.
✅ 2.7. Lợi ích của MVC
Lợi ích | Giải thích |
---|---|
Tách rõ xử lý và giao diện | View không biết nội dung xử lý |
Dễ bảo trì | Chỉ sửa 1 phần khi cần |
Dễ test từng module | Test Controller độc lập với View |
Hỗ trợ đa nền tảng | Có thể thay View (Web, App…) giữ nguyên Model |
⚠️ 2.8. Lưu ý khi áp dụng
Vấn đề thường gặp | Cách xử lý |
---|---|
Gộp View + Controller làm 1 | Chỉ phù hợp với app nhỏ |
Model bị View truy cập trực tiếp | Dùng Observer, event để cập nhật |
Tên gọi nhưng chưa thật sự tách vai trò | Đảm bảo đúng nhiệm vụ từng phần |
📘 2.9. Mở rộng nâng cao: MVVM / MVP
Mẫu | Thay đổi gì? |
---|---|
MVVM | ViewModel thay Controller, hỗ trợ binding 2 chiều |
MVP | Presenter đóng vai trò trung gian mạnh hơn giữa View và Model |
→ Tuy nhiên, nguyên lý lõi của tách biệt giữa dữ liệu – giao diện – điều khiển vẫn giữ nguyên.
💬 Ghi nhớ chương này:
- MVC là nền tảng cho kiến trúc phần mềm hiện đại
- Rất phổ biến trong:
- Java Swing
- .NET WinForms
- Web framework (Spring MVC, Laravel MVC)
- iOS, Android
🎯 Khi phần mềm của bạn đang “rối như canh hẹ” vì gộp đủ thứ vào cùng một class – hãy bắt đầu bằng tách View, Controller và Model.
Flyweight Pattern
🎯 5.1. Vấn đề: Quá nhiều object giống nhau gây lãng phí tài nguyên
Hãy tưởng tượng bạn xây dựng:
- Trình xử lý văn bản (mỗi ký tự là 1 object)
- Game có hàng nghìn cây, lính, đạn…
- Hệ thống bản đồ hiển thị hàng nghìn điểm
→ Nếu mỗi phần tử có object riêng (dù nội dung giống nhau), bạn sẽ:
❌ Lãng phí bộ nhớ
❌ Gây giảm hiệu năng
❌ Gặp lỗi do quản lý quá nhiều object
✅ 5.2. Mục tiêu của Flyweight Pattern
Cho phép tái sử dụng các object giống nhau thay vì tạo mới từng cái → tiết kiệm bộ nhớ
Chia nhỏ object thành: phần dùng chung (intrinsic) và phần riêng biệt (extrinsic)
🧱 5.3. Cấu trúc tổng quát
Thành phần | Vai trò |
---|---|
Flyweight | Interface khai báo hành vi chung |
ConcreteFlyweight | Class dùng chung dữ liệu (vd: Font, kiểu) |
FlyweightFactory | Quản lý cache các object giống nhau |
Client | Cung cấp phần dữ liệu riêng biệt khi dùng |
✍️ 5.4. Ví dụ Java – Hiển thị chữ trong văn bản
Giả sử bạn có tài liệu với 1000 chữ A
, 800 chữ B
.
Interface:
javaCopyEditinterface Glyph {
void draw(String position);
}
Flyweight (được dùng lại):
javaCopyEditclass CharacterGlyph implements Glyph {
private char letter; // intrinsic
public CharacterGlyph(char c) {
this.letter = c;
}
public void draw(String position) {
System.out.println("Vẽ '" + letter + "' tại " + position);
}
}
Factory:
javaCopyEditclass GlyphFactory {
private static Map<Character, Glyph> cache = new HashMap<>();
public static Glyph get(char c) {
cache.putIfAbsent(c, new CharacterGlyph(c));
return cache.get(c);
}
}
Sử dụng:
javaCopyEditGlyph a1 = GlyphFactory.get('A');
Glyph a2 = GlyphFactory.get('A'); // dùng lại a1
a1.draw("0,0");
a2.draw("1,0");
✅ 5.5. Lợi ích của Flyweight Pattern
Lợi ích | Giải thích |
---|---|
Tiết kiệm RAM đáng kể | Chỉ lưu object một lần |
Dễ quản lý object tái sử dụng | Factory kiểm soát |
Tăng hiệu năng | Ít garbage collection hơn |
Phù hợp với dữ liệu khổng lồ | Giảm chi phí tạo object |
⚠️ 5.6. Hạn chế và lưu ý
Hạn chế | Giải pháp |
---|---|
Phải phân biệt rõ dữ liệu dùng chung và dữ liệu thay đổi | Tách rõ intrinsic / extrinsic |
Gây phức tạp thêm khi không cần thiết | Dùng khi thực sự có lợi |
Phải dùng factory để tránh tạo object trùng | Tự quản lý cache cẩn thận |
📚 5.7. Ứng dụng thực tế
Tình huống | Flyweight giúp gì? |
---|---|
Game có nhiều NPC giống nhau | Tái sử dụng hình ảnh, animation |
Text Editor / IDE | Một object Font dùng chung cho hàng nghìn chữ |
Hệ thống bản đồ | Các tile hoặc marker lặp lại |
TreeView, biểu đồ cây lớn | Dùng lại node cấu trúc |
🔁 5.8. So sánh với Prototype / Singleton
Pattern | Khác biệt chính |
---|---|
Flyweight | Dùng chung object giống nhau, chia nhỏ phần dữ liệu |
Prototype | Tạo object mới bằng cách clone object có sẵn |
Singleton | Một object toàn cục duy nhất dùng chung toàn hệ thống |
💬 Ghi nhớ:
- Flyweight không phải để chia sẻ mọi thứ, mà là chia sẻ phần giống nhau
- Rất mạnh khi:
- Bạn có hàng nghìn object cùng kiểu
- Mỗi object chỉ khác 1 chút (position, id…)
- Cần tối ưu bộ nhớ và hiệu suất
🧵 Nếu bạn thấy mình tạo 10.000 object giống nhau → hãy dùng Flyweight để “may một lần, mặc nhiều lần”.