Có kiến trúc phần mềm sạch và tuân thủ các nguyên tắc thiết kế được xác định trước từ khi bắt đầu dự án là một trong những cách tốt nhất để tránh nợ kỹ thuật có thể xảy ra trong tương lai của hệ thống phần mềm đó. Thiết kế phần mềm sạch là điểm mấu chốt cho một sản phẩm phần mềm hiệu quả.
Hãy cùng xem xét một số nguyên tắc, quy tắc, hướng dẫn quan trọng đảm bảo thiết kế phần mềm sạch:
Các nguyên tắc
- Loose Coupling: nếu các lớp (classes) phu thuộc vào nhau, chúng sẽ ảnh hưởng nhau. Càng ít các lớp phu thuộc (Low coupling hay loose coupling), thì việc thay đổi chúng càng dễ dàng.
- High Cohesion: mức độ mà các element của một tổng thể thuộc về nhau. Cohesion ngược lại so với Coupling, các components của classes nên có tính gắn kết cao.
- Locality: Các thay đổi, bảo trì, tiện ích mở rộng chỉ mang tính cục bộ. Điều này dẫn đến không gây hại cho toàn bộ môi trường.
- Removeable: Các component phần mềm phải có thể dễ dàng tháo gỡ.
- Small Components: lý tưởng nhất là hệ thống phần mềm chỉ gồm các thành phần nhỏ (small component), mỗi thành phần chỉ làm một nhiệm vụ.
Thiết kế lớp (Class design)
- Nguyên tắc trách nhiệm duy nhất – Single Responsibility Principle (SRP): mỗi lớp chỉ nên làm một nhiệm vụ.
- Nguyên tắc đóng mở – Open Closed Principle (OCP): Chỉ nên mở rộng các lớp, không nên sửa đổi các lớp
- Nguyên tắc thay thế Liskov – Liskov Liskov Substitution Principle (LSP): các lớp con phải thay thế được các lớp cha (super class) của chúng.
- Nguyên tắc đảo ngược phụ thuộc – Dependency Inversion Principle (DIP): các component cấp cao không nên phụ thuộc vào các component cấp thấp.
- Nguyên tắc phân tách giao diện – Interface Segregation Principle (ISP): interface nên được tách nhỏ với các nhiệm vụ cụ thể: các lớp không nên triển khai các phương thức không cần thiết.
Các nguyên tắc Cohesion (Cohesion Principles)
- Release Reuse Equivalence Principle (RREP): chỉ các thành phần có thể thay thế nhau mới nên được nhóm lại với nhau.
- Common Closure Principle (CCP): các lớp thay đổi cùng nhau nên được nhóm lại với nhau.
- Common Reuse Principle (CRP): các lớp được sử dụng cùng nhau nên được nhóm lại với nhau.
Các nguyên tắc Coupling (Coupling Principles)
- Nguyên lý phụ thuộc tuấn hoàn – Acyclic Dependencies Principle (ADP): không nên có chu trình phụ thuộc
- Nguyên tắc phụ thuộc ổn định – Stable Dependencies Principle (SDP): một package hay biến động thì nên hạn chế để package khác phụ thuộc vào nó. Nói cách khác, các package nên phụ thuộc theo chiều hướng từ biến động tới ổn định (depend on direction of stability).
- Nguyên tắc trừu tượng ổn định – Stable Abstractions Principle (SAP): càng trừu tượng, càng ổn định (the more abstract, the more stable)
Kiến trúc cấp cao (High-Level Architecture)
- Giữ dữ liệu có thể cấu hình ở cấp cao – Keep Configurable Data at High Levels: hằng số hoặc dữ liệu cấu hình được nên giữ ở cấp cao.
- Nhất quán – Don’t Be Inconsistent: có một quy ước, nguyên tắc, quy tắc hoặc hướng dẫn và luôn tuân theo chúng.
- Ưu tiên sử dụng Đa hình hơn If/Else hoặc Switch/Case – Prefer Polymorphism To If/Else or Switch/Case
- Code đa luồng riêng biệt – Separate Multi-Threading Code: tách đa luồng khỏi phần còn lại của code.
- Chỉ một mức Trừu tượng cho mỗi lớp – Only one level of Abstraction per layer: hãy tuân theo các lớp trừu tượng hiện có.
- Các trường không xác định trạng thái – Fields Not Defining State: các trường chứa dữ liệu không thuộc về trạng thái của instance mà là để giữ dữ liệu tạm thời. Sử dụng các biến cục bộ hoặc trích xuất vào một lớp trừu tượng (class abstracting) hành động đã thực hiện.
- Micro Layers: tránh các lớp thiết kế không cần thiết.
- Singletons / Service Locator: Sử dụng Dependency Injection ngăn chặn sự phụ thuộc giữa các class. Thay vào đó các classes sẽ liên kết với nhau thông qua một Interface hoặc base class.
- Các lớp cơ sở tùy thuộc vào phái sinh của chúng – Base Classes Depending On Their Derivatives: Các base class nên hoạt động với bất kỳ derived class nào.
- Tính năng Envy – Feature Envy: Các phương thức của một lớp nên quan tâm đến các biến và hàm của lớp mà chúng thuộc về, chứ không phải các biến và hàm của các lớp khác. Việc sử dụng các Accessors và Mutators của một số đối tượng khác để thao tác dữ liệu của nó, là đang chiếm phạm vi của đối tượng kia.
- Unused Coupling: tránh các dependencies không sử dụng.
- Hidden Coupling: hãy đảm bảo rằng thứ tự các lệnh gọi đến các phương thức khác nhau là chính xác.
- Điều hướng chuyển tiếp (Nguyên tắc Demeter) – Transitive Navigation: viết code riêng biệt. Các lớp chỉ nên có quyền truy cập vào các dependencies trực tiếp của nó
Môi trường
- Project Build Requires Only One Step: Kiểm tra và sau đó xây dựng bằng một lệnh duy nhất..
- Executing Tests Requires Only One Step: Chạy tất cả các bài kiểm thử đơn vị (unit test) bằng một lệnh duy nhất.
- Source Control System: luôn sử dụng source control system.
- Continuous Integration: Đảm bảo tính toàn vẹn với Continuous Integration.
- Overridden Logs: Không ghi đè các cảnh báo, lỗi, xử lý ngoại lệ (exception handling)
Nguồn bài viết “Các nguyên tắc trong kiến trúc phần mềm để sử dụng hàng ngày”: Software Architecture Cheat Sheet for Daily Usage