Architecture

Thực hành đo lường các chỉ số (metric) của software với NDepend

Ngày nay, có rất nhiều vấn đề chúng ta cần suy nghĩ đến khi xây dựng ứng dụng thực tế. MAINTAINABILITY (tính dễ bảo trì), UNDERSTANDABILITY (tính dễ hiểu), DEPENDENCY (tính phụ thuộc giữa các module), COHESION (tính gắn kết) chỉ là 1 vài trong số chúng. Chúng ta phải làm việc cật lực để duy trì chất lượng (quality) của ứng dụng, làm cho mã nguồn có khả năng tự diễn đạt ý nghĩa của chúng (self-comment), tránh các lỗi phụ thuộc vòng tròn (CYCLIC DEPENDENCIES) giữa các module. Có rất nhiều tool có thể giúp chúng ta đạt được các mục tiêu đó ví dụ như CheckStyle, FindBug, JTest, Sonar, Resharper, JustCode, NDepend, JDepend. Trong bài viết này, mình sẽ lấy ví dụ với NDepend, một công cụ giúp đo lường chất lượng mã nguồn trong .NET, bài viết được tham khảo từ Future Processing, nhằm mục đích thực hành và hiểu các thông số (metric) của 1 ứng dụng đã đề cập trong bài viết trước đó “Đo lường kết cấu của kiến trúc“. Mã nguồn ví dụ được dùng trong bài viết này là một open-source project có tên GALLIO, một TEST AUTOMATION PLATFORM và MBUNIT và UNIT TESTING FRAMEWORK. Bạn có thể down load nó ở đây để cùng thực hành với mình.

NDepend là gì?

NDepend là một công cụ phân tích tĩnh (static analysis) cho các dự án .NET. Nó hỗ trợ một số lượng lớn các chỉ số (metric) và có thể hình dung các phụ thuộc bằng cách sử dụng đồ thị trực tiếp và ma trận phụ thuộc. NDepend cũng có thể thực hiện codebase snapshots để so sánh về sau. Một trong những tính năng chính của NDepend là người dùng có thể viết các quy tắc của riêng mình bằng các truy vấn LINQ (CQLinq). Ngoài ra còn có rất nhiều quy tắc mã CQLinq được xác định trước. NDepend có thể dễ dàng tích hợp với Visual Studio. Hơn thế nữa, với NDepend API và Power Tools, mọi người đều có thể tự viết những chương trình phân tích tĩnh (static analyser) hoặc tinh chỉnh các nguồn mở Power Tools hiện tại.

Phân tích mã nguồn của Gallio

Để phân tích một dự án trong NDepend, chúng ta phải tạo mới một project phân tích, và thêm vào tệp solution cũng như tất cả các assemblies cần được phân tích. Sau khi hoàn thành phân tích, bạn có thể tìm thấy bản tóm tắt các số liệu quan trọng trên bảng điều khiển (dashboard).

BASIC CODE METRICS ON DASHBOARD

Trên Dashboard (Hình 1) chúng ta có thể tìm thấy tóm tắt liên quan đến các chỉ số cơ bản cho dự án được phân tích:

LINES OF CODE

Gallio có 72121 Dòng Mã (LOC) và chỉ có 4609 LOC được tự động generated. Để so sánh, các dự án NUnit bao gồm 33460 LOC, vì vậy, chúng ta có thể nói Gallio có kích thước lớn hơn NUnit.

SỐ ƯỢNG TYPES, NAMESPACES, METHODS, SOURCE FILES VÀ ASSEMBLIES

Gallio chứa 5124 TYPES, 89 ASSEMBLIES, 409 NAMESPACES, 22185 METHODS, 4261 FIELDS và 2156 SOURCE FILES. Một số lượng lớn các method trong một dự án là một chút đáng báo động vì chỉ có 5124 kiểu dữ liệu và chúng ta nên kiểm soát sự cân bằng giữa chúng.

THIRD-PART USAGE (ASSEMBLIES, NAMESPACES, TYPES, METHODS AND FIELDS)

Gallio sử dụng 47 ASSEMBLIES, 135 NAMESPACES, 1118 TYPES, 3044 METHODS và 204 FIELDS từ các thư viện thứ ba. Tất cả những số liệu thống kê này là một phần của thông tin cho biết mức độ mà một dự án phụ thuộc vào thư viện bên ngoài.

CODE COVERAGE BY TESTS

Chúng ta không nhận được bất kỳ kết quả nào về Code coverage vì không có bất bài test nào cho dự án đó.

CODE COVERAGE BY COMMENTS

Một số assemblies trong Gallio được viết bằng một ngôn ngữ khác với C #. Trong trường hợp đó, NDepend không thể đo được tỷ lệ comment.

METHOD COMPLEXITY (IL)

Độ phức tạp trung bình của các method đối với Gallio là 1,93 nhỏ hơn giá trị được gợi ý, nghĩa là 2. Độ phức tạp (Cyclomatic complexity – CC) cao nhất là 88 và kết quả cao cho thấy rằng method đó nên được xem xét và refactored nếu có thể.

STATISTICS ON THE VIOLATED CODE RULES AND NUMBER OF VIOLATIONS FOUND IN A PROJECT

Khi nhìn vào trường Code Rules, chúng ta có thể thấy rằng có khoảng 31700 vi phạm đối với 92 quy tắc và 1207 vi phạm nghiêm trọng (critical violations) đối với 13 quy tắc. Những con số này là một chút đáng báo động và do đó chúng ta nên xem xét kỹ hơn những vấn đề này.

1-1024x552
Hình 1

Các biểu đồ trình bày bằng đồ hoạ ở trên cho thấy các chỉ số nói trên cao, trong đó chúng ta có thể thấy xu hướng và thay đổi các chỉ số theo thời gian. Điều đó có thể hữu ích vì chúng ta sẽ thấy liệu quá trình phát triển hoặc các hành động tái cấu trúc có đi đúng hướng hay không.

CQLINQ QUERIES AND PREDEFINED CODE RULES

Trước khi chúng ta xem xét các kết quả mà chúng ta nhận được cho Gallio, tôi muốn nói thêm một điều về định nghĩa các vi phạm trong NDepend. CQLinq là một ngôn ngữ truy vấn cho phép tìm bất kỳ vi phạm nào trong mã mà người dùng có thể tưởng tượng. CQLinq, như tên cho thấy, dựa trên LINQ và một truy vấn đơn giản như thể hiện trong hình 2.

2
Hình 2

Truy vấn này quét toàn bộ dự án và liệt kê các “Potentially dead fields” – không được sử dụng bởi bất kỳ method hoặc class khác. Quy tắc Potentially dead fields được đánh dấu là một điểm quan trọng vì kết quả hiển thị mã không sử dụng trong một dự án nên được xóa hoặc thay đổi.

Có hai quy tắc được xác định trước trong NDepend và chúng có thể được tìm thấy trong tab “Query and Rules Explorer” (Hình 3). Tất cả các vi phạm được nhóm và dán nhãn về mức độ nghiêm trọng.

3
Hình 3

Từ các quy tắc được xác định trước, những điều quan trọng nhất có thể được tìm thấy trong các nhóm sau:

  • Code Quality
  • Object Oriented Design
  • Architecture and Layering
  • Dead Code
  • Visibility
  • Purity – Immutability – Side Effects
  • Name Conventions

Có thể tìm thấy bản tóm tắt về vi phạm trong Gallio trên Bảng điều khiển (Hình 1). Như tôi đã đề cập, đã có khoảng 31700 vi phạm đối với 92 quy tắc và 1207 vi phạm nghiêm trọng đối với 13 quy tắc được tìm thấy tại Gallio. Trong bước đầu tiên, chúng ta nên xem xét các vi phạm quan trọng.

CRITICAL VIOLATIONS IN GALLIO

Khi chúng ta xem xét các vi phạm mà chúng ta nhận được đối với mỗi nhóm, có thể thấy rằng có rất nhiều đoạn mã nguy hiểm tiềm ẩn. Có 819 method và 143 type không được sử dụng (Dead Code), do đó chúng ta phải xem xét kỹ hơn những con số cao này. Khi phân tích các vi phạm khác, con số trong các nhóm khác không cao như Dead Code. Trong nhóm Architecture and Layering, chúng ta có thể tìm thấy Tránh không gian truy vấn phụ thuộc lẫn nhau với 41 lỗi và Avoid namespaces dependency cycles với 8. Chúng ta nên tránh tình huống khi hai namespaces phụ thuộc lẫn nhau hoặc thậm chí có phụ thuộc vòng. Theo nhiều nguồn, các namespaces phụ thuộc lẫn nhau dẫn đến cái được gọi là mã spaghetti và cho biết các class đã được nhóm lại không tổ chức chúng một cách nghiêm ngặt từ trên xuống dưới. Cách hay để phân tích những vấn đề đó là sử dụng Dependency Graph.

DEPENDENCY GRAPH

Trong Dependency Graph, chúng ta có thể thấy các namespaces hoặc các assemblies được biểu diễn bởi các hình chữ nhật và các kết nối giữa chúng. Kích thước của hình chữ nhật có thể được đặt theo kích thước LOC của namespaces, hoặc assemblies, sự phức tạp của chúng hoặc nhiều số liệu khác. Độ dày của các mũi tên có thể được đặt liên quan đến số fields, methods, types and namespaces. Chúng ta có thể dễ dàng phân tích phụ thuộc giữa các namespaces và assemblies cũng như để tìm phụ thuộc lẫn nhau và các cyclic dependency nếu có.
Trong Hình 3, có hai cặp namespaces của Gallio phụ thuộc lẫn nhau (các hình chữ nhật được kết nối bằng mũi tên song phương). Tuy nhiên, đối với một số namespaces phụ thuộc lẫn nhau đã được tìm thấy trong Gallio, Dependency Graph sẽ hơi khó nhìn. Trong trường hợp đó tốt hơn là sử dụng Dependency Matrix.

4
Hình 4

Trong Dependency Graph, khi chúng ta chỉ chọn các application assemblies, chúng ta sẽ nhận được một thông tin ngắn về tất cả các số liệu đã được tính toán:

  • lines of code
  • IL instructions
  • lines of comments
  • number of methods, fields, types and namespaces
  • rational cohesion
  • afferent coupling
  • efferent coupling

Khi chúng ta xem xét các kết quả cho từng số liệu và so sánh chúng với các giá trị được đề xuất từ tài liệu, chúng ta có thể tìm ra assembly nào có thể là vấn đề và chúng ta nên xem xét thêm ở đâu.

DEPENDENCY MATRIX

Trong Dependency Matrix, chúng ta có thể quan sát các phụ thuộc giữa các namespaces hoặc assemblies được biểu diễn bằng các dòng và cột. Ngoài ra, số trong các ô phản ánh số lượng phụ thuộc giữa chúng với các đặc tính đã chọn (types, methods, fields). Lý tưởng hơn, nếu các ô màu xanh trong ma trận nằm dưới đường chéo và màu xanh lá cây trên đường chéo. Các namespaces giữa một dependency cycle đã được tìm thấy được đánh dấu bằng một hình vuông có đường viền màu đỏ trong Dependency Matrix (Hình 5). Hơn nữa, trong trường hợp này chúng ta quan sát thấy các ô xanh và xanh nằm trong các đường biên trên và dưới đường chéo của ma trận.

6
Hình 5

Đối với một số dependency cycle, chúng ta có thể quan sát thấy rằng trong Dependency Matrix tất cả các ô trong hình vuông có màu đỏ là màu đen. Điều đó chỉ xảy ra khi phụ thuộc trực tiếp và gián tiếp giữa các namespaces.

6a
Hình 6

SRP (SINGLE RESPONSIBILITY PRINCIPLE)

Theo SRP, class không nên có nhiều lý do để thay đổi. Khi chúng ta đi đến một mức độ thấp hơn, chúng ta có thể nói liệu một phần tử mã sử dụng hàng chục các yếu tố khác (ở cùng cấp độ) như thể đó là trường hợp, nó có quá nhiều trách nhiệm. Chúng ta có thể quan sát phần tử mã này trong Dependency Matrix nếu nó chứa nhiều ô màu xanh trong một cột và nhiều ô xanh trong một đường thẳng. Khi chúng ta nhìn vào các kết quả cho Gallio (Hình 7) chúng ta không thể thấy được tình huống đó, vì vậy chúng ta có thể chắc chắn rằng SRP trong dự án đó không bị hỏng.

7
Hình 7

COHERENT ASSEMBLIES

Quy tắc thứ hai nói rằng các assemblies trong một dự án nên chặt chẽ (coherent). Component nên thực hiện một chức năng hợp lý duy nhất hoặc một logical entity duy nhất và tất cả các phần cần đóng góp cho việc thực hiện. Low cohesion có nghĩa là component này thực hiện rất nhiều hành động và không tập trung vào những gì nó nên làm. Ngược lại với điều đó là high cohesion, có nghĩa là thành phần này tập trung nhiều vào việc cần làm và tất cả các lớp trong thành phần đó có nhiều điểm chung. Trong khi nói về high cohesion, bạn nên nói đến coupling. Nó đề cập đến mối quan hệ giữa hai component và sự phụ thuộc lẫn nhau giữa chúng. Low coupling có nghĩa là thay đổi một cái gì đó trong một component không nên ảnh hưởng đến component khác; trong khi high coupling có nghĩa là sẽ khó khăn trong việc thay đổi mã bởi vì nó có thể có nghĩa là một toàn bộ hệ thống bị ảnh hưởng. Do đó, phần mềm với thiết kế tốt sẽ có high cohesion và low coupling.

Mức độ liên kết (cohesion) của các assemblies có thể được quan sát thấy trong Dependency Matrix. Các assemblies có độ gắn kết cao (high cohesion) khi các ô màu xanh lá cây và xanh được nhóm thành các ô vuông. Khi chúng ta nhìn vào các kết quả cho Gallio (Hình 7) chúng ta không thể thấy bất kỳ tế bào xanh lá và xanh dương nào được nhóm lại trong một hình vuông xung quanh đường chéo, có nghĩa là các assemblies trong Gallio có độ kết dính thấp (low cohesion). Kiến trúc và thiết kế các thành phần nên được xem xét vì trong tương lai có thể khó hiểu mục đích của một số component và duy trì mã. Để so sánh, khi chúng ta nhìn vào các kết quả mà chúng ta nhận được cho dự án NUnit (Hình 8), chúng ta có thể thấy rằng các assemblies có mức gắn kết cao hơn.

8
Hình 8

ABSTRACTNESS VS. INSTABILITY DIAGRAM

Mỗi assembly có các số liệu tính toán riêng của mình: Abstractness, Instability và Distance from main sequence. Những số liệu này được hình dung trong sơ đồ “Abstractness vs. Instability”. Nó giúp phát hiện những bộ phận nào có thể gây “đau thương” để duy trì (concrete and stable) và những bộ phận nào trong số chúng có tiềm năng vô dụng (abstract and instable). Khi chúng ta nhìn vào các cụm từ Gallio (Hình 9), chúng ta có thể thấy phần lớn chúng nằm trong khu vực “xanh lá”, chỉ có một assembly nằm trong vùng vô ích và ba nằm trong vùng đau thương. Một điều có thể là đáng báo động là hầu hết các assembly đều có hệ số bất ổn định (I) khoảng 1, có nghĩa là chúng có giá trị gia tăng của mối nối cực đại. Do đó, phần lớn các assembly phụ thuộc vào các assembly khác và trong tương lai chúng ta có thể gặp phải các vấn đề với việc thay đổi các assembly như một thay đổi sẽ buộc các assembly khác còn lại thay đổi.

9
Hình 9

Kết luận

Để tổng hợp các kết quả phân tích trong NDepend, nói chung có một vài điều trong mã mà chúng ta nên xem xét kỹ hơn. Các chu kỳ phụ thuộc giữa các assemblies đã được tìm thấy và một vài trong số chúng phụ thuộc lẫn nhau. Nó có nghĩa là một số thay đổi trong kiến ​​trúc dự án nên được thực hiện để tránh những phụ thuộc. Thứ hai, có vẻ như có rất nhiều mã không sử dụng trong Gallio và MbUnit unit testing framework. Thống kê vi phạm cho thấy 819 method không sử dụng, đó là một con số cao. Do đó, mã nên được xem xét kỹ lưỡng và tất cả các method cũ, không sử dụng, kiểu dữ liệu (type) và các field nên được xóa. Nó sẽ làm giảm các dòng mã và làm cho nó rõ ràng hơn. Một điều khác cần được tính đến là sự gắn kết thấp của các assemblies trong dự án. Nó chỉ ra rằng họ có thể có nhiều hơn một chức năng hợp lý duy nhất và nó có thể là khó hiểu được mục đích của một số assemblies. Các component nên được xem xét lại và phải đặt biên giới (boundary) cho các chức năng, giúp tăng sự gắn kết. Điều cuối cùng cần được đề cập là thực tế là các unit tests nên được tạo ra cho dự án đó vì chúng giúp tìm ra các khiếm khuyết ở giai đoạn đầu của quá trình phát triển.

Đây là bài viết trong loạt bài viết về “Tổng quan về sự phát triển của kiến trúc phần mềm“. Đây là loạt bài viết chủ yếu giới thiệu về một số mô hình kiến trúc phần mềm hay nói đúng hơn là sự phát triển của chúng qua từng giai đoạn, qua đó giúp chúng ta có cái nhìn tổng quát, up-to-date và là roadmap để bắt đầu hành trình chinh phục (đào sâu) thế giới của những bản thiết kế với vai trò là những kỹ sư và kiến trúc sư phần mềm đam mê với nghề.

Bài viết được tham khảo từ:

https://www.future-processing.pl/blog/analysing-the-quality-of-code-with-ndepend/

 

Tổng hợp bởi edwardthienhoang

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.