Tim hieu cong nghe DESIGN BY CONTRACT va xay dung cong cu

114 13 0
Tim hieu cong nghe DESIGN BY CONTRACT va xay dung cong cu

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Eiffel hỗ trợ rất nhiều tính năng: tiếp cận hướng đối tượng hoàn thiện, khả năng giao tiếp bên ngoài có thể giao tiếp với các ngôn ngữ C, C++, Java,…, hỗ trợ vòng đời phần mềm bao gồm vi[r]

(1)TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN KHOA CÔNG NGHỆ THÔNG TIN BỘ MÔN CÔNG NGHỆ PHẦN MỀM LÊ TRẦN HOÀNG NGUYÊN – 0112103 NGUYỄN BÁCH KHOA - 0112140 TÌM HIỂU CÔNG NGHỆ DESIGN BY CONTRACT VÀ XÂY DỰNG CÔNG CỤ HỖ TRỢ CHO C# KHÓA LUẬN CỬ NHÂN TIN HỌC GIÁO VIÊN HƯỚNG DẪN Th.s: NGUYỄN ĐÔNG HÀ NIÊN KHÓA 2001 – 2005 (2) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# LỜI CẢM ƠN Đầu tiên, xin chân thành cảm ơn cô Nguyễn Đông Hà đã trực tiếp hướng dẫn cung cấp tài liệu để chúng em có thể tiếp cận và tìm hiểu công nghệ Design By Contract hữu ích này Bên cạnh đó, xin đồng gửi lời cảm ơn đến các thầy cô môn Công nghệ Phần mềm Nâng cao đã tạo điều kiện cho chúng em dành nhiều thời gian nghiên cứu đề tài này Cuối cùng, là điều thiếu sót không kể đến ủng hộ to lớn mặt tinh thần giúp đỡ tận tình gia đình, bạn bè, đặc biệt là bạn Nguyễn Lương Ngọc Minh và Nguyễn Ngọc Khánh Xin chân thành cảm ơn tất cả, người đã góp phần giúp cho luận văn này hoàn thành Thành phố Hồ Chí Minh, Tháng 7, 2005 (3) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# MỤC LỤC LỜI NÓI ĐẦU TỔNG QUAN Chương 1: Giới thiệu Eiffel 1.1 Giới thiệu 1.2 Design By Contract Eiffel 10 1.3 EiffelStudio 10 1.3.1 Giao diện 11 1.3.2 Các thao tác trên EiffelStudio 11 Chương 2: Một số chế mang lại tính đáng tin cậy cho phần mềm 17 Chương 3: Tính đúng đắn phần mềm 18 Chương 4: Biểu diễn đặc tả 20 4.1 Những công thức tính đúng đắn 20 4.2 Những điều kiện yếu và mạnh 22 Chương 5: Giới thiệu xác nhận văn phần mềm 24 Chương 6: Tiền điều kiện và hậu điều kiện 25 6.1 Lớp ngăn xếp 25 6.2 Tiền điều kiện 28 6.3 Hậu điều kiện 28 Chương 7: Giao ước cho tính đáng tin cậy phần mềm 7.1 7.2 7.3 29 Quyền lợi và nghĩa vụ 29 7.1.1 Những quyền lợi 30 7.1.2 Những nghĩa vụ 30 Nghệ thuật tin cậy phần mềm: kiểm tra ít hơn, bảo đảm nhiều 31 Những xác nhận không phải là chế kiểm tra đầu vào 33 (4) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Chương 8: Làm việc với xác nhận 35 8.1 Lớp stack 35 8.2 Mệnh lệnh và yêu cầu 38 8.3 Lưu ý cấu trúc rỗng 41 8.4 Thiết kế tiền điều kiện: tolerant hay demanding? 42 8.5 Một môđun tolerant 43 Chương 9: Những điều kiện bất biến lớp 47 9.1 Định nghĩa và ví dụ 48 9.2 Định dạng và các thuộc tính điều kiện bất biến lớp 49 9.3 Điều kiện bất biến thay đổi 51 9.4 Ai phải bảo quản điều kiện bất biến? 52 9.5 Vai trò điều kiện bất biến lớp kỹ thuật xây dựng phần mềm 9.6 53 Những điều kiện bất biến và hợp đồng Chương 10: Khi nào lớp là đúng? 54 56 10.1 Tính đúng đắn lớp 57 10.2 Vai trò thủ tục khởi tạo 60 10.3 Xem lại mảng 60 Chương 11: Kết nối với kiểu liệu trừu tượng 62 11.1 So sánh đặc tính lớp với hàm ADT 63 11.2 Biểu diễn tiên đề 64 11.3 Hàm trừu tượng 65 11.4 Cài đặt điều kiện bất biến 66 Chương 12: Một thị xác nhận 68 Chương 13: Vòng lặp có điều kiện bất biến và điều kiện biến đổi 71 13.1 Vấn đề vòng lặp 71 13.2 Những vòng lặp đúng 71 13.3 Những thành phần vòng lặp đúng 72 13.4 Cú pháp vòng lặp 74 (5) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Chương 14: Sử dụng xác nhận 77 14.1 Những xác nhận công cụ để viết phần mềm chính xác 14.2 Sử dụng xác nhận cho việc viết tài liệu: thể rút gọn lớp đối tượng 77 78 Chương 15: Giới thiệu công cụ XC# 81 15.1 Giới thiệu 81 15.2 XC# hoạt động nào 82 15.3 Khai báo các xác nhận 82 15.4 15.3.1 Tiền điều kiện 82 15.3.2 Hậu điều kiện 83 15.3.3 Một số thuộc tính mà XC# qui ước sẵn 83 Ví dụ lớp Stack 86 Chương 16: Kết thực nghiệm: công cụ DCS 88 16.1 Nguyên lý làm việc 88 16.2 Thiết kế 94 16.2.1 Tổng thể 94 16.2.2 Chi tiết các lớp đối tượng 95 16.2.2.1 Màn hình Configuration 95 16.2.2.2 Lớp Connect 98 16.2.2.3 Lớp ProjectInfo 99 16.2.2.4 Lớp ClassInfo 101 16.2.2.5 Lớp FunctionInfo 104 16.2.2.6 Lớp Assertion 106 16.2.2.7 Lớp Extra 109 KẾT LUẬN 111 HƯỚNG PHÁT TRIỂN 112 TÀI LIỆU THAM KHẢO 113 Ý KIẾN CỦA GIÁO VIÊN PHẢN BIỆN 114 (6) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# BẢNG CÁC HÌNH VẼ Hình 1-1: Giao diện EiffelStudio 11 Hình 1-2: Thông báo lỗi xảy tiền điều kiện 14 Hình 1-3: Code gây lỗi tiền điều kiện - 14 Hình 1-4: Thông báo lỗi xảy hậu điều kiện 15 Hình 1-5: Code gây lỗi hậu điều kiện - 15 Hình 1-6: Thông báo lỗi xảy điều kiện bất biến - 16 Hình 1-7: Code gây lỗi điều kiện bất biến 16 Hình 7-1: Sử dụng lọc các module 34 Hình 8-1: Stack cài đặt mảng - 35 Hình 9-1: Thời gian tồn đối tượng 50 Hình 10-1: Thời gian tồn đối tượng - 58 Hình 11-1: Sự biến đổi đối tượng trừu tượng và cụ thể 65 Hình 11-2: Hai cài đặt cùng đối tượng trừu tượng 67 Hình 13-1: Một vòng lặp tính toán 73 Hình 16-1: Sơ đồ thiết kế tổng thể 94 Hình 16-2: Màn hình Configuration 95 Hình 16-3: Chi tiết màn hình Configuration - 96 Hình 16-4: Lớp Connect 98 Hình 16-5: Lớp ProjectInfo 99 Hình 16-6: Lớp ClassInfo -101 Hình 16-7: Lớp FunctionInfo -104 Hình 16-8: Lớp Assertion -106 Hình 16-9: Lớp Extra 109 (7) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# LỜI NÓI ĐẦU Trong ngành công nghệ thông tin, thay đổi là tất yếu diễn thường xuyên mà ta phải chấp nhận và cố gắng điều chỉnh nó Phần mềm này đời thay phần mềm khác là điều vô cùng bình thường, dễ hiểu Tại lại thế? Bởi vì người sử dụng luôn mong muốn có phần mềm hữu ích Tuy nhiên, dù phần mềm có thể đáp ứng nhu cầu người sử dụng thời gian thì không thể đảm bảo nó luôn ưa chuộng Để có thể tồn lâu dài, phần mềm phải thật chất lượng Điều này đồng nghĩa với việc nó phải không ngừng cập nhật Mà ta biết, phần mềm càng đúng đắn, đáng tin cậy và rõ ràng bao nhiêu thì công việc nâng cấp và phát triển nó càng dễ dàng nhiêu Do đó, có thể nói, tiêu chí ngành công nghệ phần mềm mà thời đại nào, sản phẩm phần mềm nào hướng đến là tính đáng tin cậy và đúng đắn Xuất phát từ nhu cầu ấy, công nghệ Design By Contract đã đời nhằm giúp đảm bảo cho tính đáng tin cậy phần mềm Đó chính là lý mà chúng em đã chọn đề tài này Với mục đích tìm hiểu công nghệ Design By Contract cách khá kỹ lưỡng, chúng em đã tiếp cận nó các tài liệu lý thuyết qua các công cụ có khả hỗ trợ Design By Contract cho các ngôn ngữ lập trình đại Không dừng đó, chúng em còn xây dựng công cụ hỗ trợ công nghệ này cho C# với tên gọi là DCS (Design By Contract for C Sharp) Đối tượng và phạm vi nghiên cứu: ý tưởng chính Design By Contract là lập “hợp đồng” lớp đối tượng (supplier) và khách hàng (client) nó, tức là lớp đối tượng khác gọi đến các phương thức lớp này Những client này phải bảo đảm số điều kiện định gọi phương thức supplier gọi là tiền điều kiện (precondition); đáp lại, sau thực thi thủ tục, supplier phải đáp ứng số điều kiện tương ứng gọi là hậu điều kiện (postcondition) Những điều kiện hợp đồng kiểm tra trình biên dịch, và vi phạm nào phần mềm phát (8) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# TỔNG QUAN Các hướng nghiên cứu đã có số tác giả: - Bertrand Meyer, tác giả công nghệ Design By Contract và ngôn ngữ Eiffel, ngôn ngữ hỗ trợ hoàn toàn Design By Contract Vấn đề tồn tại: Bởi vì đây là ngôn ngữ lập trình chính tác giả Design By Contract tạo nên hỗ trợ đầy đủ và rõ ràng cho công nghệ này, vấn đề đây là ngôn ngữ Eiffel còn xa lạ với người lập trình dù đã đời gần 10 năm, ít người sử dụng ngôn ngữ này để phát triển phần mềm - ResolveCorp và eXtensible C# (XC#), Add-In hỗ trợ Design By Contract cho C# Đây là công cụ tốt, hỗ trợ đầy đủ Design By Contract cho C# Tuy nhiên, công cụ này sử dụng miễn phí vài DLL và source code không mở - Man Machine Systems và JMSAssert, công cụ hỗ trợ Design By Contract cho Java Đây là công cụ tốt Tuy nhiên, JMSAssert hỗ trợ biên dịch command line và sử dụng cho JDK từ 1.2 trở xuống, không thể tích hợp vào các môi trường hỗ trợ lập trình Java JBuilder, Sun One Studio hay Eclipse (9) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Chương 1: Giới thiệu Eiffel 1.1 Giới thiệu Đầu tiên, chúng ta làm quen với phần mềm Eiffel trước tìm hiểu công nghệ Design By Contract Vì lại vậy? Vì tất ví dụ dùng luận văn sử dụng cấu trúc ngôn ngữ Eiffel Còn khái niệm nào đề cập chương này giải thích kỹ các phần sau giới thiệu Design By Contract Qua 10 năm tồn tại, Eiffel coi là môi trường phát triển phần mềm tốt Trước sức mạnh to lớn Eiffel lĩnh vực phần mềm thì dù muốn dù không, bạn nên biết qua nó Vậy thực chất Eiffel là gì? Eiffel là khung làm việc trợ giúp cho việc suy nghĩ, thiết kế và thực thi phần mềm hướng đối tượng Eiffel là phương pháp, ngôn ngữ hỗ trợ mô tả cách hiệu và phát triển hệ thống có chất lượng Eiffel là ngôn ngữ thiết kế Vai trò Eiffel còn ngôn ngữ lập trình Những gì nó đem lại không giới hạn ngữ cảnh lập trình mà trải rộng khắp công việc phát triển phần mềm: phân tích, lên mô hình, viết đặc tả, thiết kế kiến trúc, thực hiện, bảo trì, làm tài liệu Eiffel là phương pháp Eiffel dẫn đường các nhà phân tích và nhà phát triển xuyên suốt tiến trình xây dựng phần mềm Phương pháp Eiffel tập trung yếu tố sản phẩm và chất lượng, với điểm nhấn: tính đáng tin cậy, tính tái sử dụng, tính mở rộng, tính khả dụng, tính bền vững (10) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# 1.2 Design By Contract Eiffel Eiffel hỗ trợ nhiều tính năng: tiếp cận hướng đối tượng hoàn thiện, khả giao tiếp bên ngoài (có thể giao tiếp với các ngôn ngữ C, C++, Java,…), hỗ trợ vòng đời phần mềm bao gồm việc phân tích, thiết kế, thực thi và bảo trì, hỗ trợ Design By Contract, viết xác nhận, quản lý ngoại lệ… Design By Contract là vấn đề luôn nhắc đến đề cập Eiffel Trong Eiffel, thành phần hệ thống có thể thực theo đặc tả tiên các thuộc tính trừu tượng nó, liên quan đến thao tác nội và giao tác nó với các thành phần khác Eiffel thực thi cách trực tiếp ý tưởng Design By Contract, phương pháp làm nâng cao tính đáng tin cậy phần mềm, cung cấp tảng cho việc đặc tả, làm tài liệu và kiểm nghiệm phần mềm, việc quản lý các ngoại lệ và cách sử dụng kế thừa thích hợp 1.3 EiffelStudio EiffelStudio là trình biên dịch Eiffel Ngoài ra, nó còn là IDE mạnh với tính độc như: công cụ công nghệ đảo tích hợp, máy phân tích mã nguồn định lượng Tùy vào nhu cầu mình, bạn có thể sử dụng EiffelStudio môi trường lập trình công cụ giúp mô hình hóa, xây dựng các mô tả hệ thống bao gồm các lớp trừu tượng mà không thực thi công cụ Diagram kết hợp khả để đạt đến hiệu cao 10 (11) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# 1.3.1 Giao diện Hình 1-1: Giao diện EiffelStudio Giao diện làm việc EiffelStudio có khung chính: Features, Class, Clusters, Context Để thuận tiện cho việc lập trình, các bạn có thể đóng bớt các khung cửa sổ Tất các khung cửa sổ này có thể đóng lại ngọai trừ Class 1.3.2 Các thao tác trên EiffelStudio Khởi động chương trình: Programs > EiffelStudio Version > EiffelStudio Chọn "Create a new project" > OK Class view là khung làm việc chính bạn Sau lập trình xong, bạn có thể biên dịch và cho chạy chương trình công cụ Compile (F7) Debug chương trình: F10, F11 Lưu project: File > Save 11 (12) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Biểu diễn Design By Contract Eiffel: Precondition: require boolean expressions Postcondition: ensure boolean expressions Class invariant: invariant boolean expressions Chỉ thị Check: check assertion_clause1 assertion_clause2 … assertion_clausen end Loop invariant, loop variant: from initialization until exit invariant inv variant 12 (13) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# var loop body end Demo: Project stack STACK_CLASS: lớp stack chính, chứa các định nghĩa các thao tác trên stack make: Hàm khởi tạo stack item: hàm lấy phần tử trên cùng stack get(t): hàm lấy phần tử thứ t empty: kiểm tra stack có rỗng full: kiểm tra stack có đầy put(x): thêm phần tử x vào stack remove: bỏ phần tử trên cùng stack TEST_CLASS: lớp chính(main), lớp gọi các hàm lớp STACK_CLASS Ta thử vài trường hợp cho thấy khả bắt lỗi Eiffel Lưu ý: Sau trường hợp hãy sửa lại code ban đầu thử tiếp trường hợp khác Mở tập tin test_class.e Chạy thử chương trình (F5) Chương trình khởi tạo stack gồm phần tử từ đến và xuất stack Stack xuất màn hình TH1: Lỗi xảy tiền điều kiện Sửa n:=8 thành n:=-8 Tại dòng if (n >= 0) then nhấn tổ hợp phím Ctrl-K Tại dòng end end if , nhấn tổ hợp phím Ctrl-K Recompile (Shift-F7) và cho chạy lại chương trình (F5) 13 (14) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Xuất thông báo ngoại lệ sau: Hình 1-2: Thông báo lỗi xảy tiền điều kiện và trỏ dừng lại câu lệnh Hình 1-3: Code gây lỗi tiền điều kiện Nguyên nhân: Khi bạn gọi thủ tục a.make(n), trước đó khởi tạo n là số âm (=-8), client không đảm bảo contract, nên thủ tục make lớp STACK_CLASS, thủ tục make kiểm tra không thỏa tiền điều kiện positive_capacity: n>=0, nó dừng lại và thông báo cho người lập trình biết TH2: Lỗi xảy hậu điều kiện Trong lớp TEST_CLASS, thủ tục make, sửa sau: 14 (15) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Capacity := n capacity := n-1 Recompile (Shift-F7) và cho chạy lại chương trình (F5) Xuất thông báo ngoại lệ sau: Hình 1-4: Thông báo lỗi xảy hậu điều kiện và trỏ dừng lại câu lệnh Hình 1-5: Code gây lỗi hậu điều kiện Nguyên nhân: Trước đó, ta gán capacity := n-1, hậu điều kiện lại yêu cầu capacity = n TH3: Lỗi xảy điều kiện bất biến Trong lớp TEST_CLASS, thủ tục make, thêm vào dòng sau: count:=-1 15 (16) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Chọn menu Project > Project Setting… Bỏ dấu check ensure Đánh dấu check invariant Hành động này nhằm bỏ qua chế độ kiểm lỗi hậu điều kiện Ở đây muốn minh họa cho việc phát lỗi điều kiện bất biến Recompile (Shift-F7) và cho chạy lại chương trình (F5) Xuất thông báo ngoại lệ sau: Hình 1-6: Thông báo lỗi xảy điều kiện bất biến và trỏ dừng lại câu lệnh Hình 1-7: Code gây lỗi điều kiện bất biến Nguyên nhân: Trước đó, ta gán count := -1, điều kiện bất biến yêu cầu count>=0 16 (17) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Chương 2: Một số chế mang lại tính đáng tin cậy cho phần mềm Trước hết, phải nói kỹ thuật định nghĩa thuộc tính đối tượng gần là có liên quan với cấu trúc hệ thống phần mềm Những kiến trúc đơn giản, riêng biệt và có khả mở rộng giúp chúng ta đảm bảo tính đáng tin cậy phần mềm dễ dàng so với cấu trúc vặn vẹo Đặc biệt, cố gắng giới hạn liên quan các môđun với đến mức tối thiểu là tiêu điểm cho việc thảo luận tính riêng biệt Điều này giúp ngăn chặn rủi ro thông thường tính đáng tin cậy, ví dụ biến toàn cục và việc định nghĩa chế liên lạc bị giới hạn, client và mối quan hệ kế thừa Nói đến chất lượng phần mềm thì không thể bỏ qua tính đáng tin cậy Chúng ta cố gắng giữ cho cấu trúc càng đơn giản càng tốt Tuy điều này chưa đủ đảm bảo cho tính đáng tin cậy phần mềm, dù sao, nó là điều kiện cần thiết Một điều kiện khác cần thiết là làm cho phần mềm chúng ta tối ưu và dễ đọc Văn phần mềm không viết lần mà nó còn phải đọc đọc lại và viết viết lại nhiều lần Sự sáng và tính đơn giản các câu chú thích là yêu cầu để nâng cao tính đáng tin cậy phần mềm Một vũ khí khác cần thiết là việc quản lý nhớ cách tự động, đặc biệt là thu gom rác (garbage collection) Bất kỳ hệ thống nào có khởi tạo và thao tác với cấu trúc liệu động mà lại thực thu hồi nhớ tay (tức là người lập trình điều khiển) nhớ không thu hồi thì thật là nguy hiểm Bộ thu gom rác không là xa xỉ mà nó là thành phần thiết yếu để mở rộng tính đáng tin cậy cho môi trường hướng đối tượng nào Một kỹ thuật khác mà có thể là thiết yếu mà có liên quan đến genericity là static typing Nếu không có luật thì chúng ta không kiểm soát lỗi xảy lúc run-time quá trình gõ code gây nên 17 (18) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Tóm lại, tất kỹ thuật này cung cấp tảng cần thiết để ta có cái nhìn gần hệ thống phần mềm đúng đắn và bền vững Chương 3: Tính đúng đắn phần mềm Giả sử có người đưa cho bạn chương trình C với 300 000 dòng lệnh và hỏi nó có đúng không Tôi nghĩ có khả bạn thấy khó và chí là không thể trả lời Tuy nhiên, là cố vấn viên, bạn hãy trả lời “Không” và sau đó tính giá thật cao vì có thể bạn đúng Thật sự, để có thể trả lời câu hỏi trên cách đúng nghĩa, bạn không cần phải lấy chương trình đó mà còn phải lấy lời diễn giải gì mà chương trình đó làm hay ta gọi chúng là đặc tả chương trình Có chú thích giống chẳng sao, dĩ nhiên, đó ta không để ý đến kích thước chương trình Ví dụ, câu lệnh x := y+1 không đúng không sai Vì đúng hay sai có ý nghĩa xét quan hệ nó với lời chú dẫn, tức là cái mà người ta mong đợi có sau thực câu lệnh hay ít thì là ảnh hưởng đến trạng thái các biến chương trình Do đó, câu lệnh trên đúng với đặc tả: “Điều này đảm bảo cho x và y có giá trị khác nhau” nó sai với đặc tả: “Điều này đảm bảo x có giá trị âm” (giả sử các thực thể có kiểu số nguyên Như vậy, x có thể có kết không âm sau gán Điều đó tùy thuộc vào giá trị y) Ví dụ này nhằm minh họa cho khái niệm “tính đúng đắn” trình bày bên dưới: 18 (19) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Tính đúng đắn phần mềm Tính đúng đắn là khái niệm quan hệ Một hệ thống phần mềm hay thành phần phần mềm thì không đúng không sai Nó đúng hay sai có liên quan với đặc tả nào đó Nói cách chính xác, ta không thảo luận thành phần phần mềm có đúng hay không, mà là thảo luận chúng có phù hợp với đặc tả chúng hay không Do đó, thuật ngữ “tính đúng đắn” không dùng cho thành phần phần mềm, mà nó dùng cho cặp, cặp bao gồm thành phần phần mềm và đặc tả Trong phần này, ta biết cách biểu diễn đặc tả thông qua xác nhận (assertion) để giúp ta xác nhận tính đúng đắn phần mềm Điều này cho thấy kết việc viết đặc tả là bước đầu tiên quan trọng để đảm bảo phần mềm thật đúng Việc viết xác nhận cùng lúc đúng là trước viết phần mềm mang lại lợi ích tuyệt vời sau: − Sản xuất phần mềm đúng với bắt đầu vì nó thiết kế đúng Ích lợi này đã Harlan D.Mills (một người khởi đầu đề xướng việc lập trình có cấu trúc “Structured Programming”) trình bày vào năm 1970 sách “How to write correct programs and know it” (có nghĩa là “Làm nào để viết chương trình đúng và biết nó đúng”) “Biết” đây có nghĩa là trang bị cho phần mềm đối số ta viết nó nhằm hiển thị tính đúng đắn nó − Có hiểu biết tốt vấn đề và cách giải cuối cùng nó − Việc thực các tài liệu cho phần mềm dễ dàng Chúng ta thấy phần sau xác nhận đóng vai trò trung tâm việc hướng đối tượng đến gần tài liệu 19 (20) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# − Cung cấp cho việc kiểm tra và debug hệ thống Trong phần còn lại chúng ta tìm hiểu ứng dụng này Trong C, C++ và số ngôn ngữ khác (dưới đạo Algol W), ta có thể viết câu lệnh đóng vai trò xác nhận để kiểm tra tình trạng nào đó có giữ trạng thái nào đó mong muốn hay không thực thi phần mềm, và chương trình không thực thi nó không thoả Mặc dù có thể làm gì mà ta muốn, việc làm tượng trưng cho phần nhỏ việc sử dụng lời xác nhận phương pháp hướng đối tượng Do đó, giống nhiều người phát triển phần mềm khác thì bạn quen với câu lệnh lại không thấy tranh toàn cảnh Hầu hết tất khái niệm bàn đây lạ với bạn Chương 4: Biểu diễn đặc tả Chúng ta có thể trở lại nhận xét trước với hình ảnh ký hiệu toán học đơn giản mượn từ lý thuyết việc kiểm tra chương trình hình thức và lý quý giá để lập luận tính đúng đắn các thành phần phần mềm 4.1 Những công thức tính đúng đắn Giả sử A thực vài thao tác (ví dụ A là câu lệnh hay thân thủ tục) Một công thức tính đúng đắn là cách biểu diễn theo dạng sau: {P} A {Q} Ý nghĩa công thức tính đúng đắn {P} A {Q} Bất kỳ thi hành nào A, bắt đầu trạng thái P thì kết thúc với trạng thái Q Những công thức tính đúng đắn (còn gọi là ba Hoare) là ký hiệu toán học, không phải là khái niệm lập trình; chúng không phải là 20 (21) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# số ngôn ngữ phần mềm mà thiết kế nhằm giúp cho việc thể thuộc tính các thành phần phần mềm Trong {P} A {Q}, A biểu thị cho thao tác, P và Q là thuộc tính thực thể khác có liên quan hay còn gọi là xác nhận (chúng ta định nghĩa từ “xác nhận” (“assertion”) cách chính xác phần sau) Trong hai xác nhận này, P gọi là tiền điều kiện (precondition) và Q gọi là hậu điều kiện (postcondition) Ví dụ, ta có công thức bình thường tính đúng đắn sau với giả sử x là số nguyên: {x>=9} x := x+5 {x>=13} Công thức tính đúng đắn sử dụng để đánh giá tính đúng đắn phần mềm Điều đó có nghĩa là tính đúng đắn xét đến nó gắn với đặc tả nào đó Như vậy, thảo luận tính đúng đắn phần mềm, ta không nói đến thành phần phần mềm riêng lẻ A, mà nó là ba bao gồm thành phần phần mềm A, tiền điều kiện P và hậu điều kiện Q Mục đích việc này là thiết lập kết cho công thức tính đúng đắn {P} A {Q} Trong ví dụ trên, số 13 hậu điều kiện không phải là lỗi in ấn hay gõ phím! Giả sử thực đúng phép tính trên số nguyên công thức trên: với điều kiện x>=9 là đúng trước câu lệnh, x>=13 đúng sau thực câu lệnh Tuy nhiên, ta thấy nhiều điều thú vị hơn: − Với tiền điều kiện vậy, hậu điều kiện hay phải là điều kiện mạnh nhất, và trường hợp này là x>=14 − Còn với hậu điều kiện đã đưa thì tiền điều kiện hay phải là tiền điều kiện yếu nhất, đây là x>=8 Từ công thức đã cho, ta luôn có thể có công thức khác cách mở rộng tiền điều kiện hay nới lỏng hậu điều kiện Bây giờ, ta cùng xem xét nhiều khái niệm “mạnh hơn” và “yếu hơn” là nào 21 (22) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# 4.2 Những điều kiện yếu và mạnh Một cách để xem xét đặc tả theo dạng {P} A {Q} là xem nó mô tả các công việc cho A Điều này giống có mục quảng cáo tuyển người trên báo đăng “Cần tuyển người có khả thực công việc A A có trạng thái bắt đầu là P, và sau A hoàn tất thì nó phải thỏa mãn Q” Giả sử, người bạn bạn kiếm việc và tình cờ đọc quảng cáo tương tự này, tất lương và lợi ích chúng nhau, có điều là chúng khác cái P và Q Cũng giống nhiều người, bạn bạn thì lười nhác, có thể nói rằng, muốn có công việc dễ Và hỏi ý kiến bạn là nên chọn công việc nào Trong trường hợp này, bạn khuyên anh nào? Trước hết, với P: bạn khuyên nên chọn công việc với tiền điều kiện yếu hay mạnh? Câu hỏi tương tự cho hậu điều kiện Q Bạn hãy suy nghĩ và chọn cho mình định trước xem câu trả lời phần Trước hết, ta nói tiền điều kiện Từ quan điểm người làm công tương lai, tức là người thực công việc A, tiền điều kiện P định nghĩa trường hợp mà ta phải thực công việc Do đó, P mạnh là tốt, vì P càng mạnh thì các trường hợp bạn phải thực A càng giới hạn Như vậy, P càng mạnh thì càng dễ cho người làm công Và tuyệt vời là kẻ làm công làm gì tức là ta là kẻ ăn không ngồi Điều này xảy công việc A định nghĩa bởi: Công thức {False} A {…} Trong trường hợp này, hậu điều kiện không cần thiết phải đề cập dù nó có là gì thì không có ảnh hưởng Nếu có bạn thấy mục tuyển người thì đừng công đọc hậu điều kiện mà hãy chớp lấy công việc đó 22 (23) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Tiền điều kiện False là xác nhận mạnh có thể vì nó không thỏa mãn trạng thái nào Bất yêu cầu nào để thực thi A không đúng, và lỗi không nằm trách nhiệm A mà là người yêu cầu hay khách hàng (client) nó đã không xem xét tiền điều kiện nào cần đến Dù cho A có làm hay không làm gì thì nó luôn đúng với đặc tả Còn với hậu điều kiện Q, tình trạng bị đảo ngược Một hậu điều kiện mạnh là tin xấu: nó bạn phải mang lại nhiều kết Q càng yếu, càng tốt cho người làm thuê Thực tế, công việc ăn không ngồi tốt nhì trên giới là công việc định nghĩa mà không chú ý đến tiền điều kiện: Công thức {…} A {True} Hậu điều kiện True là xác nhận yếu vì nó thỏa mãn trường hợp Khái niệm “mạnh hơn” hay “yếu hơn” định nghĩa cách hình thức từ logic: P1 gọi là mạnh P2, và P2 yếu P1 P1 bao hàm P2 và chúng không Khi lời xác nhận bao hàm True, và False bao hàm xác nhận thì thật là hợp lý để nói True là yếu và False là mạnh với tất xác nhận có thể Đến đây, có câu hỏi đặt ra: “Tại công thức là công việc tốt nhì trên giới?” Câu trả lời chính là điểm tinh tế phần định nghĩa ý nghĩa công thức {P}A{Q}: kết thúc Định nghĩa nói thực thi phải kết thúc tình trạng thoả Q miễn là bắt đầu nó thoả P Với công thức 1, không có trường hợp nào thoả P Do đó, A là gì không thành vấn đề, nó là đoạn code mà thi hành, chương trình bị rơi vào vòng lặp không điểm dừng hay là gây hỏng máy chẳng Vì dù A là gì thì đúng với đặc tả nó Tuy nhiên, với công thức 2, vấn đề là trạng thái kết thúc, nó không cần thoả mãn thuộc tính đặc biệt nào trạng thái kết thúc phải đảm bảo là có tồn 23 (24) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Những đọc giả mà đã quen với lý thuyết khoa học máy tính hay kỹ thuật chứng minh lập trình thấy công thức {P} A {Q} dùng đây ám toàn tính đúng đắn, bao gồm kết thúc việc phù hợp với đặc tả (Tính chất mà chương trình thoả mãn với đặc tả nó lúc kết thúc là phần tính đúng đắn) Nãy giờ, ta thảo luận xác nhận mạnh hay yếu là “tin tốt” hay “tin xấu” là dựa trên quan điểm người làm công tương lai Nếu bây thay đổi cách nhìn, đứng trên cương vị người chủ, ta thấy thứ thay đổi: tiền điều kiện yếu là tin tốt ta muốn các trường hợp thực thi công việc tăng lên, và hậu điều kiện mạnh là tốt ta muốn kết công việc thật có ý nghĩa Sự đảo ngược tiêu chuẩn này là đặc trưng việc thảo luận tính đúng đắn phần mềm, và xuất lại khái niệm chính luận văn này: hợp đồng khách hàng và môđun cung cấp là ràng buộc hai bên Để sản xuất phần mềm hiệu và đáng tin cậy thì cần phải có hợp đồng đại diện cho thoả hiệp tốt tất mối liên hệ nhà cung cấp và khách hàng Chương 5: Giới thiệu xác nhận văn phần mềm Để biểu diễn đặc tả, chúng ta tin cậy vào xác nhận Một xác nhận là biểu thức bao gồm thực thể phần mềm và phát biểu thuộc tính các thực thể này có thể thoả mãn giai đoạn xác định thực thi phần mềm Một xác nhận tiêu biểu có thể biểu diễn số nguyên có giá trị dương tham chiếu không phải rỗng Về mặt cú pháp, khái niệm xác nhận đơn giản là biểu thức logic Ví dụ như: 24 (25) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# n>0; x/=Void Lưu ý cách dùng dấu chấm phẩy (“;”) Ý nghĩa dấu chấm phẩy đây tương đương với phép and Dấu chấm phẩy có thể đặt phần khai báo và thị Khi mệnh đề xác nhận nằm trên dòng khác nhau, ta không cần dùng dấu chấm phẩy (xem có phép and mặc định các dòng liên tiếp) Những quy ước này giúp ta có thể nhận biết các thành phần riêng biệt xác nhận Trong thực tế, ta thường dán nhãn (label) cho thành phần này, ví dụ như: Positive: n>0 Not_void: x/=Void Các nhãn trên có vai trò định lúc thực thi xác nhận Tuy nhiên, việc sử dụng chúng đây là nhằm làm cho văn ta rõ ràng và tường minh Chương 6: Tiền điều kiện và hậu điều kiện Ứng dụng đầu tiên xác nhận là đặc tả ngữ nghĩa thủ tục Một thủ tục không là đoạn mã chương trình mà nó là cài đặt hàm nào đó từ đặc tả kiểu liệu trừu tượng, nó thực công việc hữu ích Việc biểu diễn công việc này cách chính xác là vô cùng cần thiết Ta có thể đặc tả công việc cần thực thi thủ tục xác nhận liên quan với nó là tiền điều kiện (preconditions) và hậu điều kiện (postconditions) Tiền điều kiện thuộc tính cần thoả mãn nào thủ tục gọi; còn hậu điều kiện thuộc tính chắn có sau thủ tục thực thi xong 6.1 Lớp ngăn xếp Một ví dụ giúp ta làm quen với cách sử dụng các xác nhận: 25 (26) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# class STACK [G] feature …Declaration of the features: count, empty, full, put, remove, item end Trước xem phần cài đặt, cần chú ý thủ tục đặc trưng thuộc tính ngữ nghĩa mạnh mẽ và độc lập với cách biểu diễn nó Ví dụ như: − remove và item thực thi có số phần tử lớn − put tăng số phần tử lên 1, remove giảm số phần tử Những thuộc tính là phần đặc tả kiểu liệu trừu tượng, người chưa sử dụng đến kiểu liệu trừu tượng phải hiểu Nhưng các cách tiếp cận thông thường việc xây dựng phần mềm, văn phần mềm thường không đề cập đến chúng Thông qua tiền điều kiện và hậu điều kiện thủ tục, ta có thể đưa chúng vào thành phần tường minh phần mềm Ta biểu diễn tiền điều kiện và hậu điều kiện là mệnh đề giới thiệu qua hai từ khóa require và ensure Lưu ý phần cài đặt thủ tục chừa trống, ta tìm hiểu phần sau indexing description: "Stacks: Cấu trúc liệu với quy tắc truy xuất LIFO” class STACK1 [G] feature – Access count: INTEGER Số phần tử Stack item: G is Phần tử trên cùng require not empty 26 (27) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# … end feature – Status report empty: BOOLEAN is Kiểm tra Stack rỗng? end full: BOOLEAN is Kiểm tra Stack đầy? … end feature Element change put (x: G) is Thêm phần tử x vào Stack require not full … ensure not empty item = x count = old count + end remove is Xóa phần tử trên cùng Stack require not empty … ensure not full 27 (28) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# count = old count – end end Cả mệnh đề require và ensure là tùy chọn Nếu có thì require phải nằm trước mệnh đề local Những phần giải thích chi tiết ý nghĩa tiền điều kiện và hậu điều kiện 6.2 Tiền điều kiện Tiền điều kiện biểu diễn ràng buộc mà thủ tục thực cách chính xác Ví dụ như: − put không gọi ngăn xếp đã đầy − remove và item không thực trên ngăn xếp rỗng Tiền điều kiện vào có hiệu lực đến tất lời gọi thủ tục, lớp và từ lớp liên quan Một hệ thống chính xác không thực thi lời gọi không thỏa mãn tiền điều kiện 6.3 Hậu điều kiện Hậu điều kiện biểu diễn thuộc tính trạng thái kết có sau thực thi thủ tục Ví dụ như: − Sau thủ tục put, ngăn xếp không thể rỗng, phần tử trên cùng là phần tử thêm vào, và số lượng phần tử tăng lên − Sau thủ tục remove, ngăn xếp không thể đầy, số phần tử giảm Sự có mặt mệnh đề hậu điều kiện thủ tục bảo đảm kết thủ tục thoả mãn các thuộc tính (giả sử thủ tục đã thỏa mãn tiền điều kiện để gọi) Một từ khóa đặc biệt, old, xuất hậu điều kiện put và remove dùng từ khóa này để biểu diễn thay đổi biến count Cú pháp: old e, đó e là 28 (29) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# biểu thức, thường là thuộc tính, biểu thị giá trị e đầu vào thủ tục Hậu điều kiện put có mệnh đề count = old count + để thể put, gọi đối tượng nào, tăng giá trị biến count đối tượng đó lên Chương 7: Giao ước cho tính đáng tin cậy phần mềm Định nghĩa tiền điều kiện và hậu điều kiện cho thủ tục là cách định nghĩa hợp đồng (contract) thủ tục và lớp gọi nó 7.1 Quyền lợi và nghĩa vụ Bằng cách kết hợp mệnh đề require pre và ensure post thủ tục r, lớp đối tượng “tuyên bố” với khách hàng nó: Nếu các bạn hứa gọi r thỏa mãn pre, tôi hứa trả kết thỏa mãn post Trong mối liên hệ người và người các công ty với nhau, hợp đồng là văn làm cho điều khoản mối quan hệ trở nên sáng, rõ ràng Thật đáng ngạc nhiên lĩnh vực phần mềm, nơi mà đúng đắn, rõ ràng có vai trò sống còn, ý tưởng hợp đồng này lại phải quá nhiều thời gian để thể mình Tiền điều kiện và hậu điều kiện mô tả hợp đồng thủ tục (đóng vai trò nhà cung cấp) và đối tượng gọi đến nó (vai trò khách hàng) Đặc tính quan trọng hợp đồng công việc người là đòi hỏi “nghĩa vụ” (obligation) và “quyền lợi” (right) cho bên – thường là nghĩa vụ bên này trở thành quyền lợi bên Điều này đúng hợp đồng các lớp đối tượng: 29 (30) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# − Tiền điều kiện ràng buộc khách hàng: nó định nghĩa điều kiện để lời gọi đến thủ tục trở nên hợp pháp Đây là nghĩa vụ khách hàng và là quyền lợi nhà cung cấp − Hậu điều kiện ràng buộc lớp đối tượng: nó định nghĩa điều kiện cần phải đảm bảo thủ tục trả Đây là quyền lợi khách hàng và là nghĩa vụ nhà cung cấp 7.1.1 Những quyền lợi − Đối với khách hàng, đó là đảm bảo thuộc tính phải có sau gọi thủ tục − Đối với nhà cung cấp, đó là đảm bảo tính chất phải thỏa mãn nơi nào thủ tục gọi 7.1.2 Những nghĩa vụ − Đối với khách hàng, đó là đáp ứng yêu cầu phát biểu tiền điều kiện − Đối với nhà cung cấp, đó là gì phải làm mà hậu điều kiện đã định Đây là hợp đồng cho thủ tục put ví dụ trên: Nghĩa vụ put Quyền lợi Kết hậu điều kiện: Khách hàng Đáp ứng tiền điều kiện: Chỉ gọi Thông tin ngăn xếp cập put(x) ngăn xếp chưa đầy nhật: không rỗng, x nằm trên cùng, count tăng Đáp ứng hậu điều kiện: Cập Nhà cung cấp nhật thông tin ngăn xếp: không rỗng, x nằm trên cùng, count tăng 30 Kết tiền điều kiện: Được bảo đảm là ngăn xếp chưa đầy put gọi (31) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# 7.2 Nghệ thuật tin cậy phần mềm: kiểm tra ít hơn, bảo đảm nhiều Mặc dù bạn có thể chưa để ý, nguyên tắc hợp đồng có xu hướng ngược lại kiến thức tổng quát đã công nhận ngành công nghiệp phần mềm; gặp nhiều phản ứng từ đầu, Design by Contract đã là số đóng góp chính vào tin cậy phần mềm và đạt tầm quan trọng xứng đáng Một quy tắc tương phản với quan sát đã nêu trên: tiền điều kiện là quyền lợi nhà cung cấp và biểu diễn góc bên phải bảng trên: phần khách hàng hợp đồng không thõa mãn, nói cách khác lời gọi không đáp ứng tiền điều kiện, sau đó lớp đối tượng không bao bọc hậu điều kiện Trong trường hợp này thủ tục có thể làm chuyện gì: trả giá trị bất kỳ, lặp vô hạn, không trả giá trị, chí làm hỏng thực thi cách nào đó Đây là trường hợp “khách hàng sai” Lợi ích đầu tiên thỏa thuận này là đơn giản hóa đáng kể phong cách lập trình Tiền điều kiện xem ràng buộc mà lời gọi đến thủ tục phải tuân theo, còn bạn là người phát triển lớp đối tượng, bạn giả sử ràng buộc này thỏa mãn viết thủ tục; bạn không cần phải kiểm tra ràng buộc đó thủ tục Xét hàm bậc sau: sqrt (x: REAL): REAL is Căn bậc x require x >= … end Bạn viết thuật toán tính bậc mà không cần quan tâm đến trường hợp x < 0, điều này đã kiểm tra tiền điều kiện và trở thành trách nhiệm khách hàng – lớp gọi tới hàm này Thực tế phương pháp Design by Contract còn xa Viết đoạn chương trình này vào sau 31 (32) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# if x < then “Handle the error, somehow” else “Proceed with normal square root computation” end không là không cần thiết mà là không thể chấp nhận Điều này có thể biểu diễn nguyên tắc: Nguyên tắc không dư thừa Không kiểm tra tiền điều kiện thân thủ tục trường hợp nào Nguyên tắc này đã ngược với chủ trương nhiều sách giáo khoa công nghệ phần mềm hay phương pháp lập trình, thường biết đến là phương pháp lập trình phòng thủ - với ý tưởng: để thu phần mềm đáng tin cậy, bạn nên thiết kế thành phần hệ thống cho nó có khả tự bảo vệ cao Thà dư còn thiếu, người không là quá cẩn thận tiếp xúc với người lạ Sự kiểm tra dư thừa có thể vô ích, ít là nó không gây tác hại Design By Contract theo hướng ngược lại: kiểm tra dư thừa có thể và thật gây tác hại Điều này nghe có vẻ lạ, người nghĩ kiểm tra dư thừa có thể là vô ích, không thể nào gây tác hại Tuy nhiên người có hiểu biết chưa sâu tính tin cậy phần mềm và tập trung vào phần riêng biệt phần mềm có suy nghĩ Nếu hạn chế tầm nhìn phạm vi hẹp thành phần cụ thể, ví dụ thủ tục sqrt trên, ta thấy thủ tục có vẻ chặt chẽ có phần kiểm tra thêm thân thủ tục Nhưng hệ thống không giới hạn thủ tục cụ thể mà nó bao gồm nhiều thủ tục nhiều lớp đối tượng khác Để đạt hệ thống 32 (33) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# đáng tin cậy, ta phải từ bỏ cái nhìn hạn chế để có tầm nhìn vĩ mô, bao hàm trên kiến trúc tổng thể Nếu ta có cái nhìn thì tính đơn giản trở thành tiêu chuẩn định Như ta đã nói từ đầu, phức tạp là kẻ thù chính chất lượng Khi quan tâm đến điều này, ta thấy kiểm tra dư thừa không còn là vô hại Thử hình dung hệ thống thông thường, với hàng ngàn thủ tục, kiểm tra dư thừa này trở thành phức tạp không cần thiết khổng lồ Nếu chúng ta bắt đầu đường này, có thể chắn điều là: không đạt hệ thống đáng tin cậy Với Design By Contract, bạn nhận điều kiện chắn và cần thiết để thực các chức liên hiệp khách hàng – nhà cung cấp (hay gọi là hợp đồng); và rõ ràng, điền kiện, cần xác định rõ trách nhiệm là ai: khách hàng hay nhà cung cấp Có thể có nhiều câu trả lời, theo chừng mực nào đó, điều này là phong cách thiết kế khác Nhưng bạn đã định thì bạn cần phải bám vào nó Nếu bạn có yêu cầu chính xác tiền điều kiện, xác định rõ yêu cầu là phần trách nhiệm khách hàng (những lời gọi đến thủ tục) thì không kiểm tra thân thủ tục; còn yêu cầu này không nằm tiền điều kiện thì thủ tục cần phải kiểm tra nó Đối với hệ thống phần mềm, dù lớn hay nhỏ, chất lượng riêng thành phần là chưa đủ Cái giá trị chính là bảo đảm tương tác hai thành phần phải có phân công rõ ràng quyền lợi và nghĩa vụ, tức là cần phải có hợp đồng 7.3 Những xác nhận không phải là chế kiểm tra đầu vào Để tránh hiểu lầm, lưu ý hợp đồng ta bàn là thủ tục (nhà cung cấp) và thủ tục khác (khách hàng – thủ tục gọi đến thủ tục đó) Chúng ta quan tâm đến mối quan hệ phần mềm – phần mềm, không 33 (34) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# phải là phần mềm – người hay phần mềm – ngoại cảnh Vì vậy, tiền điều kiện không quan tâm đến đúng sai liệu người dùng nhập vào, ví dụ xét thủ tục read_positive_integer đòi hỏi người dùng nhập vào số dương, tiền điều kiện: require input > không phải là kỹ thuật đáng tin cậy Trong trường hợp này không thể có thay cho điều kiện if … then thông thường Sự xác nhận không phải là giải pháp kiểm tra đúng đắn liệu nhập Tiêu chuẩn Modular Protection (bảo vệ tính môđun cho phần mềm) khuyến khích cần xác nhận tính hợp lệ liệu nhập từ các đối tượng ngoài hệ thống cho càng gần với mã nguồn càng tốt, có thể sử dụng “bộ lọc” cần thiết: Hình 7-1: Sử dụng lọc các module Để nhận liệu từ ngoài hệ thống (đường màu xanh nhạt), bạn không thể dựa vào tiền điều kiện Nhưng với môđun màu vàng, liệu phải thỏa mãn 34 (35) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# tiền điều kiện có thể vào môđun xử lý (màu xanh đậm) Vậy trường hợp này, ta có thể sử dụng xác nhận cho đường màu xanh đậm, thể tương tác phần mềm – phần mềm Và hậu điều kiện mà môđun nhập nhận phải phù hợp với tiền điều kiện thủ tục xử lý Chương 8: Làm việc với xác nhận Trong phần này chúng tìm hiểu chi tiết cách dùng tiền điều kiện và hậu điều kiện thông qua ví dụ Những vấn đề đơn giản và phức tạp xác nhận minh họa rõ ràng qua ví dụ đây 8.1 Lớp stack Lớp STACK trang bị xác nhận có dạng chung STACK1 Bây giờ, ta có thể nghĩ phiên đầy đủ cùng với cài đặt giải thích rõ ràng Để lớp có thể sử dụng trực tiếp được, ta phải chọn cài đặt Hãy xem cài đặt ngăn xếp cách dùng mảng Hình 8-1: Stack cài đặt mảng Mảng gọi là representation, có phạm vi từ đến capacity Thuộc tính count có kiểu số nguyên (integer) cho biết số phần tử ngăn xếp 35 (36) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Gọi a là đối tượng lớp này, phương thức a.put(x,i) gán giá trị x cho phần tử thứ i stack; để lấy giá trị phần tử thứ i, ta dùng phương thức a.item(i) a @ i Lưu ý phạm vi mảng là từ đến capacity, vì i phải nằm và capacity indexing description: “Stacks: Cấu trúc liệu với quy tắc truy xuất LIFO, và có độ lớn cố định.” class STACK2 [G] creation make feature Initialization make (n: INTEGER) is Cấp phát cho Stack độ lớn n phần tử require positive_capacity: n >= capacity := n !! representation make (1, capacity) ensure capacity_set: capacity = n array_allocated: representation /= Void stack_empty: empty end feature Access capacity: INTEGER Số phần tử tối đa Stack count: INTEGER Số phần tử Stack item: G is Phần tử trên cùng 36 (37) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# require not_empty: not empty i.e count > Result := representation @ count end feature – Status report empty: BOOLEAN is Kiểm tra Stack rỗng? Result := (count = 0) ensure empty_definition: Result = (count = 0) end full: BOOLEAN is Kiểm tra Stack đầy? Result := (count = capacity) ensure full_definition: Result = (count = capacity) end feature – Element change put (x: G) is Thêm phần tử x vào Stack require not_full: not full i.e count < capacity in this representation count := count + representation put (count, x) ensure not_empty: not empty added_to_top: item = x 37 (38) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# one_more_item: count = old count + in_top_array_entry: representation @ count = x end remove is Xóa phần tử trên cùng Stack require not_empty: not empty i.e count > count := count – ensure not_full: not full one_fewer: count = old count – end feature {NONE} Implementation representation: ARRAY [G] Mảng dùng để chứa các phần tử Stack invariant … Sẽ tìm hiểu phần sau end class STACK2 Phần biểu diễn lớp trên cho ta thấy đơn giản làm việc với xác nhận Ngoại trừ mệnh đề invariant còn thiếu bổ sung phần sau, chúng ta hãy cùng xem xét tỉ mỉ thuộc tính khác nó 8.2 Mệnh lệnh và yêu cầu Những xác nhận lớp STACK2 minh họa khái niệm mà ta đã có cái nhìn lướt qua chuyển tiếp từ kiểu liệu trừu tượng sang lớp: khác khung nhìn “imperative” và “applicative” Những xác nhận empty và full có thể làm bạn băn khoăn Xét thủ tục full lớp trên: 38 (39) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# full: BOOLEAN is Stack có đầy không? Result := (count = capacity) ensure full_definition: Result = (count = capacity) end Hậu điều kiện yêu cầu thực thể Result có giá trị luận lý với giá trị biểu thức count = capacity Điều đó có nghĩa là Result có giá trị true count với capacity và nó có giá trị false ngược lại Result = (count = capacity) (1) Bởi vì thân thủ tục đã gán Result := (count = capacity) (2) Vậy thì liệu hậu điều kiện đây có dư thừa không? Có khác biệt lớn (1) và (2), vì không có dư thừa (2) là câu lệnh, thể hành động, gán giá trị true hay false biểu thức count = capacity cho biến Result Trong đó, (1) là xác nhận, không làm gì hết Nó đặc tả thuộc tính trạng thái cuối cùng mong đợi Một câu lệnh, tức (2), thì mang tính chất lệnh, còn xác nhận, tức (1), thì mang tính chất mô tả Câu lệnh mô tả cho câu hỏi “như nào”, còn xác nhận mô tả cho câu hỏi “cái gì” Một câu lệnh là phần cài đặt, còn xác nhận là thành phần đặc tả Câu lệnh thì mang tính mệnh lệnh, bắt buộc, còn xác nhận thì mang tính yêu cầu Hai thuật ngữ này nhấn mạnh khác tin học và toán học − Những thao tác tin học có thể làm thay đổi trạng thái máy tính Những thị các ngôn ngữ lập trình thông thường là câu lệnh tác động trực tiếp đến máy tính 39 (40) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# − Những lý luận toán học không thể thay đổi gì Ví dụ ta lấy bậc hai thì số trước lấy và sau lấy Tóm lại, xác nhận là mô tả kết mong đợi, còn thị (thân vòng lặp) là lệnh cách nào đó đạt kết Ta không nhằm lẫn hai khái niệm này hai khái niệm “:=” và “=” Những người sử dụng lớp nào đó để tạo môđun riêng mình quan tâm đến các xác nhận là các thị Nguyên nhân gần giống dấu gán (:=) và dấu (=) là việc gán nhiều trường hợp là cách đơn giản để đạt đến ngang Cài đặt Result := (count = capacity) thật là ví dụ rõ ràng dễ nhầm lẫn Nhưng ví dụ cao thì khác đặc tả và cài đặt lớn hơn, ví dụ đơn giản là hàm tính số thực x có hậu điều kiện là abs(Result^2-x)<=tolerance (với abs là trị tuyệt đối, còn tolerance là dung sai) Các thị thân hàm có tầm quan trọng thấp vì chúng là cài đặt cho thuật toán chung việc tính bậc hai Thậm chí thủ tục put STACK2, có cùng đặc tả có thể có thực thi khác nhau, mặc dù khác biệt có thể là nhỏ Chẳng hạn thân thủ tục sau: if count=capacity then Result:=True else Result:=False end có thể đơn giản (nhờ quy tắc khởi tạo mặc định) thành: if count = capacity then Result:=True end Do đó, diện thành tố giống thủ tục và hậu điều kiện không phải là chứng dư thừa Nó là chứng tính bền vững cài đặt và đặc tả - có thể nói là tính đúng đắn Qua đây, ta thấy đặc tính xác nhận mà phát triển cao hơn: thích hợp chúng tác giả các lớp client, người mà chúng ta không nên thắc mắc đọc cài đặt thủ tục, là người cần mô tả trừu tượng vai trò thủ tục Ý tưởng này đưa đến ý niệm dạng thức ngắn gọn (short form) thảo luận sau 40 (41) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Vì nguyên nhân thực tiễn, ta cho phép các xác nhận bao hàm vài thành tố mệnh lệnh (các hàm) Để tóm tắt cho chương này, ta có bảng sau chia thành cột để thấy tương phản hạng mục các thành tố phần mềm: 8.3 Cài đặt Đặc tả Câu lệnh Biểu thức Như nào Cái gì Mệnh lệnh, bắt buộc Yêu cầu Ra lệnh Mô tả Lưu ý cấu trúc rỗng Tiền điều kiện thủ tục khởi tạo make lớp STACK1 yêu cầu lời chú giải Nó đưa n>=0, theo sau là stack rỗng Nếu n 0, make gọi thủ tục khởi tạo mảng, có tên là make, với đối số và cho khoảng tiệm cận và trên Đây không phải là lỗi, mà là xuất phát từ quy ước thủ tục khởi tạo ARRAY: dùng đối số đầu tiên lớn đối số thứ hai đưa mảng rỗng n có giá trị là 0, là đối số đầu tiên lớn đối số thứ hai mảng không sai mà đơn giản là stack hay mảng này rỗng Lỗi xảy có lời gọi muốn truy xuất đến phần tử cấu trúc, chẳng hạn lời gọi put cho stack hay item cho mảng, hai tiền điều kiện thủ tục này luôn luôn sai vì cấu trúc rỗng (“Khách hàng luôn luôn sai.”) Khi định nghĩa cấu trúc liệu nói chung stack hay mảng, bạn nên xác định trường hợp cấu trúc rỗng là có nghĩa hay không Trong số trường hợp thì không: Ví dụ: hầu hết định nghĩa khái niệm tree giả định có ít node (là node gốc) Nhưng trường hợp rỗng đưa không phải là không có khả hợp lý, mảng và stack, bạn nên lên kế hoạch thiết kế 41 (42) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# cấu trúc liệu bạn, thừa nhận lần khách hàng tạo thể rỗng thì không chấp nhận nó Một hệ thống ứng dụng, ví dụ cần stack có n phần tử, với n là tiệm cận trên số phần tử chứa stack, tính toán ứng dụng trước nó tạo stack Trong số lần chạy, số có thể có giá trị Đây không phải là lỗi mà nó là trường hợp vô cùng 8.4 Thiết kế tiền điều kiện: tolerant hay demanding? Ta có thể gắn điều kiện cho hai đối tác hợp đồng: khách hàng (client) hay nhà cung cấp (supplier) Có khả năng: − Nếu gán trách nhiệm cho khách hàng, điều kiện là phần tiền điều kiện − Nếu đặt nhà cung cấp, điều kiện này xuất cấu trúc if then mã lệnh Ta gọi trường hợp thứ là demanding và thứ hai là tolerant Lớp stack trên là minh hoạ cho kiểu demanding, phiên tolerant không có diện tiền điều kiện: remove is Xóa phần tử trên cùng Stack if empty then print ("Error: attempt to pop an empty stack") else count := count – end end Dùng kiểu nào thì tốt hơn? Thoáng nhìn thì tolerant có vẻ tốt (cả tính đáng tin cậy và tính tái sử dụng), có nhiều khách hàng mà có nhà cung cấp, tính tái sử dụng 42 (43) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# thể rõ Nhà cung cấp thực việc kiểm tra chung không cần khách hàng phải có kiểm tra riêng Nhưng chúng ta xem xét vấn đề gần hơn, lập luận trên không còn đúng Điều kiện đây mô tả gì cần thiết để thủ tục có thể làm công việc nó Trong ví dụ trên, stack là rỗng, thông báo lỗi đưa ra, điều này không thích hợp Bởi vì có khách hàng – mođun dùng stack ứng dụng cụ thể định việc xoá phần tử chuỗi rỗng trên có ý nghĩa gì 8.5 Một môđun tolerant Mặc dù ta đã thấy tolerant không phải là tiếp cận đúng, nên nghiên cứu xem lớp đối tượng trông nào ta định tiếp cận theo cách này indexing description: " Stacks: Cấu trúc liệu với quy tắc truy xuất LIFO, và có độ lớn cố định; phiên tolerant, đưa dòng lệnh kiểm tra vào thẳng mã nguồn." class STACK3 [G] creation make feature Initialization make (n: INTEGER) is Cấp phát cho stack độ lớn n phần tử n>0; Nếu không thì gán error = Negative_size Không có tiền điều kiện! if capacity >= then capacity := n !! representation.make (capacity) 43 (44) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# else error := Negative_size end ensure error_code_if_impossible: (n<0)=(error =Negative_size) no_error_if_possible: (n >= 0) = (error = 0) capacity_set_if_no_error:(error = 0) implies (capacity = n) allocated_if_no_error: (error=0) implies (representation/= Void) end feature Access item: G is là phần tử trên cùng stack (nếu stack không rỗng) Nếu Stack rỗng thì gán error = Underflow Không có tiền điều kiện! if not empty then check representation /= Void end Result := representation.item error := else error := Underflow Trong trường hợp này, Result có giá trị mặc định end ensure error_code_if_impossible: (old empty) = (error = Underflow) 44 (45) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# no_error_if_ possible: (not (old empty)) = (error = 0) end feature Status report empty: BOOLEAN is Số phần tử Stack Result := (capacity = 0) or else representation.empty end error: INTEGER Xác định lỗi full: BOOLEAN is Số phần tử Stack Result := (capacity = 0) or else representation.full end Overflow, Underflow, Negative_size: INTEGER is unique Những lỗi có thể xảy feature Element change put (x: G) is Thêm vào phần tử x; không thì gán giá trị cho error Không có tiền điều kiện! 45 (46) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# if full then error := Overflow else check representation /= Void end representation.put (x); error := end ensure error_code_if_impossible: (old full) = (error = Overflow) no_error_if_possible: (not old full) = (error = 0) not_empty_if_no_error: (error = 0) implies not empty added_to_top_if_no_error: (error = 0) implies item = x one_more_item_if_no_error: (error = 0) implies count = old count + end remove is Xóa phần tử trên cùng Stack; không thì gán giá trị cho error Không có tiền điều kiện! if empty then error := Underflow else check representation /= Void end representation.remove error := end 46 (47) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# ensure error_code_if_impossible: (old empty) = (error = Underflow) no_error_if_possible: (not old empty) = (error = 0) not_full_if_no_error: (error = 0) implies not full one_fewer_item_if_no_error: (error = 0) implies count = old count – end feature {NONE} – Cài đặt representation: STACK2 [G] Cài đặt stack (không bảo vệ) capacity: INTEGER Số phần tử tối đa stack end class STACK3 Ví dụ trên đã cho ta thấy nặng nề lớp dùng cách tiếp cận tolerant Đây là minh chứng cho thấy tolerant dẫn đến phần mềm phức tạp và không cần thiết Ngược lại, với demanding, theo tinh thần Design By Contract, giúp client phát lỗi tất các trường hợp theo cách tốt Chương 9: Những điều kiện bất biến lớp Tiền điều kiện và hậu điều kiện mô tả thuộc tính thủ tục riêng biệt Điều này cần thiết cho việc biểu diễn thuộc tính toàn cục thể (instance) lớp vì chúng phải lưu giữ tất thủ tục Những thuộc tính tạo nên điều kiện bất biến lớp (class invariant) 47 (48) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Chúng giữ thuộc tính ngữ nghĩa sâu và mô tả ràng buộc toàn vẹn lớp 9.1 Định nghĩa và ví dụ Xem lại phần cài đặt ngăn xếp (stack) cách sử dụng mảng mà không có bảo đảm (STACK2) class STACK2 [G] creation make feature … make, empty, full, item, put, remove … capacity: INTEGER count: INTEGER feature {NONE} –- Cài đặt representation: ARRAY [G] end Những thuộc tính lớp bao gồm: representation kiểu mảng, capacity và count kiểu số nguyên tạo nên stack tượng trưng Mặc dù tiền điều kiện và hậu điều kiện thủ tục đưa trước đây có thể biểu diễn vài thuộc tính ngữ nghĩa stack chúng thất bại việc biểu diễn tính quán các thuộc tính liên kết với Ví dụ, count luôn luôn có giá trị từ đến capacity: <= count; count <= capacity (điều này hàm ý capacity >= 0), và capacity là kích thước mảng: capacity = representation.capacity Một điều kiện bất biến lớp (class invariant) là xác nhận, biểu diễn ràng buộc quán chung dùng cho thể lớp Nó khác với tiền điều kiện và hậu điều kiện là cái mô tả cho thủ tục riêng biệt Sự xác nhận trên liên quan đến thuộc tính Những điều kiện bất biến có thể biểu diễn mối quan hệ ngữ nghĩa hàm với hay 48 (49) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# hàm với thuộc tính Ví dụ, điều kiện bất biến STACK2 có thể mô tả liên quan thuộc tính empty và count sau: empty = (count = 0) Trong ví dụ này, xác nhận điều kiện bất biến liên quan đến thuộc tính và hàm Nó không riêng là việc lặp lại xác nhận hậu điều kiện hàm (empty) Một xác nhận trở nên hữu ích nó có liên quan đến nhiều thuộc tính ví dụ trên nhiều hàm Tiếp theo, ta có ví dụ tiêu biểu khác Liên quan đến khái niệm tài khoản ngân hàng, ta giả sử có lớp là BANK_ACCOUNT có các đặc tính deposits_list, withdrawals_list và balance Lúc đó, điều kiện bất biến lớp này có thể là mệnh đề sau: consistent_balance: deposits_list.total – withdrawals_list.total = balance Hàm total cho biết giá trị tích lũy danh sách hoạt động (số tiền gửi hay số tiền rút) Ví dụ trên cho thấy tình trạng quán giá trị có thể truy cập thông qua các thuộc tính deposits_list, withdrawals_list và balance 9.2 Định dạng và các thuộc tính điều kiện bất biến lớp Về mặt cú pháp, điều kiện bất biến lớp là xác nhận, nằm phần invariant, sau phần feature và trước end class STACK4[G] creation …As in STACK2 feature As in STACK2 invariant count_non_negative: <= count count_bounded: count <= capacity 49 (50) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# consistent_with_array_size: capacity = representation.capacity empty_if_no_elements: empty = (count = 0) item_at_top: (count > 0) implies (representation item (count) = item) end Một điều kiện bất biến lớp C là xác nhận mà thể C thoả mãn tất thời điểm bền vững (stable times) Những thời điểm bền vững là thời điểm mà đây, thể lớp tình trạng có thể quan sát được: + Khi khởi tạo thể hiện, tức là sau thực thi !!a là !!a.make(…), a có kiểu là lớp C + Trước và sau yêu cầu thủ tục r lớp thông qua lời gọi a.r(…) Hình vẽ sau thời gian tồn đối tượng Hình 9-1: Thời gian tồn đối tượng 50 (51) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Vào lúc bắt đầu, tức là bên trái hình Hình 8-1, đối tượng không tồn Đối tượng sinh câu lệnh !!a hay !!a.make(…) là clone Sau đó, client sử dụng đối tượng thông qua các tham chiếu đến a có dạng a.f(…) với f là tính lớp sinh đối tượng Và từ đó, đối tượng tồn ít là việc thi hành kết thúc Điều kiện bất biến là thuộc tính đặc thù trạng thái biểu diễn ô vuông màu xám hình Hình 8-1 (Ví dụ: S1) Những thời điểm bền vững (stable times) hình trên là chỗ mà đối tượng có thể thấy từ bên ngoài, nghĩa là client có thể áp dụng tính nào đó cho nó, bao gồm: + Trạng thái kết việc tạo đối tượng (trong hình là S1) + Những trạng thái trước và sau client thực lời gọi có dạng a.some_routine(…) 9.3 Điều kiện bất biến thay đổi Tuy có tên là điều kiện bất biến nó không cần phải thoả mãn hết thời điểm mặc dù ví dụ STACK4, nó đúng sau khởi tạo Trong trường hợp tổng quát hơn, việc thủ tục g vào lúc ban đầu vì cố thực mục đích mình - tức là cố đạt hậu điều kiện – mà có thể làm hủy điều kiện bất biến quá trình này (cũng người, việc cố gắng làm cái gì đó hữu ích có thể phá vỡ trật tự đã thiết lập thứ); sau đó, giai đoạn thực thi tiếp theo, thủ tục này không vi phạm quá nhiều điều kiện bất biến vốn có là hoàn toàn chấp nhận Trong vài tình trạng tức thời, ví dụ tình trạng đánh dấu trên hình Hình 8-1, điều kiện bất biến không thể giữ Tuy nhiên, điều này có thể chấp nhận miễn là thủ tục thiết lập lại điều kiện bất biến trước kết thúc việc thực thi nó 51 (52) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# 9.4 Ai phải bảo quản điều kiện bất biến? Những lời gọi đủ điều kiện, có dạng là a.f(…), thực nhân danh cho client, là cái luôn phải bắt đầu và rời khỏi trạng thái thoả mãn điều kiện bất biến; không có quy định nào cho lời gọi không đủ điều kiện có dạng f(…) thực thi trực tiếp client phục vụ công cụ hỗ trợ cho việc tiến hành cái cần thiết lời gọi đủ điều kiện Do đó, việc bắt buộc trì điều kiện bất biến áp dụng cho tính xuất (export) ngoài là theo cách chung chung là có lựa chọn; tính bí mật mà không client nào sử dụng là tính mà không bị điều kiện bất biến nào ảnh hưởng Sau đây là quy định định nghĩa chính xác nào xác nhận coi là điều kiện bất biến đúng lớp: Quy định điều kiện bất biến Một xác nhận I là điều kiện bất biến đúng lớp C và nó thoả điều kiện: + E1: Mọi thủ tục khởi tạo C , áp dụng cho đối số thoả mãn tiền điều kiện nó trạng thái thuộc tính có giá trị mặc định, dẫn đến trạng thái thoả mãn I + E2: Mọi thủ tục export khỏi lớp, áp dụng cho đối số và trạng thái thoả mãn I lẫn tiền điều kiện thủ tục, dẫn đến trạng thái thoả mãn I Chú ý, luật này: + Mọi lớp coi có thủ tục khởi tạo, và định nghĩa là null nó không định tường minh 52 (53) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# + Trạng thái đối tượng định nghĩa tất trường nó (tức là giá trị các thuộc tính lớp ứng với thể cụ thể) + Tiền điều kiện thủ tục có thể liên quan đến trạng thái đầu tiên và đối số + Hậu điều kiện có thể liên quan đến trạng thái cuối, trạng thái đầu (thông qua khái niệm old) và trường hợp là hàm thì giá trị trả (nếu có) định nghĩa trước thực thể Result + Điều kiện bất biến có thể liên quan đến trạng thái Những xác nhận có thể là hàm hàm là tham chiếu gián tiếp đến các thuộc tính tức các trạng thái Ta có thể dùng quy định điều kiện bất biến sở để trả lời câu hỏi: “Sẽ có ý nghĩa gì điều kiện bất biến gây xâm phạm tiến trình thực thi hệ thống?” Trước đây, chúng ta đã thấy dấu hiệu xâm phạm tiền điều kiện là lỗi (một “bug”) khách hàng, còn xâm phạm hậu điều kiện là lỗi người cung cấp Thật ra, điều kiện bất biến là hậu điều kiện Chính thuộc tính này giúp ta biết gì nhận 9.5 Vai trò điều kiện bất biến lớp kỹ thuật xây dựng phần mềm Thuộc tính E2 chúng ta có thể xem điều kiện bất biến là phần thêm vào cách không tường minh cho tiền và hậu điều kiện thủ tục export ngoài Do đó, nguyên tắc, khái niệm điều kiện bất biến là không cần thiết vì không có nó ta làm việc tốt là việc mở rộng tiền và hậu điều kiện tất thủ tục lớp là Tuy nhiên, không phải Dù việc có thêm điều kiện bất biến làm phức tạp văn thủ tục, quan trọng hơn, ý nghĩa sâu xa điều kiện bất biến là nó vượt khỏi thủ tục riêng và áp dụng cho lớp Thật sự, điều kiện bất biến không dùng để phục vụ cho thủ tục viết lớp mà còn có thể sử dụng ta có nhu cầu thêm sau đó Vì vậy, điều kiện bất biến có 53 (54) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# thể kiểm soát phần mở rộng lớp Điều này thể rõ quy định việc kế thừa Trong phát triển phần mềm, thay đổi là điều tất yếu mà chúng ta phải chấp nhận, là ta phải cố gắng điều chỉnh nó Một vài lĩnh vực hệ thống phần mềm thành phần riêng biệt, lớp có thể thay đổi nhanh cái khác Đặc biệt, việc thêm vào, bớt hay thay đổi đặc tính là tượng thường xuyên và bình thường Trong quá trình dễ thay đổi này, việc bám mãi vào thuộc tính mặc dù nó có thể thay đổi gây khó khăn cho việc đảm bảo toàn hệ thống trì vĩnh viễn Nhưng với điều kiện bất biến, nó không gây khó khăn cho ta muốn thay đổi vì chúng giữ ràng buộc ngữ nghĩa áp dụng cho lớp Ví dụ STACK2 đã minh họa ý kiến bản, để đánh giá toàn ích lợi điều kiện bất biến thì ta phải theo dõi thêm ví dụ Khái niệm điều kiện bất biến là số khái niệm trội mà ta học từ phương pháp hướng đối tượng Chỉ nào ta kế thừa điều kiện bất biến (của lớp chính mình viết) hay đọc và hiểu nó (từ lớp người khác) thì ta có thể thực cảm nhận lớp đó là gì 9.6 Những điều kiện bất biến và hợp đồng Những điều kiện bất biến hiểu rõ đưa nó vào ngữ cảnh hợp đồng Những hợp đồng người với thường liên quan đến quy tắc chung áp dụng cho khế ước mục nào đó; ví dụ quy định việc phân chia vùng thành phố áp dụng cho tất hợp đồng xây dựng nhà Những điều kiện bất biến đóng vai trò hợp đồng phần mềm: điều kiện bất biến lớp ảnh hưởng đến tất hợp đồng thủ tục lớp và đối tượng sử dụng lớp đó Trong phần trên, ta xem điều kiện bất biến cái gì đó thêm vào cho tiền và hậu điều kiện thủ tục export ngoài Coi body là phần thân thủ tục (là tập hợp câu lệnh phần do), pre là tiền điều 54 (55) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# kiện, post là hậu điều kiện và INV là điều kiện bất biến lớp Khi ấy, yêu cầu tính đúng đắn thủ tục có thể biểu diễn thông qua khái niệm đã giới thiệu trước đây là: {INV and pre} body {INV and post} (Câu lệnh trên có nghĩa là: thi hành nào phần body, bắt đầu với trạng thái nào INV và pre thì kết thúc trạng thái thoả mãn INV và post) Như vậy, có câu hỏi dành cho người cung cấp tức người đã viết phần body: điều kiện bất biến là tin tốt hay xấu, nó làm cho công việc dễ hay khó hơn? Để trả lời điều này thì bạn phải hiểu ý nghĩa thảo luận đầu tiên ý nghĩa tiền và hậu điều kiện người làm công và người làm chủ Thực chất, điều kiện bất biến Nó có thể là tin tốt mà có thể là tin xấu Đối với người xin việc lười biếng, họ muốn có tiền điều kiện mạnh và hậu điều kiện yếu Ở đây, thêm INV vào làm cho tiền và hậu điều kiện mạnh ít là với ban đầu Cái này xuất phát từ quy tắc logic: a và b luôn luôn hàm ý a, điều này có thể nói là nó mạnh hay a Vì vậy, bạn chịu trách nhiệm thực phần body, thì điều kiện bất biến sẽ: + Làm cho công việc bạn dễ thêm vào tiền điều kiện pre vì trạng thái đầu tiên còn phải thỏa mãn thêm INV Như vậy, giới hạn trường hợp có thể gặp phải + Làm cho công việc bạn khó thêm vào hậu điều kiện chính thức post vì bạn phải bảo đảm trạng thái cuối còn phải thoả mãn thêm INV Những ý kiến này là đáng tin với cách nhìn điều kiện bất biến là tình trạng quán chung dùng cho lớp tổng thể Do đó, nó dùng cho tất các thủ tục lớp Khi là tác giả thủ tục thế, bạn thấy có lợi điều kiện này đúng vào lúc bắt đầu thủ tục, bạn phải bảo đảm 55 (56) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# thủ tục thoả mãn điều kiện này lần lúc kết thúc để thủ tục thực thi trên cùng đối tượng lại thấy việc này là có lợi lần Trong lớp BANK_ACCOUNT trên, mệnh đề điều kiện bất biến: deposits_list.total – withdrawals_list.total = balance là ví dụ tốt Nếu bạn phải thêm thủ tục vào lớp, mệnh đề này đảm bảo đặc tính deposits_list, withdrawals_list và balance có giá trị quán Vì vậy, bạn không cần kiểm tra thuộc tính này (và thế, sau xem, chúng ta không phải kiểm tra nó) Nhưng nó có nghĩa là bạn phải viết thủ tục, để mà, với cái gì khác nó làm, nó rời khỏi đối tượng trạng thái thỏa mãn thuộc tính này lần Do thế, thủ tục withdraw dùng để ghi thao tác rút tiền không cập nhật withdrawals_list mà nó phải cập nhật giá trị balance balance là thuộc tính để lấy số tiền gửi vào tài khoản và khôi phục lại điều kiện bất biến, bảo đảm thủ tục nào khác gọi sau đó cùng đối tượng có thể thi hành dễ dàng Không là thuộc tính mà balance còn có thể là hàm mà đó nó tính toán và trả giá trị của: deposits_list.total – withdrawals_list.total Trong trường hợp này, thủ tục withdraw không cần làm gì đặc biệt để có thể trì điều kiện bất biến Khả chuyển đổi hai khả này mà không làm ảnh hưởng đến khách hàng là minh họa nguyên tắc truy cập đồng (uniform access) Ví dụ này điều kiện bất biến lớp phương tiện vận chuyển phần mềm cách lịch thiệp bạn sử dụng loại tài nguyên chia sẻ thì bạn phải đưa nó lại cho người khác sau lần sử dụng Chương 10: Khi nào lớp là đúng? Phần này giúp chúng ta tiếp cận với lý thuyết Ngay là đọc lần đầu tiên thì bạn có thể tiếp cận với ý tưởng trình bày 56 (57) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# tới vì nó tập trung vào điểm chính yếu và quý báu sử dụng phương pháp kế thừa 10.1 Tính đúng đắn lớp Với tiền điều kiện, hậu điều kiện và điều kiện bất biến, ta có thể định nghĩa cách chính xác lớp đúng đắn là nào Thật ra, sở để định nghĩa tính đúng đắn lớp đã trình bày từ đầu luận văn: lớp, giống thành phần phần mềm nào khác, có thể đánh giá là đúng hay không đúng nó gắn liền với đặc tả nào đó Như vậy, tiền điều kiện, hậu điều kiện và điều kiện bất biến chính là thông tin mà ta có thể thêm vào phần đặc tả lớp Điều này cung cấp sở mà dựa vào đó, ta có thể đánh giá tính đúng đắn: lớp là đúng và cài đặt nó thân thường trình phù hợp với tiền điều kiện, hậu điều kiện và điều kiện bất biến Khái niệm {P}A{Q} giới thiệu từ đầu giúp cho việc biểu diễn điều này chính xác Hãy nhớ ý nghĩa công thức đúng đắn là: nào A thực thi trạng thái thỏa P thì thực thi này kết thúc trạng thái thỏa Q Coi C là lớp, INV là điều kiện bất biến lớp Với thường trình r lớp, gọi prer(xr) và postr(xr) là tiền điều kiện và hậu điều kiện nó, xr là tham số có thể r, mà tiền điều kiện và hậu điều kiện có thể trỏ đến (Nếu phần thường trình, tiền điều kiện hậu điều kiện thiếu thì prer postr là True) Gọi Bodyr là thân thường trình r Cuối cùng, DefaultC biểu diễn cho việc xác nhận thuộc tính C có giá trị mặc định cùng với kiểu chúng Ví dụ, DefaultSTACK2 nói đến lớp ngăn xếp trước là xác nhận: representation = Void capacity = count = 57 (58) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Những khái niệm này cho phép ta có định nghĩa chung tính đúng đắn lớp: Định nghĩa: tính đúng đắn lớp Một lớp là đúng với xác nhận nó và nếu: + C1: Với tham số hợp lệ xp nào thủ tục khởi tạo p thì: {DefaultC and prepp(xp)} Bodyp {postp(xp) and INV} + C2: Với thường trình r export ngoài và tham số hợp lệ xr nào thì: {prepr(xr) and INV} Bodyr {postr(xr) and INV} Quy định này thật là khai báo toán học lược đồ thông tin trước đây biểu diễn chu kỳ sống đối tượng điển hình Ta hãy cùng xem lại ví dụ BANK_ACCOUNT: Hình 10-1: Thời gian tồn đối tượng 58 (59) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Điều kiện C1 nghĩa là: thủ tục khởi tạo nào (ứng với hình Hình 9-1 trên là make), gọi với tiền điều kiện thỏa mãn nó thì phải trả trạng thái khởi đầu (trong hình trên là S1) thỏa mãn điều kiện bất biến và hậu điều kiện thủ tục đó Điều kiện C2 diễn tả thường trình r nào export (ứng với hình trên là f và g), gọi trạng thái (S1, S2 hay S3) thỏa mãn tiền điều kiện và điều kiện bất biến nó, thì phải kết thúc trạng thái thỏa mãn hậu điều kiện và điều kiện bất biến nó Như vậy, luật C1 là bước việc dẫn nhập, nói điều kiện bất biến giữ tất đối tượng sinh là kết trực tiếp câu lệnh khởi tạo Luật C2 là bước dẫn nhập mà thông qua đó, ta định có hệ thể nào đó thỏa mãn điều kiện bất biến thì hệ - tức là thể thu áp dụng đặc tính export thành viên hệ – thỏa mãn nó Việc đối tượng sinh và từ hệ này qua hệ khác thông qua đặc tính export, ta thu toàn thể có thể lớp cho phép ta định tất thể đó có thỏa mãn điều kiện bất biến hay không Có ý kiến mặt thực tiễn sau: + Nếu lớp không có mệnh đề creation, ta có thể xem nó có thủ tục khởi tạo không tường minh là nothing với thân rỗng Áp dụng luật C1 cho Bnothing với ý nghĩa là DefaultC phải bao gồm INV: giá trị mặc định phải thỏa mãn điều kiện bất biến + Một yêu cầu có dạng {P}A{Q} không thực thi A trường hợp nào mà P không thoả mãn lúc ban đầu Do đó, hợp đồng không bị ràng buộc với thường trình client không đáp ứng yêu cầu có liên quan với giao tác Theo đó, định nghĩa tính đúng đắn lớp không cho phép thường trình thực thi nó vi phạm tiền điều kiện hay điều kiện bất biến 59 (60) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# 10.2 Vai trò thủ tục khởi tạo Thảo luận điều kiện bất biến cho ta cái nhìn sâu khái niệm thủ tục khởi tạo Một điều kiện bất biến lớp biểu diễn thuộc tính mà đối tượng (những thể lớp) phải thỏa mãn thời điểm ổn định thời gian sống chúng Đặc biệt, thuộc tính này phải giữ trước thể khởi tạo Cơ cấu cấp phát đối tượng chuẩn khởi tạo trường với giá trị mặc định kiểu thuộc tính tương ứng; giá trị này có thể thỏa mãn hay không điều kiện bất biến Nếu không, thủ tục khởi tạo đặc biệt yêu cầu để gán giá trị cho thuộc tính này để chúng thoả mãn điều kiện bất biến Vì vậy, khởi tạo có thể xem là thao tác đảm bảo cho tất thể lớp bắt đầu sống chúng với chế độ đúng – tức là thỏa mãn điều kiện bất biến Việc biểu diễn đầu tiên thủ tục khởi tạo giới thiệu cách để trả lời cho câu hỏi tầm thường là: làm để viết đè quy luật khởi tạo mặc định chúng không thích hợp cho lớp đặc biệt nào đó, là trường hợp ta muốn cung cấp cho client nhiều cấu khởi tạo? Nhưng với giới thiệu điều kiện bất biến và thảo luận cách lý thuyết cùng với áp dụng luật C1 thì ta có thể kết luận vai trò sâu thủ tục khởi tạo là: chúng đảm bảo cho thể nào lớp bắt đầu thời gian sống phải thỏa mãn quy định thuộc lớp đó tức là thoả mãn điều kiện bất biến lớp đó 10.3 Xem lại mảng Thư viện lớp ARRAY đã tóm tắt chương trước Tuy nhiên, bây là lúc đưa định nghĩa chính xác cho nó Khái niệm mảng luôn đòi hỏi phải có tiền điều kiện, hậu điều kiện và điều kiện bất biến Đây là phác thảo cùng với xác nhận Những tiền điều kiện là cần thiết việc truy cập và điều chỉnh mảng với quy định là số mảng phải 60 (61) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# giới hạn cho phép Điều kiện bất biến biểu diễn mối quan hệ count, lower và upper; nó cho phép count cài đặt hàm không phải thuộc tính indexing description: "Mảng giá trị cùng kiểu, truy xuất các phần tử thông qua các số mảng" class ARRAY [G] creation make feature Khởi tạo make (minindex, maxindex: INTEGER) is Xác định biên mảng với minidex và maxindex Mảng rỗng minindex > maxindex require meaningful_bounds: maxindex >= minindex - … ensure exact_bounds_if_non_empty: (maxindex >= minindex) implies ((lower = minindex) and (upper = maxindex)) conventions_if_empty: (maxindex < minindex) implies ((lower = 1) and (upper = 0)) end feature – Truy cập lower, upper, count: INTEGER Chỉ số cao vào thấp hợp lệ; kích thước mảng infix "@", item (i: INTEGER): G is Giá trị mảng số i 61 (62) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# require index_not_too_small: lower <= i index_not_too_large: i <= upper … end feature -– Thay đổi thành phần put (v: G; i: INTEGER) is Gán giá trị v cho phần tử có số require index_not_too_small: lower <= i index_not_too_large: i <= upper … ensure element_replaced: item (i) = v end invariant consistent_count: count = upper – lower + non_negative_count: count >= end class ARRAY Phần trống còn lại là phần cài đặt thân thường trình item và put Chương 11: Kết nối với kiểu liệu trừu tượng Trong phần này, ta củng cố thêm khái niệm “xác nhận” việc tìm hiểu kết nối các xác nhận và thành phần đặc tả kiểu liệu trừu tượng (ADT – Abstract Data Type) Một ADT tạo thành phần: − Tên kiểu, có thể cùng với tham số chung (phần TYPES) 62 (63) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# − Danh sách các hàm với ký hiệu chúng (phần FUNCTIONS) − Tiên đề (AXIOMS) mô tả thuộc tính kết − Những quy định để sử dụng hàm (phần PRECONDITIONS) Ví dụ: ADT lớp STACK TYPES • STACK [G] FUNCTIONS • put: STACK [G] × G → STACK [G] • remove: STACK [G] →STACK [G] • item: STACK [G] → G • empty: STACK [G] → BOOLEAN • new: STACK [G] AXIOMS For any x: G, s: STACK [G] A1 • item (put (s, x)) = x A2 • remove (put (s, x)) = s A3 • empty (new) A4 • not empty (put (s, x)) PRECONDITIONS • remove (s: STACK [G]) require not empty (s) • item (s: STACK [G]) require not empty (s) 11.1 So sánh đặc tính lớp với hàm ADT Để hiểu mối liên hệ xác nhận và ADT, trước tiên ta cần tìm hiểu mối liên hệ đặc tính lớp và phần tương ứng ADT – hàm ADT Những hàm này gồm loại: creator, query và command 63 (64) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Sự phân loại hàm f: A × B × … Æ X phụ thuộc vào vị trí xuất ADT (A, B, …, X) so với mũi tên , gọi là T Nếu T xuất bên phải mũi tên thì f là hàm creator Trong lớp đối tượng, nó là phương thức khởi tạo Ví dụ: hàm new Nếu T có mặt bên trái mũi tên thì f là hàm query Trong lớp đối tượng, nó là phương thức truy xuất đến thuộc tính các thể lớp Ví dụ: hàm item và empty Nếu T xuất bên mũi tên thì f là hàm command Những hàm này tạo đối tượng từ đối tượng đã có Trong lớp đối tượng, hàm này thường biểu diễn phương thức để làm thay đổi đối tượng là tạo đối tượng Ví dụ hàm put và remove 11.2 Biểu diễn tiên đề Từ tương ứng hàm ADT và đặc tính lớp, ta có thể suy tương ứng ngữ nghĩa thuộc tính ADT và xác nhận Tiền điều kiện ADT xuất lại mệnh đề tiền điều kiện (precondition clauses) các thủ tục tương ứng Một tiên đề, bao gồm hàm command và hay nhiều hàm query, xuất lại mệnh đề hậu điều kiện (postcondition clauses) thủ tục tương ứng Những tiên đề bao gồm hàm query xuất lại hậu điều kiện hàm tương ứng mệnh đề điều kiện bất biến Tiên đề bao gồm hàm khởi tạo xuất lại hậu điều kiện thủ tục khởi tạo tương ứng 64 (65) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# 11.3 Hàm trừu tượng Hình 11-1: Sự biến đổi đối tượng trừu tượng và cụ thể A là ADT và C là lớp cài đặt từ A Lúc này tương ứng với hàm trừu tượng af A có hàm cụ thể cf lớp C Mũi tên a mô tả cho trừu tượng hóa hàm, với đối tượng cụ thể (thể lớp), trừu tượng hoá này sinh ta đối tượng trừu tượng (thể ADT) Những hàm trừu tượng này thường là phần Sự tương ứng lớp và ADT (cf ; a) = (a ; af) Trong đó dấu “;” là toán tử kết hợp các hàm Nói cách khác, ta có hai hàm f và g thì f;g là hàm h cho h(x) = g(f(x)) với x (f;g còn viết dạng g o f ) 65 (66) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Hai đường đứt khúc cùng đến đối tượng trừu tượng ABST_2 Kết bạn: - Áp dụng biến đối cụ thể cf, sau đó trừu tượng hóa kết quả, sinh a(cf(CONC_1)) - Trừu tượng hóa trước, sau đó áp dụng biến đổi trừu tượng af, sinh af(a(CONC_1)) 11.4 Cài đặt điều kiện bất biến Một số xác nhận xuất điều kiện bất biến chúng lại không là trực tiếp đặc tả ADT Những xác nhận này bao hàm thuộc tính, số là thuộc tính ẩn mà lúc định nghĩa thì chúng không có ý nghĩa ADT Một ví dụ đơn giản là thuộc tính đây xuất điều kiện bất biến lớp STACK4 count_non_negative: 0<=count count_bounded: count<=capacity Những xác nhận này là điều kiện bất biến lớp, gọi là cài đặt điều kiện bất biến (implementation invariant) Chúng đáp ứng cho việc biểu diễn tính vững đại diện chọn lớp (ở đây là count, capacity và representation) có quan hệ với ADT tương ứng Hình 10-1 đã giúp ta hiểu rõ cài đặt điều kiện bất biến Nó minh họa cho thuộc tính đặc trưng hàm trừu tượng a (biểu diễn mũi tên dọc) Gọi hàm a nối thành phần nguồn với nhiều thành phần đích Nếu ta theo chiều ngược lại mũi tên để xét nghịch đảo a (có thể gọi là quan hệ biểu diễn) , ta thấy đó không phải là hàm, vì có thể có nhiều biểu diễn đối tượng trừu tượng Xét mảng thể cho STACK thông qua cặp <representation, count>, STACK trừu tượng có nhiều biểu diễn khác nhau, tất có giá trị count và các phần tử từ đến count mảng representation giống nhau, giá trị capacity thể kích thước mảng có thể là giá trị nào lớn 66 (67) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# count, và phần tử có số vượt quá count có thể có giá trị tùy ý Quan hệ cài đặt thường không phải là hàm, nghịch đảo nó (mũi tên hướng lên) là hàm thật vì đối tượng cụ thể biểu diễn cho nhiều đối tượng trừu tượng Trong ví dụ lớp STACK chúng ta, cặp <representation,count> hợp lệ biểu diễn cho STACK trừu tượng Hình 11-2: Hai cài đặt cùng đối tượng trừu tượng Cả hai stack cụ thể trên đây là cài đặt (implementation) stack trừu tượng bao gồm phần tử 342, -133 và Việc a là hàm là yêu cầu chung: đối tượng cụ thể là cài đặt nhiều đối tượng trừu tượng, đại diện chọn trở nên mơ hồ và không thích hợp Vì mũi tên a nên vẽ theo chiều trên để mô tả cho kết nối kiểu cụ thể và trừu tượng 67 (68) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Cài đặt điều kiện bất biến là phần xác nhận không có đối chiếu đặc tả ADT Nó không liên hệ với ADT mà là với biểu diễn nó Nó định rõ đối tượng cụ thể thật là cài đặt (và một) đối tượng trừu tượng Chương 12: Một thị xác nhận Những tiền điều kiện, hậu điều kiện và điều kiện bất biến lớp là thành phần trung tâm phương pháp sử dụng thị xác nhận (assertion instruction) Chúng tạo nên kết nối việc xây dựng phần mềm hướng đối tượng và lý thuyết bên kiểu liệu trừu tượng (Abstract Data Type) Có xác nhận mặc dù ít đặc thù có vai trò quan trọng quy trình phát triển phần mềm Trong đó có thị check và vòng lặp có bất biến và điều kiện biến đổi (loop invariant và variant) Chỉ thị check sử dụng để thể tin người viết phần mềm thuộc tính nào đó thỏa mãn tình nào đó việc tính toán Cú pháp: check assertion_clause1 assertion_clause2 … assertion_clausen end Khi chương trình thực thi thì xác nhận assertion_clauseX bảo đảm Đây là cách để chắn lần thuộc tính định thỏa mãn, và quan trọng hơn, nó giúp người đọc phần mềm hiểu giả thuyết mà ta dựa vào Việc viết phần mềm đòi hỏi xác nhận thường xuyên thuộc tính đối tượng Xét ví dụ hàm sqrt(x), hàm nào gọi 68 (69) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# đến sqrt(x) dựa vào xác nhận x không âm Xác nhận này có thể hiển ngữ cảnh, ví dụ câu lệnh if then if x >= then y := sqrt (x) end nó có thể không trực tiếp trên, ví dụ x đã gán trước đó: x := a^2 + b^2 Chỉ thị check giúp biểu diễn xác nhận đó nó không hiển câu lệnh x := a ^2 + b^2 … Other instructions … check x >= Because x was computed above as a sum of squares end y := sqrt (x) Không cần điều kiện if then cho lời gọi hàm sqrt trường hợp này, check đã xác nhận lời gọi hàm là đúng Lưu ý thị check này không làm ảnh hưởng đến thuật toán thủ tục Ví dụ trên đã cho ta thấy hữu ích check, nó đóng vai trò tiền điều kiện lời gọi hàm Một trường hợp khác cần lưu ý, ta viết lời gọi hàm có dạng x.f, ta đã chắn x không rỗng, cho nên ta không viết if x != Void then không rỗng x lại không hiển ngữ cảnh này Ta đã gặp trường hợp này hàm put và remove lớp STACK3 Thân hàm put gọi đến hàm tương ứng lớp STACK2: 69 (70) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# if full then error := Overflow else check representation /= Void end representation.put (x); error := end Ở đây người đọc nghĩ không an toàn gọi representation.put(x); vậy, vì không có kiểm tra nào trước đó không rỗng representation Nhưng để ý kỹ, bạn thấy full sai thì capacity phải là số dương Vì vậy, representation không thể là rỗng Đây là điều quan trọng và là phần cài đặt các ràng buộc lớp Trong thực tế, với cài đặt đầy đủ ràng buộc định trước, ta nên viết thị check sau: check representation_exists: representation /= Void Because of clause representation_exists_if_not_full of the cài đặt điều kiện bất biến end Trong tiếp cận thông thường việc xây dựng phần mềm, mặc dù lời gọi hàm và phương thức khác thường tin tưởng vào đúng đắn chúng nhờ xác nhận khác nhau, chúng là xác nhận không tường minh Lập trình viên tự thuyết phục mình thuộc tính luôn luôn giữ thời điểm nào đó, và đưa phân tích này vào mã nguồn; sau thời gian, còn lại mã nguồn, không còn phân tích đó Vài tháng sau, người nào đó (có thể là chính tác giả), muốn tìm hiểu lại phần mềm, không thể biết thừa nhận không tường minh đó, và phải nhiều công sức để hình dung lại nó Chỉ thị check giúp ta tránh khỏi trường hợp đó 70 (71) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Chương 13: Vòng lặp có điều kiện bất biến và điều kiện biến đổi 13.1 Vấn đề vòng lặp Khả lặp lại tính toán với số lần tùy ý cách dễ dàng là khả vượt xa người máy tính Vì thế, vòng lặp có vai trò quan trọng ngôn ngữ lập trình Nhưng vòng lặp chứa đựng nhiều rủi ro vì khó khăn để có vòng lặp chính xác Những vấn đề hay gặp là: − Lỗi “off-by-one” (biểu diễn lặp lại quá nhiều hay quá ít) − Kiểm soát không tốt biên, ví dụ vòng lặp làm việc tốt trên mảng nhiều phần tử lại bị lỗi mảng rỗng hay có phần tử) − Vòng lặp vô tận 13.2 Những vòng lặp đúng Việc sử dụng khôn ngoan xác nhận giúp ta giải vấn đề này Một vòng lặp có thể có xác nhận liên kết, gọi là vòng lặp có điều kiện bất biến (loop invariant) Cũng có khái niệm “loop variant (vòng lặp có điều kiện biến đổi)” Đây không phải là xác nhận mà là biểu thức nguyên Hai khái niệm này giúp ta bảo đảm chính xác vòng lặp Xét ví dụ tính giá trị lớn mảng số nguyên: maxarray (t: ARRAY [INTEGER]): INTEGER is Giá trị lớn mảng t require t.capacity >= local i: INTEGER from i := t.lower 71 (72) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Result := t @ lower until i = t.upper loop i := i + Result := Result.max (t @ i) end end Ta thấy đây ta có thể gán i := t.lower và Result := t@lower mà không cần bận tâm đến việc mảng t vị trí lower có phần tử nào không, có điều này là nhờ vào việc sử dụng xác nhận, chính là tiền điều kiện: require t.capacity >= Đặc điểm điều kiện bất biến này là lần lặp, Result luôn là giá trị lớn phần mảng số nguyên đã xét 13.3 Những thành phần vòng lặp đúng Ví dụ trên minh họa cho lược đồ tổng quát việc tính toán vòng lặp Bạn đã xác định lời giải cho vấn đề là phần tử mặt phẳng n chiều POST, vì việc giải vấn đề chính là việc tìm phần tử POST Trong số trường hợp, POST có phần tử (chính là lời giải), tổng quát thường có nhiều lời giải thích hợp Những vòng lặp trở nên hữu ích bạn không có cách nào làm việc trực tiếp với POST lại thấy đường gián tiếp: đầu tiên là nhắm vào mặt phẳng m chiều INV chứa POST (m > n); sau đó tiếp cận POST, lặp lặp lại bám vào INV Xem hình minh họa đây: 72 (73) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Hình 13-1: Một vòng lặp tính toán Một tính toán vòng lặp gồm thành phần sau: Mục tiêu post, là hậu điều kiện, định nghĩa là thuộc tính mà trạng thái cuối nào tính toán phải thỏa mản Ví dụ như: “Result là giá trị lớn mảng” Mục tiêu này biểu diễn hình minh họa là tập hợp trạng thái POST thỏa mãn post Điều kiện bất biến inv, là tổng quát hóa mục tiêu (post là trường hợp đặc biệt inv) Ví dụ: “Result là giá trị lớn phần mảng không rỗng biên thấp nhất” Điều kiện bất biến biểu diễn hình minh họa là tập hợp trạng thái INV thỏa mãn inv Điểm khởi động init thuộc INV, điểm này thỏa mãn điều kiện bất biến Ví dụ: giá trị i là biên mảng và giá trị Result là phần tử mảng tương ứng vị trí đó, thỏa mãn điều kiện bất biến vì phần tử lớn mảng phần tử chính là phần tử đó 73 (74) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Sự biến đổi body (bắt đầu từ điểm INV không phải POST) phát sinh điểm tiến đến gần POST thuộc INV Trong ví dụ trên, biến đổi này mở rộng mảng lên phần tử và thay Result phần tử thêm đó nó lớn Result Thân vòng lặp hàm maxarray ví dụ trên là cài đặt biến đổi này Biên trên dựa vào số body cần thiết để đưa điểm INV đến POST Đây là biến Tính toán xấp xỉ là phương pháp chính toán giải tích, ý tưởng này thì áp dụng rộng rãi Sự khác biệt là toán học túy, ta chấp nhận có tồn giới hạn, mặc dù không thể đạt giới hạn đó Ví dụ 1/n có giới hạn là không có n cụ thể nào để đạt giới hạn đó Còn tin học, chúng ta cần có kết cụ thể, cho nên chúng ta phải nhấn mạnh tất các xấp xỉ tiến đến kết cụ thể sau số lần lặp lặp lại định 13.4 Cú pháp vòng lặp − Gồm thành phần sau: − Điều kiện bất biến (invariant) vòng lặp inv – đây là xác nhận − Điều kiện thoát khỏi vòng lặp exit − Điều kiện biến đổi (variant) vòng lặp var – đây là biểu thức nguyên − Tập lệnh khởi tạo init, tập lệnh này luôn tạo trạng thái thỏa inv và làm var không âm − Tập lệnh body, tập lệnh này, khởi đầu trạng thái mà inv giữ và var không âm, bảo vệ điều kiện bất biến và làm điều kiện biến đổi giảm (nhưng bảo đảm không âm) Vì vậy, trạng thái kết quả, inv thỏa và var có giá trị nhỏ trước (nhưng luôn không âm) Tóm lại, vòng lặp có cú pháp sau: 74 (75) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# from init invariant inv variant var until exit loop body end Đây là phiên vòng lặp cho hàm maxarray: from i := t.lower; Result := t @ lower invariant Result là giá trị lớn t số t.lower = i variant t.lower – i until i = t.upper loop i := i + Result := Result.max (t @ i) end Dưới đây là ví dụ khác điều kiện bất biến, đây là hàm tính ước chung lớn (greatest common divisor - gcd) số nguyên dương a, b thuật toán Euclid Phiên không có không có điều kiện bất biến và điều kiện biến đổi: 75 (76) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# gcd (a, b: INTEGER): INTEGER is Uớc chung lớn a và b require a > 0; b > local x, y: INTEGER from x := a; y := b until x = y loop if x>y then x:=x–y else y:=y–x end end Result := x ensure Result là ước chung lớn a và b end Làm chắn hàm gcd trả đúng giá trị ước chung lớn a và b Có cách để kiểm tra điều này, lưu ý thuộc tính này luôn đúng suốt quá trình lặp: x > 0; y > Cặp x, y có cùng ước chung lớn với cặp a, b Đây chính là điều kiện bất biến mà ta cần cho hàm này Làm chúng ta biết vòng lặp này luôn luôn kết thúc? Chúng ta cần điều kiện biến đổi để xác định điều này Không thể chọn x làm điều kiện biến đổi vì ta không bước lặp tuỳ ý làm giảm x, vì lý đó, y không thể chọn Nhưng ta x y giảm giá trị, vì giá trị lớn chúng (được biểu diễn hàm x.max(y)) là lựa chọn tốt Ta có phiên có điều kiện bất biến và điều kiện biến đổi vòng lặp cho hàm gcd: 76 (77) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# from x := a; y := b invariant x > 0; y > Cặp <x, y> có cùng UCLN với cặp <a, b> variant x.max (y) until x = y loop if x > y then x := x – y else y := y – x end end Chương 14: Sử dụng xác nhận Xem qua tất các cấu trúc có liên quan đến xác nhận, ta thấy có loại ứng dụng chính liên quan đến xác nhận: − Trợ giúp cho việc viết phần mềm chính xác − Trợ giúp việc viết tài liệu − Hỗ trợ kiểm tra (testing), chạy bước (debugging) và đảm bảo chất lượng − Hỗ trợ khả chịu lỗi phần mềm Chỉ có ứng dụng cuối có khả kiểm tra xác nhận lúc thực thi 14.1 Những xác nhận công cụ để viết phần mềm chính xác Ứng dụng đầu tiên này hoàn toàn mang tính phương pháp và có vai trò quan trọng Điều này đã làm rõ phần trước: giải thích rõ ràng yêu cầu chính xác trên thủ tục, thuộc tính tổng quát lớp đối 77 (78) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# tượng và vòng lặp, giúp cho người lập trình có thể tạo phần mềm chính xác từ lần đầu tiên (khác với cách tiếp cận khác, cố gắng sửa lỗi đạt tính đúng đắn) Từ khóa xuyên suốt đây là Design By Contract Trong giới thực, hợp đồng tốt là hợp đồng định rõ ràng quyền lợi và nghĩa vụ bên tham gia, và giới hạn quyền và nghĩa vụ này Trong thiết kế phần mềm, tính đúng đắn và tính vững vô cùng quan trọng, vì ta cần rõ điều khoản hợp đồng là điều kiện tiên trước hợp đồng có hiệu lực Những xác nhận cung cấp phương tiện nhằm rõ điều mong đợi và bảo đảm cho đối tác ký kết này 14.2 Sử dụng xác nhận cho việc viết tài liệu: thể rút gọn lớp đối tượng Ứng dụng thứ hai này cần thiết cho việc sản xuất thành phần phần mềm có tính tái sử dụng cao, và tổng quát hơn, việc tổ chức interface môđun hệ thống lớn Tiền điều kiện, hậu điều kiện và điều kiện bất biến lớp cung cấp cho khách hàng tiềm môđun thông tin dịch vụ cung cấp môđun đó Những thông tin này biểu diễn hình thức ngắn gọn và chính xác Không tài liệu dài dòng nào có thể thay tập xác nhận dùng để biểu diễn xuất chính phần mềm Thể rút gọn này sử dụng xác nhận là thành phần quan trọng việc trích thông tin thích hợp cho khách hàng tiềm từ lớp đối tượng Thể rút gọn này bao gồm thông tin có ích cho tác giả lớp client, vì nó không cho thấy đặc tính ẩn cài đặt lớp (mệnh đề do) Nhưng thể rút gọn này giữ lại xác nhận, chúng cung cấp tài liệu cần thiết hợp đồng mà lớp này quy định với client Thể rút gọn lớp STACK4 78 (79) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# indexing description: " Stacks: Cấu trúc liệu với quy tắc truy xuất LIFO, và có độ lớn cố định." class interface STACK4 [G] creation make feature Khởi tạo make (n: INTEGER) is Cấp phát cho stack độ lớn n phần tử require non_negative_capacity: n >= ensure capacity_set: capacity = n end feature -– Truy cập capacity: INTEGER Số phần tử tối đa stack count: INTEGER Số phần tử stack item: G is Phần tử trên cùng require not_empty: not empty –- i.e: count > end feature -– Báo cáo tình trạng empty: BOOLEAN is Kiểm tra stack rỗng? ensure empty_definition: Result = (count = 0) end 79 (80) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# full: BOOLEAN is Kiểm tra stack đầy? ensure full_definition: Result = (count = capacity) end feature –- Thay đổi thành phần put (x: G) is Thêm phần tử x vào stack require not_full: not full ensure not_empty: not empty added_to_top: item = x one_more_item: count = old count + end remove is Xóa phần tử trên cùng stack require not_empty: not empty –- i.e: count > ensure not_full: not full one_fewer: count = old count – end invariant count_non_negative: <= count count_bounded: count <= capacity empty_if_no_elements: empty = (count = 0) end class interface STACK4 80 (81) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Thể rút gọn này không phải là mã chương trình đúng cú pháp Nếu ta so sánh xác nhận thể rút gọn này với xác nhận lớp đối tượng, ta thấy tất mệnh đề bao gồm representation biến Thể rút gọn này gây chú ý đặc biệt lý sau: − Tài liệu này có mức độ trừu tượng so với gì chúng mô tả, đây là yêu cầu chủ yếu để có tài liệu có chất lượng Những cài đặt thực tế (trả lời câu hỏi nào) loại bỏ, xác nhận (trả lời câu hỏi cái gì – sao) giữ lại Lưu ý ghi chú đầu thủ tục (để bổ sung cho xác nhận giải thích sơ lược mục đích thủ tục) và mô tả mệnh đề clause giữ lại − Với thể rút gọn này, tài liệu không còn xem sản phẩm riêng biệt mà bao gồm chính phần mềm Điều này có nghĩa là có sản phẩm để bảo trì Và vì vậy, tài liệu chắn đúng đắn hơn, vì bạn không quên việc cập nhật tài liệu thay đổi phần mềm hay ngược lại − Thể rút gọn này có thể phát sinh từ lớp đối tượng công cụ tự động Chương 15: Giới thiệu công cụ XC# 15.1 Giới thiệu XC# là phần mở rộng trình biên dịch C# Luôn biên dịch sau trình biên dịch C# Có khả kiểm tra mã nguồn và đưa các cảnh báo lỗi có Cho phép người lập trình tự định nghĩa các ràng buộc, luật riêng mình Cho phép thêm hay loại bỏ các các khai báo tiền tố, hậu tố, hữu dụng cho việc debug chương trình 81 (82) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# 15.2 XC# hoạt động nào XC# bao gồm các component thực các tác vụ biên dịch, đưa các cảnh báo lỗi, và số tác vụ khác XCSharp.Compiler.dll đây là trình biên dịch XC# XCSharp.Parser.dll đây là phân tích cú pháp XC#, nó tạo CodeDom ASTs từ mã nguồn XCSharp.Interface.dll tạo tương tác trình biên dịch và các thuộc tính XCSharp.Attributes.dll and XCSharp.Verifier.dll nắm giữ toàn tập các thuộc tính (như obfuscation, declarative assertions, verification, etc.) XCSharp.VisualStudio.dll nhận dạng và biên dịch Visual Studio solutions XCSharp.AddIn.dll quản lý việc Add_in Visual Studio xcsc.exe biên dịch dòng lệnh XC# Trình biên dịch thông thường MS C# lấy liệu đầu vào là tập các file mã nguồn và đầu là mã thực thi Tuy nhiên, cách này còn hạn chế là không thể hiệu chỉnh các cư xử bên quá trình biên dịch XC# cho phép can thiệp vào bên quá trình biên dịch cách khai báo các câu lệnh, các luật mã nguồn Điều này cho phép trình biên dịch có thể đưa các cảnh báo và thông báo lỗi theo các ràng buộc đã định nghĩa 15.3 Khai báo các xác nhận Như đã mô tả trên, khai báo các xác nhận cho phép can thiệp vào bên quá trình biên dịch, đưa các cảnh báo lỗi… Các xác nhận có thể là tiền điều kiện hay hậu điều kiện 15.3.1 − Tiền điều kiện Kiểm tra điều kiện ban đầu phương thức 82 (83) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Thuộc tính Requires giúp xác định điều kiện ban đầu phương − thức Đối Requires đơn giản là đoạn text mô tả điều kiện ban đầu phương thức Ví dụ : [Requries (“obj != null”)] public void SetData(Object obj) { … } 15.3.2 Hậu điều kiện − Kiểm tra điều kiện kết thúc phương thức − Thuộc tính Ensures giúp xác định điều kiện kết thúc phương thức Đối Ensures là đoạn text mô tả điều kết thúc phương thức Ví dụ : [Ensures (“result != null”)] public Object GetData() { Object obj; ……… rerurn obj; } 15.3.3 Tên Một số thuộc tính mà XC# qui ước sẵn Mô tả Sử dụng Yêu cầu đối NotNull Void SetData([NotNull] Object tượng nhập vào obj) {} hay trả phải //obj != null “not null” Equal Yêu cầu đối Void SetData([Equal (5)] int 83 (84) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# tượng nhập vào value) {} trả phải //value == “equal” với giá trị cho trước Yêu cầu đối tượng nhập vào ExclusiveBetween Void SetData([ExclusiveBetween trả phải (1, 5)] int value) {} nẳm //1 <= value <= khoảng cho trước Yêu cầu đối tượng nhập vào GreaterThan Void SetData([GraeterThan (5)] trả phải int value) {} lớn với //value > giá trị cho trước Yêu cầu đối tượng nhập vào GreaterThanOrEqual Void trả phải SetData([GraeterThanOrEqual lớn hau (5)] int value) {} với giá //value >= trị cho trước Yêu cầu đối tượng nhập vào Is Void SetData([Is (typeof trả phải (HOCSINH))] Object obj) {} “Is” giá trị //kiểu obj phải là HOCSINH cho trước Yêu cầu đối LessThan Void SetData([LessThan (5)] int tượng nhập vào value) {} trả phải //value < 84 (85) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# nhỏ giá trị cho trước Yêu cầu đối tượng nhập vào LessThanOrEqual Void trả phải SetData([LessThanOrEqual (5)] nhỏ int value) {} giá trị //value <= cho trước Yêu cầu số thành phần đối tượng nhập MaxCount Void SetData([MaxCount (10)] vào trả ArrayList arr) {} phải nhỏ //arr.Count <= 10 giá trị cho trước Quy ước chiều dài tối đa MaxLength Void SetData([MinLength (10)] chuỗi là String str) {} giá trị cho //str.Length <= 10 trước Yêu cầu số thành phần Void SetData([MinCount (10)] đối tượng nhập MinCount vào trả ArrayList arr) {} phải lớn //arr.Count >= 10 giá trị cho trước MinLength Quy ước chiều Void SetData([MinLength (10)] dài tối thiểu 85 String str) {} (86) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# chuỗi là //str.Length >= 10 giá trị cho trước Yêu cầu đối tượng nhập vào Void SetData([NotEqual (5)] int trả phải NotEqual value) {} “not equal” với //value != giá trị cho trước Quy ước giá trị nhập vào hay trả Void SetData([OneOf giá OneOf (1,3,5,7,9,10)] int value) {} trị phải nằm //value có giá trị là các các giá trị giá trị sau : [1,3,5,7,9,10] cho trước … 15.4 Ví dụ lớp Stack private Object[] representation; public int count; // inv: count>=0 (count khong am) public int capacity;// inv: count<=capacity private int current;// inv: current>=0 // [Requires ("size >= 0")] [Ensures ("representation != null && capacity == size && IsEmpty()")] public MyStack([GreaterThanOrEqual (0)]int size) { 86 (87) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# capacity = size; representation = new Object[capacity]; } [Requires ("!IsFull()")] [Ensures ("!IsEmpty() && (int)representation[count-1] == obj")] public void put(int obj) { representation[count++] = obj; } [Requires ("!IsEmpty()")] [Ensures ("!IsFull()")] public void remove() { count; } [Ensures ("result == (count == capacity)")] public bool IsFull() { return count == capacity; } [Ensures ("result == (count == 0)")] public bool IsEmpty() { return count == 0; } 87 (88) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# [Requires ("position >= && position < count")] [Ensures ("current == position")] public void SetCurrentItem(int position) { current = position; } [Ensures ("current == count - 1")] public void ResetCurrentItem() { current = count - 1; } [Requires ("!IsEmpty()")] [Ensures ("result != null && current >= 0")] public Object nextItem() { return representation[current ]; } Chương 16: Kết thực nghiệm: công cụ DCS 16.1 Nguyên lý làm việc DCS là Add-In môi trường Visual C#, nguyên lý làm việc DCS là bắt kiện OnBuildBegin project, thực bước sau: ƒ Duyệt qua tất lớp project (mỗi lớp ứng với file *.cs, trừ file AssemblyInfo.cs) và lưu thông tin lớp (tên lớp, tên file, tên lớp dẫn xuất) Trong lớp, chương trình thực bước sau: − Kiểm tra xem lớp có chứa xác nhận (Invariant, PreCondition, PostCondition) hay không Lưu thông tin Invariant (các mệnh đề và thông báo 88 (89) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# tương ứng Invariant) có, sau đó, duyệt qua hàm lớp, hàm, thực bước sau: + Kiểm tra hàm có PreCondition PostCondition hay không Nếu có, lưu lại thông tin hàm Thông tin lưu trữ gồm có: Tên hàm, PreCondition và PostCondition (lưu các mệnh đề và thông báo) + Đổi tên hàm, lớp có Invariant, tất tên hàm đổi, không có Invariant, hàm có PreCondition PostCondition đổi tên Tên hàm đổi sau: Tên hàm = @origin_[Tên hàm cũ] − Dựa vào thông tin đã lưu trữ được, bổ sung thông tin xác nhận các lớp dẫn xuất cho lớp Việc lưu trữ này thực cách duyệt qua tất các lớp Trong đó, lớp: + Duyệt qua tất tất lớp dẫn xuất lớp này (đã lưu trữ), lưu thông tin Invariant lớp dẫn xuất + Duyệt qua các hàm, hàm nào là hàm cài đặt lại lưu trữ thông tin Assertion lớp dẫn xuất ƒ Phát sinh source code bổ sung để kiểm tra các xác nhận cho các lớp Trong lớp, source code thêm vào phía source code Mỗi hàm phát sinh source code tương ứng mình Với hàm, dựa vào thông tin đã lưu trữ, source code phát sinh sau: [Khai báo hàm theo tên hàm cũ] { Gọi hàm kiểm tra PreCondition Gọi hàm kiểm tra Invariant Gọi hàm gốc (hàm đã đổi tên @origin_*) Gọi hàm kiểm tra PostCondition Gọi hàm kiểm tra Invariant } 89 (90) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# [Hàm kiểm tra PreCondition] { Các câu lệnh kiểm tra các mệnh đề và xuất thông báo tương ứng PreCondition } [Hàm kiểm tra Invariant] { Các câu lệnh kiểm tra các mệnh đề và xuất thông báo tương ứng Invariant } [Hàm kiểm tra PostCondition] { Các câu lệnh kiểm tra các mệnh đề và xuất thông báo tương ứng PostCondition } Ví dụ: Hàm và PreCondition, PostCondition, Invariant: /* @#$Require: !IsFull() # Stack Full @#$Ensure: !IsEmpty() (int)representation[count-1] == obj count == OLD_count + $int # New count = Old count + */ public void put(int obj) 90 (91) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# { representation[count++] = obj; } /* @#$Invariant: count >= count <= capacity current >= */ 91 (92) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Code phát sinh: public void put(int obj) { int OLD_count = count; if(put_PreCondition(obj)!="") throw new Exception("PreCondition Violated at [MyStack - public void put(int obj)]:" +put_PreCondition(obj)); if(put_Invariant(obj)!="") throw new Exception("Invariant Violated at [MyStack]:" +put_Invariant(obj)); @origin_put(obj); if(put_PostCondition(obj,OLD_count)!="") throw new Exception("PostCondition Violated at [MyStack - public void put(int obj)]:" +put_PostCondition(obj,OLD_count)); if(put_Invariant(obj)!="") throw new Exception("Invariant Violated at [MyStack]:" +put_Invariant(obj)); } private string put_PreCondition(int obj) { if (!(!IsFull() )) return " Stack Full"; return ""; } 92 (93) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# private string put_Invariant(int obj) { if (!(count >= 0)) return " "; if (!(count <= capacity)) return " "; if (!(current >= 0)) return " "; return ""; } private string put_PostCondition(int obj,int OLD_count) { if (!(!IsEmpty())) return " "; if (!((int)representation[count-1] == obj)) return " "; if (!(count == OLD_count + )) return " New count = Old count + 1"; return ""; } Project biên dịch theo source code Bắt kiện OnBuildDone project, thực việc trả lại source code cũ cho project 93 (94) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# 16.2 Thiết kế 16.2.1 Tổng thể Hình 16-1: Sơ đồ thiết kế tổng thể Danh sách các lớp đối tượng: STT Tên Ý nghĩa Màn hình cho phép người dùng enable disable Configuration chức kiểm tra PreCondition, PostCondition, Invariant Connect Lớp chính chương trình, đây là lớp quản lý thao tác Add-In với project 94 (95) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Lớp đối tượng để lưu trữ thông tin project ProjectInfo ClassInfo FunctionInfo Assertion Extra 16.2.2 16.2.2.1 hành Lớp đối tượng để lưu trữ thông tin lớp project Lớp đối tượng để lưu trữ thông tin phương thức class Lớp đối tượng để lưu trữ thông tin Assertion (precondition, postcondition, invariant) Lớp đối tượng chứa hàm riêng, không thuộc trách nhiệm lớp trên Chi tiết các lớp đối tượng Màn hình Configuration Hình 16-2: Màn hình Configuration 95 (96) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Hình 16-3: Chi tiết màn hình Configuration Danh sách các đối tượng thể hiện: STT Tên Loại / Kiểu chkPreCondition checkbox chkPostCondition checkbox chkInvariant checkbox chkBasePre checkbox chkBasePost checkbox chkBaseInv checkbox btnOK Button btnClose Button Ý nghĩa Xác định có sử dụng PreCondition hay không Xác định có sử dụng PostCondition hay không Xác định có sử dụng Invariant hay không Xác định có sử dụng PreCondition lớp dẫn xuất hay không Xác định có sử dụng PostCondition lớp dẫn xuất hay không Xác định có sử dụng Invariant lớp dẫn xuất hay không Đồng ý với thông số đã chọn và thoát khỏi màn hình Thoát khỏi màn hình 96 (97) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Danh sách các đối tượng xử lý: STT Tên Lớp/Kiểu PreConditionCheck bool PostConditionCheck bool InvariantCheck bool Ý nghĩa Xác định có sử dụng PreCondition hay không Xác định có sử dụng PostCondition hay không Xác định có sử dụng Invariant hay không Xác định có sử dụng BasePreConditionCheck bool PreCondition lớp dẫn xuất hay không Xác định có sử dụng BasePostConditionCheck bool PostCondition lớp dẫn xuất hay không Xác định có sử dụng Invariant BaseInvariantCheck bool lớp dẫn xuất hay không Danh sách các biến cố : STT Thể Biến cố Xử lý Hiển thị màn hình Configuration cho phép Form Load người dùng enable disable chức kiểm tra PreCondition, PostCondition, Invariant btnOK Click btnClose Click Lưu thông số đã chọn trên màn hình và thoát khỏi màn hình Thoát khỏi màn hình 97 (98) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# 16.2.2.2 Lớp Connect Hình 16-4: Lớp Connect Danh sách các biến thành phần: STT Tên Kiểu/Lớp projectInfo ProjectInfo Ý nghĩa Lưu trữ thông tin Project hành Danh sách các biến cố: STT Thể Biến cố Xử lý RightClick vào màn hình soạn thảo code, xuất pop-up menu Click vào command Command Exec này, màn hình Configuration hiển thị cho phép người dùng enable disable chức kiểm tra PreCondition, PostCondition, Invariant Bắt đầu chạy chương trình: ¾ Lưu thông tin project hành: thông tin các phương thức các Project BuildBegin lớp (tên phương thức, precondition, postcondition) và Invariant lớp project ¾ Đổi tên tất các phương thức có liên quan đến contract 98 (99) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# ¾ Lưu thông tin contract lớp dẫn xuất cho lớp kế thừa ¾ Phát sinh source code để kiểm tra Assertion Project 16.2.2.3 BuildDone Trả source code cũ Lớp ProjectInfo Hình 16-5: Lớp ProjectInfo Danh sách biến thành phần: STT Tên Kiểu/Lớp Ý nghĩa Mảng các đối tượng ClassInfo, classInfo ClassInfo[] đối tượng lưu trữ thông tin lớp project NumFile int Số file project này 99 (100) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Danh sách hàm thành phần: STT Tên FileCount Tham số Kết Xử lý Đếm số file project, lưu vào biến NumFile Duyệt qua lớp, gọi ChangeAllFuncName phương thức đổi tên hàm lớp đó Duyệt qua lớp, gọi phương thức lưu thông SaveAssertionOfBase tin assertion Classes lớp dẫn xuất lớp đó Duyệt qua lớp, gọi phương thức thêm vào GenerateCode phần code kiểm tra Assertion lớp đó Duyệt qua lớp, gọi ReturnOriginalCode phương thức trả code cũ lớp đó 100 (101) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# 16.2.2.4 Lớp ClassInfo Hình 16-6: Lớp ClassInfo Danh sách biến thành phần: STT Tên Kiểu/Lớp Ý nghĩa Mảng các đối tượng functionInfo FunctionInfo[] FunctionInfo, đối tượng lưu trữ thông tin hàm lớp NumFunc int Số hàm class này Đối tượng lớp Assertion để Invariant Assertion lưu trữ thông tin Invariant lớp Mảng đối tượng lớp BaseInvariant Assertion[] Assertion để lưu trữ thông tin Invariant 101 Ghi chú (102) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# lớp dẫn xuất Mỗi file FileName string Tên file chứa lớp chứa lớp ClassName string BaseClassName string[] Tên lớp Mảng tên các lớp dẫn xuất Đối tượng lớp Extra, dùng extra để gọi hàm riêng, Extra không thuộc trách nhiệm lớp chính Danh sách hàm thành phần: STT Tên FuncCount Tham số Kết Xử lý Đếm số hàm lớp, lưu vào biến NumFunc Phân tích code để lấy tên GetClassName lớp, lưu vào ClassName Phân tích code để lấy tên lớp dẫn xuất GetBaseClassesName lớp này, lưu vào BaseClassName Lưu Invarian, SaveAssertionOfBase ClassInfo Class PreCondition, PostCondition [] lớp dẫn xuất 102 (103) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Lưu thông tin Invariant SaveInvariantInfo lớp vào biến Invariant Duyệt qua hàm lớp, lưu thông tin hàm (tên hàm, ChangeFuncName PreCondition, PostCondition), sau đó đổi tên hàm Tìm vị trí thích hợp code, gọi hàm GenerateCode CodeGenerated để phát sinh code vào đó Duyệt qua hàm, gọi phương thức phát sinh CodeGenerated string code hàm tương ứng, trả chuỗi code đã phát sinh Xoá phần code phát sinh ReturnOriginalCode và điều chỉnh tên hàm cũ 103 (104) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# 16.2.2.5 Lớp FunctionInfo Hình 16-7: Lớp FunctionInfo Danh sách biến thành phần: STT Tên Kiểu/Lớp PreCondition Assertion PostCondition Assertion FunctionName string Ý nghĩa Đối tượng lớp Assertion để lưu trữ thông tin PreCondition hàm Đối tượng lớp Assertion để lưu trữ thông tin PostCondition hàm Biến lưu tên hàm Mảng đối tượng lớp Assertion để lưu BasePreCondition Assertion[] trữ thông tin PreCondition lớp dẫn xuất Mảng đối tượng lớp Assertion để lưu BasePreCondition Assertion[] trữ thông tin PostCondition lớp dẫn xuất Đối tượng lớp Extra, dùng để gọi Extra Extra hàm riêng, không thuộc trách nhiệm lớp chính 104 (105) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Danh sách hàm thành phần: STT Tên Tham số Kết Xử lý Ghi chú Đổi tên hàm theo ChangeName dạng @origin_[Tên cũ] Lưu thông tin PreCondition và PostCondition SaveContractsInfo hàm vào biến PreCondition và PostCondition Phân tích mệnh đề GetVar string string[] Assertion để lấy biến và kiểu liệu Với mệnh đề đối tượng GetVarHaveOLD Keyword Assertion string[][] Assertion, gọi hàm GetVar để lấy biến và kiểu liệu Dùng tên hàm đã lưu, phát GenerateCode sinh hàm Những hàm này gọi lại 105 dùng cho mệnh đề có từ khoá OLD Chỉ làm việc với mệnh đề có từ khoá OLD (106) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# hàm gốc (đã bị đổi tên) cùng với code kiểm tra PreCondition, PostCondition và Invariant hàm 16.2.2.6 Lớp Assertion Hình 16-8: Lớp Assertion Danh sách biến thành phần: STT Tên Kiểu/Lớp Ý nghĩa Lưu tên thủ tục(tên Routine string hàm) chứa Assertion này ConditionFull string[] Lưu trữ toàn mệnh đề Assertion 106 Ghi chú Giá trị rỗng Invariant (107) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Khác ConditionFull Condition chỗ không lưu Lưu trữ toàn mệnh string kiểu liệu đề Assertion biến có từ khoá OLD Message Lưu Message string[] kèm với Condition Đối tượng lớp Extra, dùng để gọi Extra Extra hàm riêng, không thuộc trách nhiệm lớp chính Danh sách hàm thành phần: STT Tên Kết Tham số Xử lý Dựa vào FuncName và Type (Precondition, PostCodition, Invariant, GenerateAssertion string FuncName, Code string Type string BasePreCondition, BasePostCondition, BaseInvariant) để phát sinh hàm kiểm tra Assertion Phát sinh hàm string FuncName, GenerateCode FuncName dòng string Type, code kiểm tra gọi đến string[][] OLDVar hàm kiểm tra Assertion đã tạo 107 (108) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# GenerateAssertionCode OLDVar là mảng chứa biến có từ khoá OLD PostCondition và kiểu liệu tương ứng để phát sinh code kiểm tra biến này Dựa vào FuncName và Type (Precondition, PostCodition, Invariant, BasePreCondition, BasePostCondition, BaseInvariant) để phát GenerateAssertion CodeBasePre Assertion ass, string FuncName, sinh hàm kiểm tra string Assertion Hàm này tương tự hàm string Type GenerateAssertionCode dùng trường hợp lớp dẫn xuất có PreCondition, lúc này cần truyền tham số kiểu Assertion cho hàm Chức giống hàm GenerateCode_Ba sePre string FuncName, Assertion[] BasePreCondition GenerateCode hỉ dùng trường hợp lớp dẫn xuất có chứa PreCondition, vì trường hợp này code phát sinh 108 (109) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# khác với trường hợp còn lại 16.2.2.7 Lớp Extra Hình 16-9: Lớp Extra Danh sách hàm thành phần: STT Tên Kết Tham số Xử lý Kiểm tra xem dòng Line IsContractor string Line bool có phải là khai báo contructor không Kiểm tra xem dòng s có chứa từ IsAccessibilityL evelFound string s bool khóa {"private", "public", "protected", "internal","protected internal", "static" } IsContain string large, string small bool 109 Kiểm tra chuỗi large có chứa chuỗi small (110) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Kiểm tra hàm điểm IsHaveContract EditPoint e, TextDocument t EditPoint e, văn bool TextDocument t có chứa PreCondition PostCondition GetReturnType string FuncName string Lấy kiểu trả FuncName này Từ khai báo hàm (vd: string GetFuncName FuncDec,int public int A(int x) ), trả string Flag IsHaveVar string FName bool dạng: ¾ Flag=1: A(x) ¾ Flag=2: A(int x) Kiểm tra FName có tham số không Sửa tên hàm FName(…) thành FName_[Type](…), đó, Type là FixFuncName string FName, string Type string PreCondition, PostCondition, Invariant hay BasePreCondition, BasePostCondition, BaseInvariant OLDVar là mảng các biến AddOLDVar string FName1, và kiểu liệu tương ứng, string FName2, sửa đổi FName1 và string[][] FName2 cách thêm OLDVar thông tin OLDVar vào tham số 110 (111) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# KẾT LUẬN Sau nghiên cứu đề tài, chúng em đã hiểu khá rõ công nghệ Design By Contract và khả ứng dụng nó lập trình hướng đối tượng Đồng thời, để phục vụ cho yêu cầu đề tài giúp cho việc hoàn thiện kiến thức đã tìm hiểu được, chúng em đã xây dựng công cụ hỗ trợ Design By Contract dạng Add-In cho C# 111 (112) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# HƯỚNG PHÁT TRIỂN − Xây dựng công cụ hỗ trợ Design By Contract cho môi trường lập trình khác − Mở rộng khả kiểm tra công cụ, có thể kiểm tra điều kiện thiết thực − Mở rộng kiểu liệu kiểm tra công cụ, có thể kiểm tra kiểu đối tượng không dừng lại các kiểu liệu sở 112 (113) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# TÀI LIỆU THAM KHẢO [1] B Meyer, Object-Oriented Software Construction, Prentice Hall, 2nd edition, 1997 [2] Eiffel Software, Design By Contract http://www.eiffel.com/doc/manuals/technology/contract/ [3] ResolveCorp, eXtensible C# - Design by contract Add-In for C# http://www.mmsindia.com/JMSAssert.html [4] Man Machine Systems, Design by contract tool for Java—JMSAssert http://www.mmsindia.com/JMSAssert.html [5] Kevin McFarlane, Design by Contract Framework for C# http://www.codeproject.com/csharp/designbycontract.asp [6] Parasoft Corp, Jcontract home page http://www.parasoft.com/jsp/products/home.jsp?product=Jcontract [7] R Kramer, iContract home page http://www.reliable-systems.com/tools/iContract/iContract.htm 113 (114) Tìm hiểu công nghệ Design By Contract và Xây dựng công cụ hỗ trợ cho C# Ý KIẾN CỦA GIÁO VIÊN PHẢN BIỆN ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… ……………………………………………………………………………………… 114 (115)

Ngày đăng: 18/09/2021, 01:54

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan