Các nguyên tắc trong kiến trúc phần mềm

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ả.

Các nguyên tắc

  • Coupling: đề cập đến vấn đề phụ thuộc lẫn nhau giữa các component. Loose coupling có nghĩa là các component ít phụ thuộc vào nhau, sự thay đổi trong component này ít khi, hoặc không ảnh hưởng đến component kia. Loose coupling là mục tiêu chúng ta cần hướng đến để đảm bảo cho hệ thống ít bị ảnh hưởng khi có thay đổi và do đó, tăng tốc độ thực hiện công việc và bảo trì. Giải pháp: interface trong java

  • Cohesion: Khi nói đến cohesion chúng ta nghĩ đến nhiệm vụ của từng module. Nhiệm vụ của từng module càng rõ ràng và tách biệt thì cohesion càng cao (high cohesion), và đó là mục tiêu cần đạt tới khi thiết kế. High cohesion thường đạt được nếu ta tuân thủ theo nguyên tắc đơn nhiệm (Single responsibility principle), mỗi module, khi đó chỉ đảm nhiệm một nhiệm vụ duy nhất, không hơn không kém, và không có chuyện 2 module cùng làm một nhiệm vụ, một tính năng.

  • 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 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) - Nguyên tắc Tái sử dụng Tương đương Phát hành: Những tập tin có khả năng tái sử dụng sẽ ngưng kết với nhau thành component. Chúng phải được release cùng nhau, thông qua một bản phát hành được đánh phiên bản, (và gắn với một bộ tài liệu, nếu có). Chính bản phát hành sẽ đại diện cho cái gì đang được cung cấp để tái sử dụng, bằng cách đó cả tác giả lẫn người dùng của component đều đạt được tiếng nói chung.

  • Common Closure Principle (CCP) - Nguyên tắc Co rút Tương đồng: Một component không được có nhiều nguyên nhân để dẫn đến thay đổi. Trên phạm vi một hệ thống phần mềm, khả năng bảo trì quan trọng hơn nhiều so với khả năng tái sử dụng. Nếu phần mềm buộc phải thay đổi, thay đổi đó chỉ nên phải diễn ra trong một component duy nhất thay vì phân tán ra khắp hệ thống. Vậy nên những gì sẽ cùng nhau thay đổi để phục vụ cho một nhu cầu thì nên ngưng kết lại với nhau. Điều đó sẽ giúp giảm thiểu nỗ lực và rủi ro khi phát triển và phát hành component.

  • Common Reuse Principle (CRP) - Nguyên tắc Tái sử dụng Tương đồng: Mã nguồn không thường được tái sử dụng cùng nhau thì không nên kết tụ trong cùng một component. Nguyên tắc này rất giống với nguyên tắc phân tách interface: đừng để bị phụ thuộc vào những gì không dùng đến.

Các nguyên tắc Coupling (Coupling Principles)

  • Acyclic Dependencies Principle (ADP) - Nguyên tắc Phụ thuộc tuần hoàn: Biểu đồ phụ thuộc của các gói hoặc thành phần không được có chu kỳ. Một nguyên nhân quan trọng gây ra tình trạng này chính là việc phụ thuộc chéo giữa các thành phần trong hệ thống. Vấn đề sẽ trở nên phức tạp và diễn ra nhiều lần hơn nếu như một source code được chia sẻ cho một team càng lớn. Bằng cách áp dụng nguyên lý này ta có thể tránh được những tình huống như vậy. Các phụ thuộc phần mềm có thể rõ ràng hoặc ngầm định. Ví dụ về các phụ thuộc rõ ràng chẳng hạn như #include cùng một thư viện trong nhiều file liên quan trong C/C++. Ví dụ về các phụ thuộc ngầm như cấu hình các giao thức mạng.

  • Stable Dependencies Principle (SDP) - Nguyên tắc Phụ thuộc ổn định: 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). Nghĩa là, một package nhất định chỉ nên phụ thuộc vào các package ổn định hơn. Bất cứ khi nào một package thay đổi, tất cả các package phụ thuộc vào package đó phải được xác thực để đảm bảo chúng hoạt động như mong đợi sau khi thay đổi. Do đó, càng nhiều gói phụ thuộc vào package không ổn định, sự gián đoạn càng lớn bất cứ khi nào nó thay đổi.

  • Stable Abstractions Principle (SAP) - Nguyên tắc trừu tượng ổn định: Càng trừu tượng, càng ổn định. Nếu một component thực sự ổn định, có khả năng nó sẽ phục vụ nhiều mục đích hơn cho nhiều nhóm người hơn và cho các vấn đề khác nhau. Để ngăn một component trở nên quá cụ thể hoặc cứng nhắc, chúng ta nên sử dụng các lớp trừu tượng.

Kiến trúc cấp cao (High-Level Architecture)

  • Keep Configurable Data at High Levels - Giữ dữ liệu cấu hình ở cấp cao: Nếu bạn có một hằng số chẳng hạn như giá trị mặc định hoặc cấu hình đã biết và được mong đợi ở mức trừu tượng cao, đừng chôn nó trong một hàm cấp thấp. Hiển thị nó dưới dạng đối số cho hàm cấp thấp được gọi từ hàm cấp cao.

  • Don’t Be Inconsistent - Nhất quán: 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.

  • Prefer Polymorphism To If/Else or Switch/Case - Ưu tiên sử dụng Đa hình hơn If/Else hoặc Switch/Case

  • Separate Multi-Threading Code - Code đa luồng riêng biệt: Tách đa luồng khỏi phần còn lại của code. Tách chúng thành các lớp khác nhau.

  • Only one level of Abstraction per layer - Chỉ một mức Trừu tượng cho mỗi lớp: Mọi phương thức bên trong cơ sở mã của bạn sẽ xử lý các khái niệm liên quan đến chỉ một mức độ trừu tượng. Không pha trộ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 tích hợp liên tục.

  • Overridden Logs: Không ghi đè các cảnh báo, lỗi, xử lý ngoại lệ (exception handling)

Last updated