Tôi cũng xin chân thành cảm ơn ThS Phạm Thị Kim Dung, Bộ môn Công nghệ phần mềm, Khoa Công nghệ thông tin, Trường Đại học Công nghệ, Đại học Quốc Gia Hà Nội – Người đồng hướng dẫn và chỉ
Annotaions , Aspects và UML 2.0
Annotations
Annotations là các chú thích hoặc ghi chú quan trọng giúp làm rõ, mở rộng hoặc cung cấp thêm thông tin cho dữ liệu hoặc mã nguồn, được nhiều người biết đến hiện nay Các đặc điểm chính của Annotations bao gồm khả năng tăng tính rõ ràng, nâng cao hiệu quả quản lý dữ liệu và thúc đẩy quá trình tự động hóa trong lập trình và phân tích dữ liệu Hiểu rõ các khái niệm cơ bản về Annotations sẽ giúp bạn ứng dụng chúng một cách hiệu quả trong các dự án công nghệ thông tin và phần mềm.
Annotations 1 trước tiên được hiểu là một dạng metadata Metadata là đặc tả dữ liệu cho một đối tượng, giá trị gì đó Chẳng hạn các tập tin mp3, ảnh, hoặc một bài viết có thể có metadata định dạng XML là RSS Đặc tả dữ liệu là một tập giá trị chứa những thông tin gắn gọn, cơ bản mô tả về đối tượng nào đó Chẳng hạn, với một bài hát thì metadata có thể bao gồm: tên ca sĩ trình bày, tên nhạc sĩ, bài hát này hát trong bao lâu, Metadata không phải là "siêu dữ liệu" như một số người từng dịch từ tiếng Anh sang tiếng Việt
Annotations là một kỹ thuật mới đầy triển vọng trong lĩnh vực phát triển phần mềm, mang lại nhiều lợi ích đáng kể Chúng giúp tối ưu hóa quy trình phát triển bằng cách cung cấp thông tin rõ ràng và chính xác cho mã nguồn, từ đó nâng cao hiệu quả và tránh lỗi Ngoài ra, annotations còn hỗ trợ trong việc tự động hóa kiểm thử và phân tích mã, giúp tiết kiệm thời gian và tăng độ chính xác của quá trình phát triển phần mềm Với những lợi ích này, annotations hứa hẹn sẽ trở thành công cụ không thể thiếu cho các nhà phát triển phần mềm hiện đại.
Tường minh: Nhìn vào lớp trên bạn có thể thấy rõ mục đích
Trong lập trình, tránh mắc phải nhiều lỗi trong thời gian chạy là điều quan trọng để đảm bảo ứng dụng hoạt động ổn định và hiệu quả Các lỗi thời gian chạy thường gây ra sự cố khó chịu, mất nhiều thời gian để phân tích và khắc phục so với lỗi biên dịch, vốn thường dễ phát hiện và xử lý hơn Việc kiểm soát và hạn chế lỗi trong thời gian chạy giúp lập trình viên giảm thiểu rắc rối, tiết kiệm thời gian và nâng cao chất lượng phần mềm.
Annotations giúp phát hiện lỗi cú pháp trong quá trình biên dịch, cho phép biên dịch nhanh chóng thông báo các lỗi cấu hình cho lập trình viên Khác với các phương pháp như lời chú thích, thuộc tính hoặc XML, các lỗi này không được phát hiện ngay lập tức mà chỉ xuất hiện trong thời gian chạy do dữ liệu văn bản đơn thuần Việc sử dụng annotations tối ưu hóa quá trình kiểm tra lỗi và nâng cao hiệu quả lập trình.
Ứng dụng của bạn chạy nhanh hơn nhờ việc tối ưu hóa định dạng dữ liệu, giảm thiểu thời gian và bộ nhớ cần thiết khi chuyển đổi từ XML sang các dạng đối tượng thông qua API Việc sử dụng định dạng phù hợp giúp cải thiện hiệu suất và tốc độ xử lý của ứng dụng rõ rệt.
Annotations giúp giảm bớt khó khăn trong việc tương tác với các lệnh như DOM hoặc SAX khi xử lý XML trong Java, mang lại sự tiện lợi và hiệu quả hơn trong quá trình phát triển.
Một annotations được định nghĩa như sau :
Các thành phần annotations là danh sách các biến số của nó
Annotations thường được sử dụng như các chú thích cho các thành phần trong chương trình như class, biến số, phương thức hoặc bất kỳ dòng lệnh nào, giúp làm rõ ý nghĩa và chức năng của mã nguồn Chúng được thêm vào trước thành phần mà chúng ghi chú để tăng tính rõ ràng và dễ hiểu của mã Việc sử dụng annotations đúng cách không chỉ giúp ích cho quá trình bảo trì, mở rộng mã nguồn mà còn nâng cao khả năng đọc hiểu của người phát triển.
@tên_annotations() Tên_thành_Phần
Ví dụ: sử dụng annotaions cho phương thức
@Override public String toString() { return super.toString() +"Testing annotation name:
Annotations trên định sử dụng nhằm khẳng định phương thức toString được ghi đè lên phương thức của lớp cha
Annotations đa dạng với nhiều loại khác nhau, nhưng tôi sẽ tập trung giới thiệu về Target annotations vì đây là loại annotation tôi sử dụng chính cho khóa luận của mình Target annotations đóng vai trò quan trọng trong việc xác định chính xác phạm vi và mục tiêu nghiên cứu, giúp nâng cao độ chính xác và hiệu quả của quá trình phân tích dữ liệu Việc lựa chọn đúng loại annotation phù hợp là yếu tố then chốt để đảm bảo kết quả nghiên cứu đạt được chất lượng cao, đồng thời hỗ trợ tối ưu hóa quá trình xử lý ngôn ngữ tự nhiên trong dự án của bạn.
Target annotations các chú thích kiểu này có đích tới là class Bao gồm các loại
@Target(ElementType.TYPE)— Áp dụng cho bất kỳ các yếu tố nào của class
@Target(ElementType.FIELD)—Có thể áp dụng cho các lĩnh vực và các thuộc tính
@Target(ElementType.METHOD)—Có thể áp dụng cho mức độ method
@Target(ElementType.PARAMETER)—Áp dụng cho các thông số của các methods
@Target(ElementType.CONSTRUCTOR)— Áp dụng cho constructors
@Target(ElementType.LOCAL_VARIABLE)—Áp dụng cho các biến địa phương
@Target(ElementType.ANNOTATION_TYPE)—Loại hình tuyên bố là một chú thích
@Target(ElementType.METHOD) public @interface Test_Target { public String dosomething();
Annotations được định nghĩa là ghi chú cho phương thức, trong đó thành phần chính là phương thức dosomething() Để sử dụng annotations, bạn cần thêm nó vào trước phương thức đó Ví dụ, bạn có thể chèn annotations trước phương thức để tăng cường khả năng quản lý và mở rộng mã nguồn Việc sử dụng annotations giúp cung cấp thông tin rõ ràng hơn về các phương thức trong lập trình, đồng thời nâng cao khả năng đọc hiểu và bảo trì mã nguồn.
@Test_Target(“test”) Public void test(){
Annotations dạng này chỉ có thể áp dụng cho loại đối tượng mà chúng được định nghĩa để ghi chú, như trong ví dụ trên, chúng chỉ phù hợp với phương thức Sử dụng annotations cho các lớp hoặc biến sẽ gây ra lỗi, do đó, cần đặt đúng loại đối tượng để đảm bảo tính hợp lệ và tránh lỗi trong quá trình biên dịch.
Aspect
Vấn đề cốt lõi của AOP là cho phép chúng ta thực hiện các vấn đề riêng biệt một cách linh hoạt và kết hợp chúng lại để tạo nên hệ thống hoàn chỉnh AOP bổ sung cho kỹ thuật lập trình hướng đối tượng bằng cách hỗ trợ một dạng mô-đun mới, cho phép kéo thể hiện chung của các vấn đề phức tạp vào một khối nhất quán Khối này giúp tăng tính linh hoạt và khả năng mở rộng của hệ thống, đồng thời tối ưu hóa quá trình phát triển phần mềm.
Từ "Aspect" (tạm dịch là "lát" – hàm ý lát cắt đi qua nhiều lớp đối tượng) đã trở thành tên gọi của phương pháp phát triển phần mềm mới, gọi là Aspect-oriented programming Phương pháp này giúp tách riêng các mã chức năng nhằm kiểm soát các vấn đề đan xen trong phần mềm dễ dàng hơn, nâng cao hiệu quả quản lý và bảo trì mã nguồn.
Các khía cạnh của hệ thống có thể được thay đổi, thêm hoặc xóa trong quá trình biên dịch, giúp tối ưu hóa tính linh hoạt và khả năng tái sử dụng của hệ thống Một dạng biên dịch đặc biệt gọi là Aspect Weaver đóng vai trò quan trọng trong việc kết hợp các thành phần riêng lẻ, tạo thành một hệ thống hợp nhất và có cấu trúc rõ ràng.
AOP gồm 3 bước phái triển :
Phân tích các yêu cầu để xác định vấn đề chung và vấn đề đan nhau
Xây dựng thể hiện từng vấn đề riêng biệt
Tổng hợp các thể hiện
Trình biên dịch AOP thực hiện theo hai bước :
Kết hợp các hành vi riêng lẻ
Chuyển đổi thông tin kết quả sang dạng mã thực thi
Các ưu điểm của AOP[4]:
Mô đun hoá các vấn đề đan xen giúp phân tách rõ ràng các khía cạnh phức tạp trong hệ thống AOP xác định các vấn đề một cách tách biệt, hạn chế tối đa việc nhầm lẫn mã nguồn, từ đó tăng tính rõ ràng và dễ bảo trì Phương pháp này cho phép mô đun hoá các vấn đề liên quan đến nhiều lớp đối tượng khác nhau, nâng cao tính linh hoạt và khả năng mở rộng của phần mềm.
Việc mở rộng hệ thống trở nên dễ dàng hơn nhờ khả năng thêm chức năng mới thông qua việc tạo các Aspect mới, giúp giảm thiểu lo lắng về sự đan xen phức tạp Khi tích hợp các mô-đun mới vào hệ thống, các Aspect hiện có sẽ tự động liên kết và tạo ra một quá trình phát triển hệ thống chặt chẽ, linh hoạt và dễ quản lý hơn.
Thiết kế hệ thống tốt cần cân nhắc cả yêu cầu hiện tại và tương lai để đảm bảo sự linh hoạt và dễ dàng mở rộng trong tương lai Việc xác định các yêu cầu tương lai là thách thức nhưng rất quan trọng để tránh phải thay đổi hoặc xây dựng lại nhiều phần của hệ thống về sau Với phương pháp AOP (Chương trình Hướng Đối Tượng Theo Mục Tiêu), nhà thiết kế hệ thống có thể để lại các quyết định thiết kế cho các yêu cầu trong tương lai, giúp đảm bảo sự mở rộng và linh hoạt của hệ thống trong dài hạn.
Các Aspect là những mô đun riêng biệt, được kết hợp linh hoạt, đóng vai trò quan trọng trong việc nâng cao khả năng tái sử dụng mã nguồn Sử dụng AOP giúp tối ưu quá trình tái sử dụng mã hơn so với lập trình hướng đối tượng (OOP), mang lại hiệu quả và linh hoạt cao hơn trong phát triển phần mềm.
AspectJ là một mở rộng của lập trình hướng khía cạnh (AOP) giúp việc học và phát triển ứng dụng Java dựa trên AOP trở nên dễ dàng và nhanh chóng hơn Với AspectJ, việc mô-đun hóa các vấn đề quan tâm như kiểm tra và xử lý lỗi, đồng bộ hóa, tối ưu hóa hiệu quả, theo dõi và tìm lỗi trở nên đơn giản hơn nhiều Điều này giúp các nhà phát triển tối ưu hóa quá trình xây dựng ứng dụng Java một cách hiệu quả và chuyên nghiệp.
AspectJ được cấu thành từ join-point, pointcut, advice[7]
Trong lập trình, jointpoints là các điểm định nghĩa trong chương trình, có thể là lời gọi hàm, khởi tạo lớp hoặc đối tượng Nhờ vào jointpoints, chúng ta có thể xác định các điểm cắt ngang trong hệ thống để thêm các chức năng bổ sung một cách linh hoạt Các loại chính của jointpoints bao gồm các điểm gọi hàm, các điểm khởi tạo lớp và các điểm khởi tạo đối tượng, giúp mở rộng khả năng mở rộng và quản lý mã nguồn một cách hiệu quả.
Join-point tại các hàm khởi tạo
Join-point tại các điểm truy cập thuộc tính
Join-point tại các điểm truy cập ngoại lệ
Pointcut là tập hợp các joinpont mà bạn sử dụng định nghĩa để thực thi advice
Pointcut sẽ chọn một jointpoint nào đó và ngữ cảnh tại join-point đó Cú pháp của pointcut như sau :
[acess specifier] pointcut pointcut-name([args]) : pointcut-definition;
Ví dụ : public pointcut test() : call(String Start()) || call(boolean end());
In aspect-oriented programming, advice refers to the code executed at specific join-points, with different types including 'before' advice, which runs prior to the join-point, and 'after' advice, which executes afterwards Advice allows developers to insert additional behavior at targeted points in the application, selected by pointcuts, making it a powerful tool for modularizing cross-cutting concerns.
Advice được chia làm các loại sau :
Before : Được thực hiện trước join-point
Around: Được thực hiện sau join-point
Affter : Được thực hiện sau joint-point
AspectJ 5 có nhiều điểm mở rộng hơn so với các AspectJ trước đó, một điểm nỏi bật là nó có thể sử dụng annotations trong Java 5 như một join-point Thông thường annotations trong trường hợp này được định nghĩa như một target annotations
502 Bad GatewayUnable to reach the origin service The service may be down or it may not be responding to traffic from cloudflared
502 Bad GatewayUnable to reach the origin service The service may be down or it may not be responding to traffic from cloudflared
@Tên_annotations Tên_phương_thức(danh sách đối số)
Ví dụ: pointcut test() : call(@Implement * m());
Ví dụ trên, pointcut được kích hoạt khi một phướng thức m() được ghi chú bởi annotations @Implement được gọi.
UML 2.0
502 Bad GatewayUnable to reach the origin service The service may be down or it may not be responding to traffic from cloudflared
Những mục đích chính trong việc thiết kế của UML là:
Chúng tôi cung cấp cho người dùng một tài liệu đọc dễ hiểu để sử dụng, giúp họ làm quen với ngôn ngữ mô hình trực quan Điều này giúp người dùng phát triển và trao đổi các mô hình có ý nghĩa một cách dễ dàng và hiệu quả.
Cung cấp cơ chế đặc tả và khả năng mở rộng để mở rộng các khái niệm cốt lõi
Không phụ thuộc vào ngôn ngữ lập trình cụ thể và các quy trình phát triển
Cung cấp một cơ sở chính thức cho việc hiểu những ngôn ngữ mô hình hóa
Gia tăng sự phát triển của thị trường các công cụ hướng đối tượng
Hỗ trợ sự phát triển ở mức cao hơn các khái niệm như collaborations, frameworks, patterns and components
Tích hợp trong thực tế tốt nhất
UML 2.0 với nhiều thay đổi trong việc mô tả các thành phần trong biểu đồ
Việc mở rộng mô tả các thành phần như đoạn gộp, alt, opt, loop đã mang lại nhiều tiện ích cho người sử dụng trong việc tạo biểu đồ trình tự UML 2.0 Trong bài viết này, chúng tôi sẽ giới thiệu chi tiết về một số thành phần quan trọng trong biểu đồ UML 2.0 giúp nâng cao hiệu quả truyền đạt thông tin.
2.3.2 Biểu đồ trình tự UML
Biểu đồ trình tự thể hiện mối quan hệ động giữa các đối tượng trong hệ thống bằng cách trình bày thứ tự gửi nhận thông điệp Nó giúp xác định trình tự các hoạt động và tương tác diễn ra tại các thời điểm khác nhau, với các đối tượng được thể hiện bằng các đường thẳng đứng theo chiều từ trên xuống dưới trong trục thời gian Các thông điệp được biểu diễn bằng các đường gạch ngang có mũi tên nối các đối tượng, minh họa rõ ràng quá trình trao đổi thông tin theo thời gian Thông thường, phần chú thích và các ghi chú sẽ nằm ở bên lề của biểu đồ để làm rõ nội dung Ví dụ về biểu đồ trình tự sẽ giúp hiểu rõ quá trình hoạt động của hệ thống qua từng bước cụ thể.
Hình 2.3.2a: Quá trình đăng nhập ATM
Tiếp theo đây, tôi sẽ giới thiệu về các dạng của biểu đồ tuần tự trong UML 2.0
Uml 2.0 đã có sự thay đổi đáng kể vể cách thức biểu diễn biểu đồ tuần tự Đầu tiên là chú thích trong biểu đồ, đặt tên thành thành phần chú thích trong một khung, thành phần khung được sử dụng như một nền tảng cho UML 2.0[1] Một thành phần khung cung cấp một ranh giới cho biểu đồ, xác định vị trí và mối quan hệ của các thành phần trong biểu đồ Dưới đây, là mô tả cho khung
Hình 2.3.2b mô tả một khung cơ bản
Trong biểu đồ tuần tự, có tổng cộng 12 loại đoạn gộp được định nghĩa, bao gồm alt, opt, break, par, seq, strict, neg, critical, ignore, consider, assert, loop Tuy nhiên, thực tế chỉ có 4 dạng chính được sử dụng phổ biến nhất là alt, opt, loop và break Trong bài viết này, tôi sẽ trình bày chi tiết về các loại đoạn gộp này để giúp bạn hiểu rõ hơn về cách xây dựng và tối ưu hóa sơ đồ tuần tự.
Mục đích chính của bài viết là giới thiệu cách lựa chọn tổng thể có thể thay thế lẫn nhau giữa hai hoặc nhiều trình tự thông điệp để tối ưu hóa quá trình ra quyết định Các phương pháp thay thế này giúp mô hình hóa các quyết định trong lập trình cổ điển, như câu lệnh "nếu thì" (if-else), ví dụ như: "Nếu tôi mua ba món hàng, tôi sẽ được giảm giá 15%, hoặc được giảm giá 10%." Điều này giúp nâng cao khả năng tối ưu hóa và linh hoạt trong các hệ thống quyết định tự động.
Hình 2.3.2.1 biểu đồ trình tự sử dụng alt
Mảng kết hợp opt (sự tùy chọn) dùng để mô hình hóa các trình tự xảy ra trong các điều kiện nhất định, trong đó nếu điều kiện không được thỏa mãn thì trình tự sẽ không xảy ra Sự tùy chọn giúp mô tả các câu lệnh đơn giản dạng “if-then” (nếu thì), ví dụ như “nếu trên bàn có ít hơn 5 chiếc bánh, thì thêm vào đó 2 chiếc bánh” Điều này giúp xây dựng các quy trình logic rõ ràng và dễ hiểu trong các mô hình tự động hoặc chương trình.
Chú thích của mảng kết hợp tùy chọn tương tự như đối với mảng kết hợp thay thế, ngoại trừ nó chỉ có một toán hạng và không bao giờ có trạng thái “else” Mảng kết hợp tùy chọn cho phép kiểm tra nhiều điều kiện khác nhau trong code, giúp tối ưu hóa luồng thực thi Tuy nhiên, do chỉ có một toán hạng, nó phù hợp cho các trường hợp đơn giản và không cần xử lý rẽ nhánh phức tạp Việc hiểu rõ sự khác biệt này giúp lập trình viên viết mã rõ ràng và dễ bảo trì hơn.
Hình 2.3.2.2 biểu đồ dùng opt
2.3.2.3 Loop Đôi khi bạn sẽ cần một trình tự lặp đi lặp lại Trong UML 2.0 mô hình hóa một biểu đồ lặp đi lặp lại đã được cải tiến cùng với điều kiện của mảng kết hợp vòng lặp
Hình 2.3.2.3 biểu đồ sử dụng loop
Các khung break được dùng thông dụng để mô hình hóa ngoại trừ sự kiểm soát
Hình 2.3.2.4 Biểu đồ sử dụng break
Khi kiểm tra điều kiện `balance < amount` là sai, chương trình sẽ thực hiện phần mã sau lệnh `break`; ngược lại, nếu điều kiện đúng, lệnh `break` sẽ được thực thi Khi các thông điệp trong đoạn mã chứa lệnh `break` đã được thực hiện xong, các thông điệp hoặc hoạt động bên ngoài sẽ không còn được phép thực thi nữa, đảm bảo quá trình kiểm soát luồng chương trình hiệu quả.
Xây dựng máy trạng thái từ biểu đồ trình tự
Biểu đồ trình tự gồm nhiều thành phần quan trọng như đường sống, các thông điệp trao đổi giữa các đường sống và các đoạn gộp, thể hiện quá trình tương tác theo thứ tự rõ ràng Khi mô tả biểu đồ trình tự, cần nhấn mạnh các thành phần chính như đường sống để xác định các hoạt động chính, các thông điệp truyền tải để làm rõ các hành động được thực hiện, và các đoạn gộp dùng để nhóm các bước liên quan nhằm tối ưu hóa quá trình mô tả Việc hiểu rõ các thành phần này giúp truyền đạt nội dung hiệu quả, đồng thời phù hợp với các quy tắc tối ưu hóa SEO khi viết về biểu đồ trình tự.
Tên lớp trong mã nguồn chương trình tương ứng với tên một đường sống
Các thông điệp trao đổi giữa các đường sống mô tả đầy đủ các thành phần của một phương thức trong lớp tương ứng trong mã nguồn chương trình Một phương thức bao gồm các thành phần chính như tên phương thức, kiểu giá trị trả về và danh sách tham số Điều này giúp đảm bảo tính rõ ràng và khả năng tương tác hiệu quả giữa các thành phần trong hệ thống lập trình hướng đối tượng.
Hình 3.1.1 mô tả các thành phần trong biểu đồ trình tự của Altova Umodel, giúp xây dựng máy trạng thái (FSM) dựa trên cấu trúc dữ liệu biểu đồ trình tự Để phát triển FSM đầu tiên, cần thiết lập cấu trúc dữ liệu mô tả chính xác biểu đồ trình tự, trong đó hai thành phần quan trọng là đường sống và các thông điệp trao đổi giữa các đường sống này Việc xác định rõ các thành phần này sẽ hỗ trợ quá trình thiết kế và triển khai hệ thống hiệu quả hơn.
Thông qua phân tích tài liệu XMI, các lớp mô tả thành phần đường sống và thông điệp giữa các đường sống được xác định rõ ràng Lớp LifeLine đóng vai trò quan trọng trong việc mô tả một đường sống trong sơ đồ, giúp hiểu rõ hơn về cách các thành phần tương tác và truyền tải thông điệp Lớp Lifeline bao gồm các thuộc tính như id, className và name, góp phần xây dựng mô hình UML chính xác và chi tiết.
Trong tài liệu XMI, một đường sống được định nghĩa trong thẻ lifeline và nó có các thành phần quan trọng như:
- xmi.id cho biết id của đường sống
The "name" refers to the designation of the pathway, crucial for identifying the specific communication route Communication between two pathways is facilitated through the Message class, which is designed to handle data exchange effectively This class implements the Comparable interface to enable comparison between Message objects, aiding in efficient message management The Message class encapsulates key attributes such as "id," "name," "sendEventId," and "receiveEventId," ensuring comprehensive tracking of message details within the system Proper implementation of the Message class enhances the reliability and organization of inter-pathway communication.
Trong tài liệu XMI, môt thông điệp được đặc tả trong thẻ message, với một thông điệp có các thành phần chính như sau :
- xmi.id mô tả id của thông điệp
- name mô tả tên của thông điệp
- sendEventId mô tả thành phần nguồn, nơi bắt đầu thông điệp
- receiveEventId mô tả nơi đến của thông điệp
- from thông điệp được bắt nguồn từ đường sống nào
- To thông điệp kết thúc ở đường sống nào
- sourceState cho biết trạng thái bắt đầu của thông điệp
- targetState cho biết trạng thái kết thúc của thông điệp
2.4.2 Xây dựng máy trạng thái(FSM)
Trong biểu đồ trình tự, các thông điệp được xếp theo thứ tự để thể hiện thứ tự của các hành động Việc mô tả các thành phần của máy trạng thái phải đảm bảo mô tả chính xác quá trình này Các thông điệp đóng vai trò là tác nhân chính gây chuyển đổi trạng thái trong hệ thống, do đó, máy trạng thái phải phản ánh thứ tự trước sau của các thông điệp đó Máy trạng thái được thiết kế bao gồm các thành phần dữ liệu chính nhằm mô tả quá trình chuyển đổi trạng thái một cách rõ ràng và chính xác.
states danh sách các trạng thái của máy trạng thái
events danh sách các thông điệp
firstState trạng thái đầu tiên của máy trạng thái
lastStates tập các trạng thái cuối của máy trạng thái
Thuật toán xây dựng máy trạng thái : Bảng 2.4.2: Thuật toán xây dựng máy trạng thái[2]
2 output Máy trangjt hái được sinh ra nếu không thông báo lỗi
3 Các bước thực hiện 1 Khởi tạo fsm, states, events, firstState, lastState
2 Đọc file XMI, lấy các thành phần cần thiết: thông điệp, trạng thái,…
3 Duyệt từng thông điệp, đưa vào events
3.1 Nếu là thông điệp cuối thì thêm trạng thái kết thúc vào lastStates
3.2 Nếu là thông điệp bắt đầu , thêm trạng thái nguồn vào firstState
3.3 Nếu là thông điệp trung gian, lưu các dữ liệu vào máy trạng thái
Tổng kết chương
Trong chương này, tôi trình bày các khái niệm cơ bản về annotations, AspectJ và annotations trong AspectJ để xây dựng nền tảng cho công trình của mình Ngoài ra, tôi giới thiệu UML, biểu đồ trình tự UML 2 nhằm hỗ trợ quá trình thiết kế hệ thống Đây là các nền tảng trọng yếu trong khóa luận để phát triển công cụ sinh mã Aspect tự động Bên cạnh đó, tôi cũng trình bày thuật toán xây dựng máy trạng thái cơ bản nhằm tối ưu hóa quá trình tự động hóa trong ứng dụng của dự án.
Xây dựng cộng cụ tự động sinh Aspect từ máy trạng thái
Biểu đồ trình tự và các đoạn gộp
Trong biểu đồ trình tự UML 2.0, các đoạn gộp đóng vai trò quan trọng trong việc thể hiện luồng điều kiện và thao tác lặp lại Để xây dựng máy trạng thái chính xác, cần mô tả đầy đủ các yếu tố này, bao gồm các loại đoạn gộp phổ biến như alt, opt, loop và break Việc phát triển cấu trúc dữ liệu phù hợp giúp lưu trữ và xử lý các thành phần này hiệu quả Áp dụng phương pháp phân tích tài liệu XMI, các lớp mô tả khối gộp được định nghĩa rõ ràng, hỗ trợ việc xây dựng biểu đồ trình tự UML 2.0 chính xác hơn.
Hình 4.1 Sơ đồ biểu diễn các thành phần khối gộp
Lớp CombinedFragment trong UML2 chịu trách nhiệm mô tả các đoạn gộp trong biểu đồ trình tự, giúp thể hiện các phần mở rộng và điều kiện trong quá trình tương tác Đây là một thành phần quan trọng trong biểu đồ trình tự của UML, giúp mô tả rõ ràng các luồng xử lý phức tạp và các tình huống đa dạng trong hệ thống phần mềm Việc sử dụng CombinedFragment nâng cao khả năng thể hiện linh hoạt và chính xác các kịch bản tương tác giữa các thành phần trong hệ thống.
Trong XMI, một đoạn gộp được mô tả trong thành phần fragment với xmi:type="uml:CombinedFragment", thể hiện rõ các khung cảnh hợp nhất trong sơ đồ UML Thành phần này bao gồm các yếu tố như điều kiện, bài tập, và các hoạt động liên quan, giúp minh họa các tương tác phức tạp trong mô hình Việc sử dụng xmi:type="uml:CombinedFragment" là cách chuẩn để biểu diễn các đoạn gộp có tính phân nhánh hoặc lặp lại trong sơ đồ UML, hỗ trợ cho việc phân tích và thiết kế hệ thống phần mềm chính xác hơn.
Xmi.Id : cho biết id của đoạn gộp
Name : cho biết tên của đoạn gộp
interactionOperator : cho biết dạng của fragment là alt, loop,break hay opt …
The xmi.id and xmi.uuid components are essential for identifying messages within combined fragments by comparing the IDs of the fragment with the message's senEvent The CombinedFragment class plays a crucial role in managing these interactions, facilitating accurate message tracking and analysis in UML diagrams.
Vector message; public Message firstMessageAfterFragment;
Lớp Loop, opt, break là các lớp con của lớp CombinedFragment, kế thừa đầy đủ các thành phần của nó mà không thêm mới Để xác định các lớp này trong quá trình phân tích tài liệu XMI, chúng ta dựa vào thành phần interactionOperator để phân biệt Việc này giúp đảm bảo phân loại chính xác các loại combined fragment trong quá trình phân tích mô hình UML.
Lớp Alt là một lớp con của lớp CombinedFragment, khác với ba lớp con trên, chúng ta cần quan tâm đến các thành phần khác của nó Alt là mô tả điều kiện lựa chọn giữa hai điều kiện, đòi hỏi chúng ta phải rõ ràng về đặc điểm này Ngoài các thành phần của lớp cha, lớp Alt còn có các thành phần riêng biệt cần được mô tả rõ ràng để hiểu đúng chức năng của nó.
Sau đó, máy trạng thái sẽ được sinh ra như phân tích ở chương 2
3.2 Sinh Aspect từ biểu đồ trình tự
Việc kiểm chứng giao thức trên nhiều lớp đối tượng trong biểu đồ trình tự có thể được coi như quá trình gọi tuần tự các phương thức Giao thức này được mô tả dưới dạng một máy trạng thái hữu hạn (FSM), giúp xác định hành vi chuyển đổi trạng thái chính xác Hành vi chuyển trạng thái được xem là đúng khi các chuyển đổi diễn ra theo quy trình đã định trước trong mô hình FSM, đảm bảo tính nhất quán và đúng đắn của giao thức.
- Trạng thái trước đó có gọi hàm chuyển trạng thái không
- Điểu kiện chuyển trạng thái có thỏa mãn không
Khi các điều kiện phù hợp được thỏa mãn, quá trình chuyển trạng thái diễn ra chính xác và hệ thống chuyển sang trạng thái mới Các điểm join-point xác định các vị trí chuyển trạng thái, trong đó before advice kiểm tra tính hợp lệ của chuyển trạng thái trước khi diễn ra Sau khi trạng thái được cập nhật, after advice kiểm tra các điều kiện của vòng lặp, các hành vi như alt, break, opt, cũng như điều kiện dừng của hệ thống Để thuận tiện trong việc kiểm tra, trạng thái ban đầu của máy trạng thái được thiết lập là “0”, và một biến Id được sử dụng để xác định tên các trạng thái hiện tại trong danh sách ArrayList Id.
Trước tiên, cần kiểm tra xem phương thức gây biến đổi trạng thái có bắt đầu từ trạng thái hiện tại hay không Nếu phát hiện vi phạm quy định này, hệ thống sẽ lập tức thông báo lỗi để người dùng biết Trong quá trình thực hiện, trạng thái hiện tại sẽ được cập nhật lại phù hợp với các biến đổi mới Để đảm bảo quá trình hoạt động chính xác, trạng thái hiện tại được khởi tạo mặc định ban đầu là “0”.
Khi trạng thái hiện tại đã chuyển thành trạng thái mới sau khi thực hiện lời khuyên, cần kiểm tra xem trạng thái đó có thuộc các loại như alt, loop, opt hay break không để xác định hướng giải quyết phù hợp Nếu trạng thái là một trong các trạng thái kết thúc, hệ thống sẽ cung cấp thông báo tổng quát về quá trình thực hiện Điều này giúp đảm bảo quá trình xử lý diễn ra một cách rõ ràng và dễ theo dõi.
Thuật toán sinh mã Aspect như sau : Bảng 3.2: Thuật toán sinh mã Aspect
Duyệt danh sách các trạng thái FSM và xác định tập các thông điệp gây ra biến đổi trạng thái giúp tối ưu hoá quá trình xử lý Các hàm được sử dụng để xử lý các thông điệp này và chuyển đổi chúng về dạng @Implement * ten_ham() nhằm chuẩn hóa mã nguồn Việc tích hợp các hàm này vào pointcut giúp giảm số lượng pointcut cần thiết, tạo ra sự đơn giản và hiệu quả trong quản lý trạng thái của hệ thống.
2 Duyệt danh sách các trạng thái trong FSM lấy các trạng thái nguồn và các trạng thái đích tương ứng để tạo ra các điều kiện kiểm tra
3 Tạo các pointcut, advice bằng cách thay thế các xâu tương ứng vừa được tạo ra ở trên vào vị trí cần thiết
4 Kết hợp pointcut advice thành Aspect cần thiết
Việc kiểm chứng từng thành phần trong đoạn gộp của biểu đồ trình tự có những điểm khác biệt quan trọng Khi trạng thái của các thành phần như break hoặc opt được kích hoạt, hệ thống sẽ gửi thông báo phù hợp để đảm bảo quá trình vận hành diễn ra chính xác Trạng thái kích hoạt của một đoạn gộp break hoặc opt chính là trạng thái ban đầu khi chúng bắt đầu hoạt động, giữ vai trò then chốt trong quá trình kiểm tra và xử lý biểu đồ trình tự.
Khi một phương thức kích hoạt chuyển đổi trạng thái, ta cần thông báo và ghi nhận sự thay đổi này Trong quá trình hoạt động, loop cũng có các trạng thái kích hoạt tương tự, nhưng cần đếm số lần xuất hiện của chúng bằng cách sử dụng biến có tên dạng loop+ Ví dụ, khi trạng thái loop là “2”, ta đặt tên biến đếm là loop2 và khởi tạo ban đầu là 0, rồi tăng lên khi trạng thái này được kích hoạt Đối với các trạng thái như alt, break, opt, chúng cũng có các trạng thái kích hoạt riêng, nhưng khác với loop, cần xác định rõ hướng kích hoạt khi có nhiều trạng thái cùng hiện diện để đảm bảo quá trình theo dõi trong hệ thống hoạt động chính xác.
Trong chương này, tôi đã trình bày cách xây dựng cấu trúc dữ liệu để mô tả các đoạn gộp, giúp tổ chức thông tin một cách rõ ràng và dễ quản lý Đồng thời, tôi cũng hướng dẫn cách đưa các thông tin về các đoạn gộp này vào máy trạng thái, tối ưu hóa quá trình xử lý và phân tích dữ liệu Những kiến thức này giúp nâng cao hiệu quả trong thiết kế hệ thống và ứng dụng thực tiễn.
Trong chương này, tôi đã trình bày phương pháp sinh Aspect từ máy trạng thái để kiểm tra các giao thức sinh ra từ biểu đồ tuần tự UML Phương pháp chính là duyệt qua các trạng thái của FSM, xác định các thành phần tương ứng và chuyển đổi chúng thành các pointcut và advice trong Aspect Phương pháp này đã được kiểm chứng với hầu hết các giao thức mô tả bằng biểu đồ trình tự UML, bao gồm vòng lặp, lựa chọn một thành phần, lựa chọn nhiều thành phần, và đoạn gộp break, góp phần mở rộng khả năng của UML 2 trong kiểm thử phần mềm.
Kết luận
Trong chương này, tôi đã trình bày cách xây dựng cấu trúc dữ liệu để mô tả các đoạn gộp một cách chính xác và tối ưu Tôi cũng hướng dẫn cách đưa các thông tin về các đoạn gộp vào máy trạng thái để nâng cao hiệu quả xử lý và phân tích dữ liệu Đây là các bước quan trọng trong việc tối ưu hóa hệ thống và đảm bảo tính linh hoạt trong quản lý dữ liệu gộp.
Trong chương này, tôi trình bày phương pháp sinh Aspect từ máy trạng thái để kiểm tra các giao thức được sinh ra từ biểu đồ tuần tự UML Phương pháp này duyệt qua các trạng thái của FSM, xác định các thành phần tương ứng, sau đó đưa vào Aspect dưới dạng các pointcut và advice nhằm kiểm tra đúng quy tắc của giao thức Các Aspect được xây dựng đã chứng minh khả năng kiểm thử hầu hết các giao thức mô tả bằng biểu đồ trình tự UML, bao gồm vòng lặp loop, lựa chọn một thành phần (opt), lựa chọn nhiều thành phần (alt) và đoạn gộp break, mở rộng khả năng của UML 2 trong kiểm thử phần mềm.
Aspect được tích hợp vào chương trình nguồn giúp kiểm thử hiệu quả các chức năng của ứng dụng Java theo đúng đặc tả của biểu đồ trình tự, đảm bảo tính chính xác và độ tin cậy của hệ thống.
Thực nghiệm
Xây dựng công cụ
Sau khi đặc tả giao thức kiểm chứng bằng UML, công cụ Altova UModel 2 hỗ trợ xuất biểu đồ UML dưới dạng XMI, đây chính là đầu vào cho công cụ của tôi Áp dụng phương pháp đã giới thiệu ở các chương trước, tôi đã cài đặt thành công công cụ tự động sinh Aspect từ tài liệu XMI Phương pháp này chủ yếu sử dụng công cụ Eclipse 3 và framework JDK để cài đặt và tích hợp các thành phần cần thiết Giao diện làm việc của Eclipse được minh họa rõ ràng giúp hỗ trợ quá trình phát triển và tự động hóa sinh mã hiệu quả.
Hình 5.1a : Cài đặt bằng công cụ eclipse
Thuật toán cốt lõi của phương pháp là phân tích tài liệu XMI để xây dựng cấu trúc mô tả các thành phần của biểu đồ trình tự UML Quá trình này giúp tạo lập máy trạng thái phản ánh chính xác sơ đồ, từ đó duyệt danh sách các trạng thái để xác định vị trí phù hợp trong mô hình Việc này hỗ trợ hiểu rõ các yếu tố của biểu đồ UML, nâng cao hiệu quả trong quá trình phân tích và thiết kế hệ thống.
Umodel của Altova giúp tạo ra các chuỗi nội dung chứa từ khóa "Aspect" theo đúng cấu trúc đã định Quá trình này sử dụng các thông điệp để xác định nguồn và đích, từ đó gọi phương thức phù hợp để sinh ra nội dung "Aspect" một cách chính xác Điều này đảm bảo sự nhất quán và phù hợp với các quy tắc SEO, tăng khả năng tối ưu hóa nội dung trên các công cụ tìm kiếm.
Cài đặt công cụ thông qua các bước :
- Bước 1 : Tạo ra cấu trúc dữ liệu lưu các thông tin về biểu đồ trình tự
- Bước 2 : Cài đặt thuật toán sinh ra máy trạng thái (FSM)
- Bước 3 : Cài đặt thuật toán sinh tự động Aspect
Sau khi hoàn tất quá trình cài đặt, tôi đã có được công cụ tự động sinh mã kiểm chứng Công cụ của tôi được cài đặt dưới dạng một plugin cho Eclipse, tên là “org.eclipse.adj.creattoxmi” Để công cụ hoạt động, cần sao chép file “org.eclipse.adj.creattoxmi.jar” vào thư mục “plugins” của Eclipse Khi đã cài đặt, plugin sẽ xuất hiện trong Eclipse và hoạt động theo hình thức mô tả trong hình minh họa.
Hình 5.1b : mô tả hoạt động của công cụ Để hoạt động công của của tôi cài đặt, có một số yêu cầu như sau :
- Thứ nhất : copy file org.eclipse.adj.creattoxmi.jar vào thư mục plugins của eclipse
- Thứ hai : import file Implement-Java.jar vào thư viện của project
- Các file xmi được để trong một package của project.Mặc định, Aspect được sinh ra sẽ nằm trong package này
- Hoạt động: o Chọn file xmi cần chuyển qua Aspect o Nhấn ctrl + N o Chọn Create Aspect from Xmi
Hình 5.1.c hướng dẫn cách lựa chọn chức năng, trong đó bạn có thể đặt lại tên và địa điểm cho Aspect nếu cần thiết Nếu không thực hiện thay đổi, Aspect sẽ tự sinh ra với tên trùng với tên của file XMI đã chọn và nằm trong package chứa file XMI đó, giúp đảm bảo tính nhất quán và dễ quản lý trong quá trình thiết kế mô hình.
Hình 5.1d: Giao diện đặt tên cho aspect sinh ra
Nếu không có file XMI nào được chọn hoặc Aspect không sinh ra được thì sẽ tạo ra một Aspect mặc định không có nội dung bên trong.
Kiểm chứng một số giao thức thực tế
Hình 4.2.1.1.Giao thức truy cập thông tin cơ bản ATM
Giao thức mô tả mối quan hệ giữa các lớp User, ATM, Consortium và Branch
Khi một đối tượng User gọi phương thức getCashOnHand(), các hàm liên quan phải tuân theo một trình tự gọi để đảm bảo quá trình thành công Quá trình này bắt đầu từ getCashOnHand(), sau đó gọi validateAccountInfo() để xác thực thông tin tài khoản, tiếp theo là verifyCardWithBank() để xác minh thẻ ngân hàng qua các tham số số tiền và chuỗi thẻ, cuối cùng là getConnected() để thiết lập kết nối cần thiết Điều này đảm bảo quá trình lấy tiền mặt diễn ra chính xác và an toàn.
Sử dụng công cụ Altova UModel để vẽ lại giao thức, sau đó xuất ra dưới dạng tài liệu XMI làm đầu vào cho các công cụ phân tích File XMI được lưu trong thư mục xmi của dự án để dễ dàng quản lý và sử dụng cho các bước tiếp theo Quá trình này giúp tối ưu hoá việc lưu trữ và chia sẻ mô hình phần mềm một cách hiệu quả.
When running the program, the Aspect code is generated with a structure that includes a class named `Aspect ATM` This class contains a boolean variable `isError` to determine the fault tolerance state, an integer `numError` to track the current error count, and two `ArrayList` collections: `Id`, which represents the current statuses, and `listEnd`, which stores the terminal states These components are essential for managing error detection and state transitions within the system, adhering to best SEO practices by clearly describing the code's purpose and structure.
Pointcut ATMChecked():call(@Implement * getConnected(char)) || call(@Implement * verifyCardWithBank(int)) || call(@Implement * validateAccoutinfo())|| call(@Implement * getCashOnhand()); before() : ATMChecked(){
String nameThisAspect = this.getClass().toString(); nameThisAspect = nameThisAspect.substring( nameThisAspect.lastIndexOf(".") + 1, nameThisAspect.length());
String CallMethod = getNameMethod( thisJoin-pointStaticPart);
String AnnotationOfMethod = getAnnotationOfMethod(thisJoin-pointStaticPart);
This code snippet verifies whether a specific method is configured to use a particular aspect If the aspect is not defined for the method, the process exits early to prevent errors It also initializes the default state by adding "0" to the Id list if it is empty, ensuring proper data management Additionally, the code manages process termination states by adding the end state "4" to the listEnd collection if it hasn't been initialized yet These checks and initializations are crucial for maintaining the integrity and proper flow of the program.
} isError = true; // mac dinh la goi thu tu phuong thuc sai ArrayList l = new ArrayList();
ArrayList target = new ArrayList();
Trong quá trình gọi phương thức, có thể xác định hai trạng thái chính: trạng thái nguồn khi gọi phương thức và trạng thái kết thúc được thêm vào danh sách mục tiêu Cụ thể, khi phương thức gọi là "* getConnected(char)", trạng thái nguồn được đánh dấu bằng "3" và trạng thái kết thúc được thêm vào danh sách mục tiêu là "4" Việc phân biệt rõ ràng các trạng thái này giúp tối ưu hóa quy trình xử lý và kiểm soát luồng thực thi trong hệ thống.
} else if( CallMethod.equals("* voidverifyCardWithBank(int)")){ l.add("2"); target.add("3");
} else if( CallMethod.equals("* validateAccoutinfo()")){ l.add("1"); target.add("2");
} else if( CallMethod.equals("* getCashOnhand()")){ l.add("0"); target.add("1");
/* kiem tra tinh dung sai cua viec goi phuong thuc, dat lai trạng thai hien tai*/ if(isError == true){ numError ++;
System.out.println("goi sai o dong phuong thuc "+ CallMethod +" o dong thu :
System.out.println("so loi hien tai : " + numError);
/*Neu goi moi trang thai ket thuc nao do thi hien thi cac thong tin*/ for(int i = 0; i < listEnd.size(); i ++) if(Id.lastIndexOf(listEnd.get(i)) >=0){
System.out.println("so loi hien tai : " + numError);
Lập trình viên phải xây dựng các lớp User, AMT, Consortium với các phương thức cần thiết
Lớp User gồm các phương thức float getCashOnhand() public class User {
@Implement("ATM.xmi") float getCashOnhand(){
Lớp ATM chứa phương thức : void validateAccoutinfo() và phương thức char getConnected() Được mô tả như dưới đây public class ATM {
@Implement("ATM.xmi") public char getConnected(){
@Implement("ATM.xmi") public void validateAccoutinfo() { // do somthing
Lớp Consortium chứa phương thức void verifyCardWithBank(int stringCardStrinp) Được cài đặt như dưới đây : public class Consortium {
@Implement("ATM.xmi") public void verifyCardWithBank(int stringCardStrinp){
Aspect sinh ra được tích hợp vào mã nguồn tự động tại package xmi của project Tôi được kết quả kiểm thử của một số đầu vào như sau:
The test involved a function call sequence: getCashOnhand(); validateAccountInfo(); verifyCardWithBank(0); getConnected() The result displayed a message indicating zero errors, demonstrating successful execution The call sequence aligns with the protocol specifications, confirming the correctness of the implementation.
Chương trình không báo lỗi
Test2 : o Chuỗi gọi hàm : getCashOnhand();validateAccoutinfo(); getConnected(); o Kết quả :
Hình 4.2.1.2b kết quả Test2 o Nhận xét : Chuỗi gọi hàm sai với đặc tả, không chứa phương thức getConnected() Chương trình báo lỗi và hiện ra thông báo lỗi
Chương trình hoạt động đúng
Test3 : o Chuỗi gọi hàm : getCashOnhand();verifyCardWithBank(0); validateAccoutinfo();verifyCardWithBank(0); verifyCardWithBank(0); getConnected(); o Kết quả
Hình 4.2.1.2c Kết quả Test3 o Nhận xét : Chuỗi gọi hàm sai đặc tả : Phương thức verifyCardWithBank(int) gọi trước phương thức validateAccoutinfo()
Chương trình hoạt động chính xác
Chỉ sau ba ca kiểm thử, tôi đã chứng minh được rằng công cụ kiểm thử dựa trên Aspect do tôi phát triển có khả năng kiểm tra chính xác giao thức đã mô tả Điều này cho thấy độ tin cậy cao của công cụ trong việc đảm bảo tính đúng đắn của giao thức, góp phần nâng cao chất lượng phần mềm và giảm thiểu rủi ro lỗi.
4.2.2 Kiểm chứng biểu đồ loop 4.2.2.1 Đặc tả giao thức Để minh chứng cho việc dùng Aspect sinh ra để kiểm chứng cho biều đồ dùng loop Tôi lấy một ví dụ đơn giản với biểu đồ trình tự được xây dựng như dưới đây
Hình 4.2.2.1.Biểu đồ trình tự dùng loop đơn giản
Phương thức m1() có thể được lặp lại nhiều lần
Aspect được sinh ra public Aspect Loop{ int loop2 = 0 ; private boolean isError = false ; private int numError = 0; private ArrayList Id = new ArrayList();
ArrayList listEnd = new ArrayList();
//////////////////// pointcut LoopChecked():call(@Implement * m2() )||call( @Implement * m1() )|| call(@Implement * m1())||call(@Implement * m2())||call(@Implement * m()); before() : LoopChecked(){
String nameThisAspect = this.getClass().toString(); nameThisAspect=nameThisAspect.substring(nameThisAspect.lastIndexOf(".") + 1, nameThisAspect.length());
String CallMethod = getNameMethod(thisJoin-pointStaticPart);
String AnnotationOfMethod= getAnnotationOfMethod( thisJoin-pointStaticPart ); if(isPart(nameThisAspect,AnnotationOfMethod) == false) return; if(Id.isEmpty())
Id.add("0"); if(listEnd.isEmpty()){ listEnd.add("3");
ArrayList l = new ArrayList();
ArrayList target = new ArrayList(); if( CallMethod.equals("* m2()")){ l.add("2"); target.add("3"); l.add("1"); target.add("3");
} else if( CallMethod.equals("* m1()")){ l.add("2"); target.add("2"); l.add("1"); target.add("2");
} else if( CallMethod.equals("* m()")){ l.add("0"); target.add("1");
} setIdIfTrue(l,target); if(isError == true){ numError ++;
System.out.println("goi sai o dong phuong thuc "+ CallMethod +" o dong thu :
/*Neu trang thai vua den la trang thai loop thi tang bien loop tuong ung them*/ if(Id.lastIndexOf("2")>=0) for(int i = 0; i < listEnd.size(); i ++) if(Id.lastIndexOf(listEnd.get(i)) >=0){
System.out.println("vong loop thu 1bat dau bang phuong thuc* m1() duong goi
"+loop2+ "lan"); if(numError > 0) System.out.println("so loi hien tai : " + numError);
Tiếp theo đây tôi sẽ đưa ra một số test để kiểm tra gia thức này
Test1 : o Đầu vào : m() -> m2() o Kết quả
Hình 4.2.2.2a : kết quả test1 o Nhận xét : Đoạn gộp loop không được sử dụng, nó được gọi 0 lần, giao thức đặc tả đúng, không có lỗi Kết quả đúng
Test2 o Đầu vào : m() -> m1() -> m1() -> m2() o Kết quả :
Trong bài kiểm tra này, đoạn gộp vòng lặp đã được kích hoạt và gọi đúng hai lần, thể hiện giao thức hoạt động chính xác mà không gặp lỗi nào Kết quả kiểm tra cho thấy hệ thống hoạt động đúng như mong đợi, đảm bảo tính ổn định và chính xác của quá trình.
Test3 o Đầu vào : m1() -> m2() -> m1() -> m1() ->m2() o Kết quả :
Hình 4.2.2.2c: Kết quả test3 o Nhận xét :
Phương thức đầu tên là m() không được gọi trực tiếp mà phải gọi qua các phương thức khác như m1() hoặc m2() để đảm bảo tính hợp lệ Việc gọi trực tiếp m() hoặc gọi m1() sau m2() là sai, gây ra hai lỗi cần được khắc phục Vòng lặp hoạt động chính xác nhờ phương thức m1(), được gọi đúng 3 lần, đảm bảo kết quả đúng như mong muốn.
Tương tự như đối với loop, tôi đã kiểm thử với alt, break, opt và cho kết quả kiểm tra đúng với đặc tả
4.2.3 Kiểm chứng biểu đồ tổng quát 4.2.3.1 Đặc tả giao thức
Hình 4.2.3.1: Biểu đồ trình tự sử dụng nhiều đoạn gộp
Giao thức trên chứa các đoạn gộp alt, opt, loop, break
Aspect được sinh ra : public aspect All{ int loop2 = 0 ; private boolean isError = false ; private int numError = 0; private ArrayList Id = new ArrayList();
This code defines an ArrayList named `listEnd` to store String elements and uses an AspectJ pointcut called `AllChecked()` to intercept method calls annotated with `@Implement` across multiple methods (`m0()` to `m9()`) The `before()` advice tied to this pointcut executes prior to any of these method calls, enabling pre-processing or logging, which enhances modularity and maintainability in aspect-oriented programming Implementing such pointcuts effectively allows developers to apply cross-cutting concerns seamlessly across multiple methods, improving code organization and reusability.
String nameThisAspect = this.getClass().toString(); nameThisAspect=nameThisAspect.substring( nameThisAspect.lastIndexOf(".") + 1, nameThisAspect.length());
String CallMethod = getNameMethod(thisJoin-pointStaticPart);
String AnnotationOfMethod= getAnnotationOfMethod(thisJoin-pointStaticPart); if(isPart(nameThisAspect,AnnotationOfMethod) == false) return; if(Id.isEmpty()) Id.add("0"); if(listEnd.isEmpty()){ listEnd.add("6"); listEnd.add("10");
ArrayList l = new ArrayList();
ArrayList target = new ArrayList(); if( CallMethod.equals("* m9()")){ l.add("9"); target.add("10");
} else if( CallMethod.equals("* m8()")){ l.add("8"); target.add("9");
} else if( CallMethod.equals("* m6()")){ l.add("6"); target.add("9"); l.add("5"); target.add("7");
} else if( CallMethod.equals("* m7()")){ l.add("6");
} else if( CallMethod.equals("* m5()")){ l.add("5"); target.add("6");
} else if( CallMethod.equals("* m4()")){ l.add("4"); target.add("5"); l.add("3"); target.add("5");
} else if( CallMethod.equals("* m3()")){ l.add("3"); target.add("4");
} else if( CallMethod.equals("* m2()")){ l.add("2"); target.add("3"); l.add("1"); target.add("3");
} else if( CallMethod.equals("* m1()")){ l.add("2"); target.add("2"); l.add("1"); target.add("2");
} else if( CallMethod.equals("* m0()")){ l.add("0"); target.add("1");
} setIdIfTrue(l,target); if(isError == true){ numError ++;
System.out.println("goi sai o dong phuong thuc "+ CallMethod +" o dong thu
} after():AllChecked(){ if(Id.lastIndexOf("2")>=0) loop2++;
/*neu trang thai hien tai la trang thai opt thi thong bao*/ if(Id.lastIndexOf("4")>=0)
System.out.println("khoi opt thu 1 bat dau bang * m3() duoc su dung "+" o dong thu : "+thisJoin-pointStaticPart.getSourceLocation().getLine());
/*Neu trang thai hien tai la trang thai break thi thong bao*/ if(Id.lastIndexOf("6")>=0)
System.out.println("khoi break thu 1 bat dau bang * m5() duoc su dung "+" o dong thu : "+thisJoin-pointStaticPart.getSourceLocation().getLine());
/*thong bao viec khoi alt duoc kich hoat neu trang thai hien tai la trang thai alt*/ if(Id.lastIndexOf("6")>=0){
System.out.println("khoi alt thu 1 bat dau bang * m5() duoc su dung "+" o dong thu : "+thisJoin-pointStaticPart.getSourceLocation().getLine());
} for(int i = 0; i < listEnd.size(); i ++) if(Id.lastIndexOf(listEnd.get(i)) >=0){
System.out.println("vong loop thu 1bat dau bang phuong thuc* m1() duong goi
"+loop2+ "lan"); if(numError > 0) System.out.println("so loi hien tai : " + numError);
Tiếp theo đây, tôi sẽ xây dựng một số bài kiểm tra để minh chứng sự hoạt động của aspect sinh ra
- Test1 o Chuỗi gọi hàm m1() -> m2() -> m3() -> m4 -> m5() o Kết quả
Hình 4.2.3.2a: Kết quả test1 o Nhận xét
Trong quá trình thực thi, chuỗi gọi hàm kết thúc sau khi gọi đoạn gộp break, giúp kiểm soát luồng thực thi trong vòng lặp Khi khối lệnh vòng lặp được kích hoạt một lần, khối opt cũng được kích hoạt theo, đảm bảo quá trình xử lý diễn ra chính xác Thứ tự các thông điệp trong chuỗi mô tả đúng đắn các bước thực hiện, tạo ra kết quả chính xác và rõ ràng trong quá trình xử lý dữ liệu.
- Test2 o Chuỗi gọi hàm m1() -> m2() -> m3() -> m4 -> m6() -> m7() -> m8() o Kết quả
Hình 4.2.3.2b: Kết quả test2 o Nhận xét
Trong ví dụ này, chuỗi gọi hàm kết thúc ở khối alt với lựa chọn thứ nhất, phù hợp với đặc tả và đảm bảo kết quả đúng Các đoạn gộp như loop, alt, opt được sử dụng hiệu quả, trong khi đoạn gộp break không được áp dụng Việc tuân thủ đúng chuỗi gọi hàm đảm bảo tính chính xác của kết quả, phản ánh rõ ràng sự phù hợp với yêu cầu kỹ thuật của hệ thống.
- Test3 o Chuỗi gọi hàm m1() -> m2() -> m3() -> m4 -> m6() -> m9() -> m10() -> m11() o Kết quả
Hình 4.2.3.2c: Kết quả test3 o Nhận xét
Tương tự như ví dụ trên, nhưng khối alt đi theo nhánh thứ hai Kết quả mô tả đúng
- Test4 o Chuỗi gọi hàm m2()-> m4() -> m5() o Kết quả
Trong đoạn phân tích, đoạn gộp loop, alt, opt không được sử dụng, đảm bảo tính đúng đắn của việc gọi hàm theo đặc tả Kết quả kiểm tra cho thấy chuỗi gọi hàm phù hợp, xác nhận rằng kết quả mô tả là chính xác và đúng như dự kiến.
- Test5 o Chuỗi gọi hàm m1() -> m2() -> m3 -> m4() -> m7() -> m8() o Kết quả
Hình 4.2.3.2e: Kết quả test5 o Nhận xét
Chuỗi gọi hàm gọi thiếu hàm m6() so với đặc tả Kết quả thông báo đúng
- Test6 o Chuỗi gọi hàm m1() -> m2() -> m3 -> m10() -> m4() -> m6()-> m7() -> m8() o Kết quả
Hình 4.2.3.2f: Kết quả test6 o Nhận xét Chuỗi gọi hàm sai ở 2 điểm :
Thứ nhất: gọi sai hàm m10() Sau hàm m3() lẽ ra phải gọi hàm m4()
Thứ hai: Sau hàm m10() lẽ ra phải gọi hàm m11() nhưng lại gọi hàm m4()
Như vậy, kết quả mô tả là đúng.
Kết luận
Trong chương này, tôi đã trình bày cách cài đặt công cụ dựa trên phương pháp “Kiểm chứng đặc tả UML,” giúp sinh mã Aspect phù hợp để đan xen vào chương trình trong thời gian chạy Bước đầu, tôi đã kiểm chứng một số giao thức để chứng minh rằng công cụ của tôi có khả năng tạo ra Aspect phù hợp phục vụ cho quá trình kiểm chứng hệ thống.
Trong quá trình thực hiện khóa luận, tôi đã nghiên cứu kiến thức cơ bản về kiểm chứng phần mềm, giúp hiểu rõ các phương pháp đảm bảo chất lượng phần mềm hiệu quả Từ đó, tôi đã phát triển phương pháp kiểm chứng dựa trên AOP (Aspect-Oriented Programming) nhằm nâng cao khả năng phát hiện lỗi và tối ưu quá trình kiểm thử phần mềm Ngoài ra, tôi còn xây dựng một công cụ tự động sinh Aspect, nhằm tự động hoá quá trình tạo ra các khối mã kiểm chứng, giúp tiết kiệm thời gian và nâng cao độ chính xác trong kiểm thử phần mềm.
Dưới sự phát triển của công cụ PVG (Protocol Verification Generator), tôi đã mở rộng khả năng kiểm chứng đặc tả cho UML 2 để phù hợp với các yêu cầu phức tạp hơn Khả năng tự động sinh ra Aspect đã hoàn thiện, giúp tối ưu hóa quy trình kiểm thử các đặc tả UML đa dạng Trước đây, công cụ PVG chỉ giải quyết các giao thức đơn giản, nhưng trong khóa luận này, tôi đã thành công trong việc xử lý các trường hợp đa dạng, bao gồm các biểu đồ UML phức tạp Đây là bước tiến lớn trong việc nâng cao hiệu quả và độ chính xác của các công cụ kiểm thử dựa trên UML.
2 chứ không phải bằng Agent UML như ở công cụ PVG nữa
Trong phương pháp này, Aspect được sinh ra để cắt ngang hệ thống nhằm kiểm tra hoạt động trong quá trình vận hành Quá trình này bắt đầu từ việc xuất biểu đồ trình tự UML dưới dạng tài liệu XMI, sau đó phân tích tài liệu XMI để xây dựng máy trạng thái (FSM), giúp nâng cao hiệu quả kiểm thử hệ thống và đảm bảo tính chính xác của phần mềm.
FSM được xử lý để tạo ra Aspect cuối cùng nhằm phục vụ công tác kiểm chứng Phương pháp này giúp xây dựng công cụ kiểm chứng hiệu quả cho các đoạn gộp cơ bản trong biểu đồ trình tự, bao gồm các đoạn gộp như alt, opt, loop và break Công cụ này nâng cao khả năng xác thực các phần tử trong biểu đồ, đảm bảo chính xác và tin cậy trong quá trình kiểm tra.
Tuy nhiên, tôi cũng vấp phải một số khó khăn trong quá trình thực hiện khóa luận như:
Trong các đoạn gộp trọng biểu đồ trình tự, có thể xuất hiện các điều kiện kích hoạt khác nhau, đòi hỏi phải xác định rõ các thành phần trong điều kiện và phương thức trả lại dữ liệu Tuy nhiên, việc này gặp khó khăn do các đặc tả trong UML2 và hạn chế của tài liệu XMI, khiến tôi chưa thể xử lý hiệu quả vấn đề này.
Hệ thống có nhiều thành phần sẽ làm cho tài liệu XMI trở nên phức tạp hơn, đặc biệt khi các đoạn gộp không liên kết chặt chẽ Công cụ của tôi có khả năng kiểm chứng hiệu quả đối với các đoạn gộp phân tách rõ ràng, nhưng gặp khó khăn khi xử lý các đoạn gộp lồng nhau, chẳng hạn như các vòng lặp lồng trong đoạn gộp alt.
Trong tương lai, tôi sẽ tiếp tục nghiên cứu để hoàn thiện công cụ dựa trên các vấn đề tồn tại trong khóa luận này, nhằm đáp ứng mọi yêu cầu của người dùng và nâng cao hiệu quả của công trình nghiên cứu.
Hướng phát triển gần nhất của tôi là tạo ra công cụ để giải quyết vấn đề ràng buộc các đoạn gộp lồng nhau trong biểu đồ UML 2 Tôi cũng sẽ nghiên cứu phương pháp sinh mã Aspect không chỉ dựa trên tài liệu XMI mà còn dựa vào các ràng buộc điều kiện mô tả bằng các dạng dữ liệu khác nhằm nâng cao tính ứng dụng của công cụ trong quy trình phát triển phần mềm hiện nay.
Phụ lục A : Tài liệu XMI mô tả biểu đồ trình tự