Nhập môn Công nghệ phần mềm (Phần 2) - Nguyễn Thế Dũng
Bạn đang xem 20 trang mẫu của tài liệu "Nhập môn Công nghệ phần mềm (Phần 2) - Nguyễn Thế Dũng", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
Tài liệu đính kèm:
- nhap_mon_cong_nghe_phan_mem_phan_2_nguyen_the_dung.pdf
Nội dung text: Nhập môn Công nghệ phần mềm (Phần 2) - Nguyễn Thế Dũng
- Chƣơng 4. THIẾT KẾ Mục đích Trình bày quá trình thiết kế phần mềm, thiết kế kiến trúc, thiết kế giao diện. Bên cạnh đó quá trình thiết kế phần mềm theo phương pháp hướng đối tượng cũng được trình bày. Yêu cầu + Biết được các hoạt động của thiết kế và các sản phẩm. + Hiểu các nguyên lý thiết kế. + Hiểu được các nguyên tắc thiết kế giao diện. + Vận dụng vào bộ môn Phân tích thiết kế hệ thống. Cũng như trong phần phân tích và đặc tả yêu cầu, trong phần này chúng tôi cũng không tách riêng phần thiết kế theo phương pháp hướng đối tượng thành một chương của giáo trình mà chỉ đưa vào thành một mục của chương này. Tuy mục này khá dài nhưng sẽ không làm cho bố cục của giáo trình quá cồng kềnh. 1. Khái niệm về thiết kế phần mềm 1.1.Khái niệm Có thể định nghĩa thiết kế là một quá trình áp dụng kỹ thuật và các nguyên lý để tạo ra mô hình của một thiết bị, một tiến trình hay một hệ thống đủ chi tiết mà theo đó có thể chế tạo ra sản phẩm vật lý tương ứng với nó. Trong khi phân tích nhằm trả lời câu hỏi vấn đề là cái gì (What?), thì thiết kế nhằm trả lời câu hỏi làm thế nào (How to do?). Bản chất thiết kế phần mềm là một quá trình chuyển hóa các yêu cầu phần mềm thành một biểu diễn thiết kế. Từ những mô tả quan niệm về toàn bộ phần mềm, việc làm mịn (chi tiết hóa) liên tục dẫn tới một biểu diễn thiết kế rất gần với cách biểu diễn của chương trình nguồn để có thể ánh xạ vào một ngôn ngữ lập trình cụ thể. 102
- 1.2. Tầm quan trọng 1.2.1. Vai trò Tạo mô hình cài đặt của phần mềm, là phương tiện trao đổi thông tin để đảm bảo chất lượng Tầm quan trọng của thiết kế phần mềm có thể được phát biểu bằng một từ “chất lượng”. Thiết kế là nơi chất lượng phần mềm được nuôi dưỡng trong quá trình phát triển: cung cấp cách biểu diễn phần mềm có thể được xác nhận về chất lượng, là cách duy nhất mà chúng ta có thể chuyển hóa một cách chính xác các yêu cầu của khách hàng thành sản phẩm hay hệ thống phần mềm cuối cùng. Thiết kế phần mềm là công cụ giao tiếp làm cơ sở để có thể mô tả một cách đầy đủ các dịch vụ của hệ thống, để quản lý các rủi ro và lựa chọn giải pháp thích hợp. Thiết kế phần mềm phục vụ như một nền tảng cho mọi bước kỹ nghệ phần mềm và bảo trì: - Không có thiết kế có nguy cơ sản sinh một hệ thống không ổn định - một hệ thống sẽ thất bại. Một hệ thống phần mềm rất khó xác định được chất lượng chừng nào chưa đến bước kiểm thử. Thiết kế tốt là bước quan trọng đầu tiên để đảm bảo chất lượng phần mềm. 1.2.2. Thiết kế và chất lƣợng • Thiết kế phải thỏa yêu cầu tường minh cũng như ngầm định • Thiết kế phải dễ hiểu, dễ đọc và hỗ trợcho việc tạo code, test và hỗ trợ phần mềm • Thiết kế phải cung cấp hình ảnh đầy đủ của phần mềm, xác định dữ liệu, chức năng và hành vi. Nếu không có thiết kế; hoặc thiết kế tồi dẫn đến: làm tăng công sức mã hóa và tăng công sức bảo trì. 1.3. Quá trình thiết kế Thiết kế phần mềm là quá trình chuyển các đặc tả yêu cầu dịch vụ thông tin của hệ thống thành đặc tả hệ thống phần mềm. Thiết kế phần mềm trải qua một số giai đoạn chính sau: 103
- 1. Nghiên cứu để hiểu ra vấn đề. Không hiểu rõ vấn đề thì không thể có được thiết kế hữu hiệu. 2. Chọn một (hay một số) giải pháp thiết kế và xác định các đặc điểm thô của nó.Chọn giải pháp phụ thuộc vào kinh nghiệm của người thiết kế, vào các cấu kiện dùng lại được và vào sự đơn giản của các giải pháp kéo theo. Nếu các nhân tố khác là tương tự thì nên chọn giải pháp đơn giản nhất. 3. Mô tả trừu tượng cho mỗi nội dung trong giải pháp. Trước khi tạo ra các tư liệu chính thức người thiết kế cần phải xây dựng một mô tả ban đầu sơ khai rồi chi tiết hóa nó. Các sai sót và khiếm khuyết trong mỗi mức thiết kế trước đó được phát hiện và phải được chỉnh sửa trước khi lập tư liệu thiết kế. Kết quả của mỗi hoạt động thiết kế là một đặc tả thiết kế. Đặc tả này có thể là một đặc tả trừu tượng, hình thức và được tạo ra để làm rõ các yêu cầu, nó cũng có thể là một đặc tả về một phần nào đó của hệ thống phải được thực hiện như thế nào. Khi quá trình thiết kế tiến triển thì các chi tiết được bổ sung vào đặc tả đó. Các kết quả cuối cùng là các đặc tả về các thuật toán và các cấu trúc dữ liệu được dùng làm cơ sở cho việc thực hiện hệ thống. 1.4. Các hoạt động thiết kế phần mềm Các nội dung chính của thiết kế là: - Thiết kế kiến trúc: Xác định hệ tổng thể phần mềm bao gồm các hệ con và các quan hệ giữa chúng và ghi thành tài liệu. - Đặc tả trừu tượng: các đặc tả trừu tượng cho mỗi hệ con về các dịch vụ mà nó cung cấp cũng như các ràng buộc chúng phải tuân thủ. - Thiết kế giao diện: giao diện của từng hệ con với các hệ con khác được thiết kế và ghi thành tài liệu; đặc tả giao diện không được mơ hồ và cho phép sử dụng hệ con đó mà không cần biết về thiết kế nội tại của nó. - Thiết kế các thành phần: các dịch vụ mà một hệ con cung cấp được phân chia cho các thành phần hợp thành của nó. - Thiết kế dữ liệu: thiết kế chi tiết và đặc tả các cấu trúc dữ liệu (các mô hình về thế giới thực cần xử lý) được dùng trong việc thực hiện hệ thống. - Thiết kế thuật toán: các thuật toán được dùng cho các dịch vụ được thiết kế chi tiết và được đặc tả. 104
- Quá trình này được lặp lại cho đến khi các thành phần hợp thành của mỗi hệ con được xác định đều có thể ánh xạ trực tiếp vào các thành phần ngôn ngữ lập trình, chẳng hạn như các gói, các thủ tục và các hàm. Hình 4.1. Các hoạt động thiết kế phần mềm Hình 4.2. Hoạt động thiết kế và sản phẩm tƣơng ứng Các vấn đề liên quan đến thiết kế dữ liệu và thiết kế thuật toán đã được đề cập khá kỹ lưỡng trong các môn học như Nhập môn cơ sở dữ liệu và môn học Phân tích thiết kế thuật toán, nên thường thì trong các giáo trình môn học Công 105
- nghệ phần mềm không đi sâu vào các phần vừa nói trên, mà đi vào các hoạt động thiết kế đó là thiết kế kiến trúc và thiết kế giao diện 1.5. Mô tả thiết kế Một bản thiết kế phần mềm là một mô hình mô tả một đối tượng của thế giới thực có nhiều thành phần và các mối quan hệ giữa chúng với nhau. Việc mô tả thiết kế cần đảm bảo thực hiện được các yêu cầu: - Làm cơ sở cho việc triển khai chương trình - Làm phương tiện giao tiếp giữa các nhóm thiết kế các hệ con. - Cung cấp đủ thông tin cho những người bảo trì hệ thống Thiết kế thường được mô tả ở hai mức: thiết kế mức cao (high level design) và thiết kế chi tiết (low level design). Thiết kế mức cao hay thiết kế kiến trúc chỉ ra: - Mô hình tổng thể của hệ thống - Cách thức hệ thống được phân rã thành các module - Mối quan hệ (gọi thực hiện lẫn nhau) giữa các module - Cách thức trao đổi thông tin giữa các module (giao diện, các dữ liệu dùng chung, các thông tin trạng thái) Tuy nhiên thiết kế mức cao không chỉ ra được thứ tự thực hiện, số lần thực hiện của module, cũng như các trạng thái và hoạt động bên trong của mỗi module. Nội dung của các module được thể hiện ở mức thiết kế chi tiết. Các cấu trúc cơ sở của thiết kế chi tiết hay còn gọi là thiết kế thuật toán là: - Cấu trúc tuần tự - Cấu trúc rẽ nhánh - Cấu trúc lặp Mọi thuật toán đều có thể mô tả dựa trên 3 cấu trúc trên. Có ba loại hình mô tả thường được sử dụng trong thiết kế: - Dạng văn bản phi hình thức: Mô tả bằng ngôn ngữ tự nhiên các thông tin không thể hình thức hóa được như các thông tin phi chức năng. Bên cạnh các cách 106
- mô tả khác, mô tả văn bản thường được bổ sung để làm cho thiết kế được đầy đủ và dễ hiểu hơn. - Các biểu đồ: Các biểu đồ được dùng để thể hiện các mối quan hệ giữa các thành phần lập lên hệ thống và là mô hình mô tả thế giới thực. Việc mô tả đồ thị của các thiết kế là rất có lợi vì tính trực quan và cho một bức tranh tổng thể về hệ thống. Trong thời gian gần đây, người ta đã xây dựng được một ngôn ngữ đồ thị dành riêng cho các thiết kế phần mềm với tên gọi: ngôn ngữ mô hình hóa thống nhất (Unified Modeling Model - UML). Tại mức thiết kế chi tiết, có một số các dạng biểu đồ hay được sử dụng là flow chart, JSP, Nassi-Shneiderman diagrams. - Giả mã (pseudo code): Hiện nay, giả mã là công cụ được-a chuộng để mô tả thiết kế ở mức chi tiết. Các ngôn ngữ này thuận tiện cho việc mô tả chính xác thiết kế, tuy nhiên lại thiếu tính trực quan. Dưới đây là một ví dụ sử dụng giả mã: Procedure Write Name if sex = male write "Mr." else write "Ms." endif write name end Procedure Nói chung thì cả ba loại biểu diễn trên đây đều được sử dụng trong thiết kế hệ thống. Thiết kế kiến trúc thường được mô tả bằng đồ thị (structure chart) và được bổ sung văn bản phi hình thức, thiết kế dữ liệu lôgic thường được mô tả bằng các bảng, các thiết kế giao diện, thiết kế cấu trúc dữ liệu chi tiết, thiết kế thuật toán thường được mô tả bằng mã giả (pseudo code). 1.6. Nguyên lý thiết kế 1. Thiết kế không bị bó buộc vào một cách nhìn hạn chế nào, nó cần được lựa chọn từ các giải pháp có thể 107
- 2. Cho phép lần ngược lại mô hình phân tích. • Các module và các yêu cầu không nhất thiết phải tương ứng 1-1, nhưng phải kiểm tra được sự thỏa mãn các yêu cầu. 3. Không nên tạo lại các thiết kế (giải pháp) đã có, mà cần tái sử dụng tối đa chúng. 4. Mô hình thiết kế (giải pháp) nên tiến gần đến mô hình thế giới thực (bài toán). 5. Biểu diễn thiết kế phải nhất quán và có tính tích hợp: • + Thiết kế do nhiều người tiến hành song song + Phải thống nhất cách biểu diễn, thống nhất giao diện. 6. Thiết kế cần có cấu trúc để dễ hiểu, dễ thay đổi, tức phải được module hóa, phân cấp. 7. Thiết kế không phải là mã hóa, thiết kế luôn có mức trừu tượng hơn mã hóa, để đảm bảo dễ hiểu, dễ thay đổi. 8. Thiết kế cần được đánh giá chất lượng ngay trong khi được tạo ra, thể hiện qua tính kết dính, tính ghép nối, hiệu quả thuật toán 9. Thiết kế cần được thẩm định để tránh các lỗi mang tính hệ thống như thiếu chức năng, chức năng không rõ, mâu thuẫn 1.7. Chất lƣợng thiết kế Không có cách nào hay để xác định được thế nào là thiết kế tốt. Tiêu chuẩn dễ bảo trì là tiêu chuẩn tốt cho người dùng. Một thiết kế dễ bảo trì có thể thích nghi với việc cải biên các chức năng và việc thêm các chức năng mới. Một thiết kế như thế phải dễ hiểu và việc sửa đổi chỉ có hiệu ứng cục bộ. Các thành phần thiết kế phải là kết dính (cohesive) theo nghĩa là tất cả các bộ phận trong thành phần phải có một quan hệ logic chặt chẽ, các thành phần ghép nối (coupling) với nhau là lỏng lẻo. Ghép nối càng lỏng lẻo thì càng dễ thích nghi, nghĩa là càng dễ sửa đổi để phù hợp với hoàn cảnh mới. Tiêu chí chất lƣợng Cần thiết lập các tiêu chí kỹ thuật để đánh giá một thiết kế tốt hay không: 108
- + Thiết kế cần có kiến trúc tốt: cấu thành từ các mẫu (pattern), các thành phần có đặc trưng tốt, dễ tiến hoá. + Thiết kế được môđul hoá cho mỗi thành phần chức năng + Chứa các biểu diễn tách biệt nhau về: dữ liệu, kiến trúc, giao diện, thành phần, môi trường + Liên kết qua giao diện làm giảm độ phức tạp liên kết giữa các môđul với nhau và giữa hệ thống và môi trường. Để xem một thiết kế có là tốt hay không, người ta tiến hành thiết lập một số độ đo chất lượng thiết kế. Một số độ đo: •- Cohesion: mức độ liên kết giữa các thành phần trong một module •- Coupling: mức độ ghép nối giữa các module - Understandability: tính hiểu được. •- Adaptability: tính thích nghi được 1) Sự kết dính (Cohesion) Sự kết dính của một module là độ đo về tính khớp lại với nhau của các phần trong module đó. Nếu một module chỉ thực hiện một chức năng logic hoặc là một thực thể logic, tức là tất cả các bộ phận của module đó đều tham gia vào việc thực hiện một công việc thì độ kết dính là cao. Nếu một hoặc nhiều bộ phận không tham gia trực tiếp vào việc chức năng logic đó thì mức độ kết dính của nó là thấp. Thiết kế là tốt khi độ kết dính cao. Khi đó chúng ta sẽ dễ dàng hiểu được từng module và việc sửa chữa một module sẽ không (ít) ảnh hưởng tới các module khác. 8 mức kết dính theo thứ tự tăng dần sau đây: a. Kết dính gom góp: các công việc không liên quan với nhau, song lại bị bó vào một module. b. Kết dính logic: các thành phần cùng thực hiện các chức năng tương tự về logic chẳng hạn như vào/ra, xử lý lỗi, được đặt vào cùng một module. 109
- c. Kết dính thời điểm: tất cả các thành phần cùng hoạt hóa một lúc, chẳng hạn như các thao tác khởi tạo được bó lại với nhau. các thành phần hoạt động cùng thời điểm (ví dụ: hàm khởi tạo;đọc dữ liệu, cấp phát bộ nhớ ) d. Kết dính thủ tục: các phần tử trong module được ghép lại trong một dãy điều khiển. Các thành phần thực hiên theo một thứ tự xác định (ví dụ: tính lương cơ bản, tính phụ cấp, tính bảo hiểm). e. Kết dính truyền thông: tất cả các phần tử của module cùng thao tác trên một dữ liệu vào và đưa ra cùng một dữ liệu ra. Các thành phần truy cập đến cùng tập dữ liệu:, chẳng hạn như các tính toán thống kê (tính max, min, mean, varian ) f. Kết dính tuần tự: trong một module, đầu ra của phần tử này là đầu vào của phần tử khác. Output của một thành phần là input của thành phần tiếp theo. g. Kết dính chức năng: Mỗi phần của module đều là cần thiết để thi hành cùng một chức năng nào đó. Các thành phần cùng góp phần thực hiện một chức năng. Các lớp kết dính này không được định nghĩa chặt chẽ và cũng không phải luôn luôn xác định được. Một đối tượng kết dính nếu nó thể hiện như một thực thể đơn: tất cả các phép toán trên thực thể đó đều nằm trong thực thể đó. Vậy có thể xác định một lớp kết dính nữa là: h. Kết dính đối tượng: mỗi phép toán đều liên quan đến thay đổi, kiểm tra và sử dụng thuộc tính của một đối tượng, là cơ sở cung cấp các dịch vụ của đối tượng. 2) Sự ghép nối (Coupling) Ghép nối là độ đo sự nối ghép với nhau giữa các đơn vị (module) của hệ thống. Hệ thống có nối ghép cao thì các module phụ thuộc lẫn nhau lớn. Hệ thống nối ghép lỏng lẻo thì các module là độc lập hoặc là tương đối độc lập với nhau và chúng ta sẽ dễ bảo trì nó. Các module được ghép nối chặt chẽ nếu chúng dùng các biến chung và nếu chúng trao đổi các thông tin điều khiển (ghép nối chung nhau và ghép nối điều khiển). Ghép nối lỏng lẻo đạt được khi bảo đảm rằng các thông tin cục bộ được che dấu trong các module và các module trao đổi thông tin thông qua danh sách 110
- tham số (giao diện) xác định. Có thể chia ghép nối thành các mức từ chặt chẽ đến lỏng lẻo như sau: a. Ghép nối nội dung: hai hay nhiều module dùng lẫn dữ liệu của nhau, đây là mức xấu nhất, thường xẩy ra đối với các ngôn ngữ mức thấp dùng các dữ liệu toàn cục hay lạm dụng lệnh GOTO. Ví dụ (ghép nối nội dung): 10. k =1 20. gosub 100 30. if y > 120 goto 60 40. k = k+1 50. goto 20 60. print k, y 70. stop 100. Y =3*k*k+7*k-3 110. return b. Ghép nối chung: một số module dùng các biến chung, nếu xẩy ra lỗi thao tác dữ liệu, sẽ khó xác định được lỗi đó do module nào gây ra. Ví dụ: (minh họa ghép nối chung) c. Ghép nối điều khiển: một module truyền các thông tin điều khiển để điều khiển hoạt động của một module khác. Ví dụ: Procedure PrintRec is begin Display Name (name, 111
- sex); end PrintRec; Procedure DisplayName (in : name, sex) is begin if sex = m then print Mr. else print Ms print name end DisplayName; d. Ghép nối dư thừa: module nhận thông tin thừa không liên quan trực tiếp đến chức năng của nó, điều này sẽ làm giảm khả năng thích nghi của module đó. e. Ghép nối dữ liệu: Các module trao đổi thông tin thông qua tham số và giá trị trả lại. f. Ghép nối không có trao đổi thông tin: module thực hiện một chức năng độc lập và hoàn toàn không nhận tham số và không có giá trị trả lại. Ưu việt của thiết kế hướng đối tượng là do bản chất che dấu thông tin của đối tượng dẫn tới việc tạo ra các hệ ghép nối lỏng lẻo. Việc thừa kế trong hệ thống hướng đối tượng lại dẫn tới một dạng khác của ghép nối, ghép nối giữa đối tượng mức cao và đối tượng kế thừa nó. 3) Sự hiểu đƣợc (Understandability) Sự hiểu được của thiết kế liên quan tới một số đặc trưng sau đây: a. Tính kết dính: có thể hiểu được thành phần đó mà không cần tham khảo tới một thành phần nào khác hay không? b. Đặt tên: phải chăng là mọi tên được dùng trong thành phần đó đều có nghĩa? 112
- Tên có nghĩa là những tên phản ánh tên của thực thể trong thế giới thực được mô hình bởi thành phần đó. c. Soạn tư liệu: Thành phần có được soạn thảo tư liệu sao cho ánh xạ giữa các thực thể trong thế giới thực và thành phần đó là rõ ràng. d. Độ phức tạp: độ phức tạp của các thuật toán được dùng để thực hiện thành phần đó như thế nào? Độ phức tạp cao ám chỉ nhiều quan hệ giữa các thành phần khác nhau của thành phần thiết kế đó và một cấu trúc logic phức tạp mà nó dính líu đến độ sâu lồng nhau của cấu trúc if-then-elsse. Các thành phần phức tạp là khó hiểu, vì thế người thiết kế nên làm cho thiết kế thành phần càng đơn giản càng tốt. Đa số công việc về đo chất lượng thiết kế được tập trung vào cố gắng đo độ phức tạp của thành phần và từ đó thu được một vài độ đo về sự dễ hiểu của thành phần. Độ phức tạp phản ánh độ dễ hiểu, nhưng cũng có một số nhân tố khác ảnh hưởng đến độ dễ hiểu, chẳng hạn như tổ chức dữ liệu và kiểu cách mô tả thiết kế. Các số đo độ phức tạp có thể chỉ cung cấp một chỉ số cho độ dễ hiểu của một thành phần. 4) Sự thích nghi đƣợc (Adaptability) Một thiết kế dễ bảo trì thì nó phải sẵn sàng thích nghi được, nghĩa là các thành phần của chúng nên được ghép nối lỏng lẻo. Một thành phần có thể là ghép nối lỏng lẻo theo nghĩa là chỉ hợp tác với các thành phần khác thông qua việc truyền các thông báo. Sự thích nghi được còn có nghĩa là thiết kế phải được soạn thảo tư liệu tốt, dễ hiểu và nhất quán. Để có độ thích nghi thì hệ thống còn cần phải phải tự chứa. Muốn là tự chứa một cách hoàn toàn thì một hệ thống không nên dùng các thành phần khác được xác định ngoại lai. Tuy nhiên, điều đó lại mâu thuẫn với kinh nghiệm nói rằng các thành phần hiện có nên là dùng lại được. Vậy là cần có một cân bằng giữa tính ưu việt của sự dùng lại các thành phần và sự mất mát tính thích nghi được của hệ thống. Một trong những ưu việt chính của kế thừa trong thiết kế hướng đối tượng là các thành phần này có thể sẵn sàng thích nghi được. Cơ cấu thích nghi được này 113
- không dựa trên việc cải biên thành phần đã có mà dựa trên việc tạo ra một thành phần mới thừa kế các thuộc tính và các chức năng của thành phần đó. Chúng ta chỉ cần thêm các thuộc tính và chức năng cần thiết cho thành phần mới. Các thành phần khác dựa trên thành phần cơ bản đó sẽ không bị ảnh hưởng gì. 2. Thiết kế hƣớng chức năng 2.1. Cách tiếp cận hƣớng chức năng Thiết kế hướng chức năng là một cách tiếp cận thiết kế phần mềm trong đó bản thiết kế được phân giải thành một bộ các đơn thể tác động lẫn nhau, mà mỗi đơn thể có một chức năng được xác định rõ ràng. Các chức năng có các trạng thái cục bộ nhưng chúng chia sẻ với nhau trạng thái hệ thống, trạng thái này là tập trung và mọi chức năng đều có thể truy cập được. Nhiều tổ chức đã phát triển các chuẩn và các phương pháp dựa trên sự phân giải chức năng. Nhiều phương pháp thiết kế kết hợp với công cụ CASE đều là hướng chức năng. Rất nhiều các hệ thống đã đượcphát triển bằng cách sử dụng phương pháp tiếp cận hướng chức năng. Các hệ thống đó sẽ được bảo trì cho một tương lai xa. Bởi vậy thiết kế hướng chức năng vẫn sẽ còn được tiếp tục sử dụng rộng rãi. Trong thiết kế hướng chức năng, người ta dùng các biểu đồ luồng dữ liệu (mô tả việc xử lý dữ liệu), các lược đồ cấu trúc (chỉ ra cấu trúc của phần mềm), và các mô tả thiết kế chi tiết. Thiết kế hướng chức năng gắn với các chi tiết của một thuật toán của chức năng đó nhưng các thông tin trạng thái hệ thống là không bị che dấu. Việc thay đổi một chức năng và cách nó sử dụng trạng thái của hệ thống có thể gây ra những tương tác bất ngờ đối với các chức năng khác. Cách tiếp cận chức năng để thiết kế là tốt nhất khi mà khối lượng thông tin trạng thái hệ thống được làm nhỏ nhất và thông tin dùng chung nhau là rõ ràng. 2.2. Các cơ sở của thiết kế phần mềm hƣớng chức năng 2.2.1. Trừu tƣợng hóa Trừu tượng hóa là một khái niệm cơ sở trong tư duy của con người, đây là quá trình ánh xạ một sự vật/hiện tượng của thế giới thực thành một khái niệm logic. 114
- Có nhiều mức trừu tượng khác nhau nhằm cho phép con người tập trung (tư duy) vào giải quyết vấn đề mà không cần bận tâm đến chi tiết hoặc biểu diễn vấn đề bằng một cấu trúc tự nhiên. Trong thiết kế phần mềm có các trừu tượng hóa như trừu tượng hóa dữ liệu, trừu tượng hóa chức năng, trừu tượng hóa điều khiển Quá trình thiết kế trải qua nhiều mức trừu tượng hoá khác nhau: Mức cao nhất: vấn đề cần thiết kế được mô tả một cách tổng quát sử dụng thuật ngữ hướng vấn đề. Các mức thấp hơn: hướng đến thủ tục xử lý chi tiết; kết hợp các thuật ngữ hướng đến hiện thực. Mức thấp nhất: vấn đề được mô tả theo cách có thể hiện thực trực tiếp. Phân loại trừu tượng hoá: trừ tượng hóa dữ liệu và trừu tượng hoá thủ tục. Trừu tượng hóa thủ tục là xác định chuỗi các lệnh liên tiếp thực hiện chức năng nào đó. Ví dụ: mở cửa (bao gồm đi đến cửa, cầm lấy tay nắm, xoay tay nắm, kéo cánh cửa, đi vào ); thêm một phần tử vào danh sách có thứ tự (xác định vị trí, chèn phần tử mới). Trừu tượng hoá dữ liệu: là tổ hợp dữ liệu mô tả một đối tượng dữ liệu (liên hệ tới đối tượng thực thể trong UML). Ví dụ: hàng, chồng, cánh cửa 2.2.2. Tinh chế Tinh chế là quá trình làm rõ vấn đề Tinh chế và trừu tượng hoá là hai khái niệm bù trừ nhau: càng tinh chế thì càng hạ thấp mức trừu tượng hóa. 2.2.3. Phân chia module Khái niệm module đã xuất hiện khoảng 4 thập niên trở lại đây. Phần mềm được xây dựng bằng cách phân chia thành nhiều module, sau đó sẽ được tích hợp lại. Quá trình này dựa trên chiến lược “chia để trị”. 115
- Phân chia module làm cho việc quản lý phần mềm khoa học hơn, nhằm giảm độ phức tạp cục bộ, dễ sửa đổi, có khả năng phát triển song song, dễ sửa đổi, dễ hiểu nên dễ tái sử dụng. Giả sử C(x): độ phức tạp của x, E(x): công sức để thực hiện x. Rõ ràng: nếu C(p1) > C(p2) thì E(p1) > E(p2). Nếu phân chia p = p1 + p2 ta thấy: C(p1 + p2) > C(p1) + C(p2) => E(p1 + p2) > E(p1) + E(p2) Số lượng module phụ thuộc vào độ phức tạp của hệ thống phần mềm cần xây dựng quá ít hoặc quá nhiều module đều không tốt. Hình 4.3. Số lƣợng module và chi phí tích hợp module Kích cỡ module được quyết định dựa trên khái niệm độc lập chức năng: mỗi module nên thực hiện một công việc nhằm dễ hiểu, dễ sửa đổi, dễ tái sử dụng. Một số heuristics cho việc phân chia module Nhiều khi cần sửa lại thiết kế ban đầu để tăng độ kết dính và giảm sự liên kết, với lưu ý: Gữ cho tầm ảnh hưởng của một module nằm bên trong tầm điều khiển của nó và loại bỏ dư thừa trong giao tiếp của các module. Bên cạnh đó cần ưu tiên các module tất định, hạn chế các module nhiều ràng buộc, đóng gói các module để đạt được tính khả chuyển (portability). 2.2.4. Kiến trúc phần mềm 116
- Kiến trúc phần mềm mô tả các thành phần (component) kiến tạo nên hệ thống phần mềm và sự giao tiếp giữa các thành phần đó. Thành phần có thể là: Các module mã nguồn; Các file thực thi (*.dll, *.exe, *.class ); Các thành phần của kiến trúc hệ thống: ActiveX control, bean ; Các trang HTML, *.asp, *.jsp 2.2.5. Cấu trúc dữ liệu Cấu trúc dữ liệu mô tả sự tổ chức, phương thức truy xuất, mức độ liên kết và các xử lý khác của thông tin. Dữ liệu đơn là dạng cấu trúc dữ liệu đơn giản nhất chỉ bao gồm một phần tử thông tin mà có thể được truy xuất bằng một danh định. Một số dạng phức tạp hơn: vector, ma trận, mảng nhiều chiều, danh sách liên kết, hàng, chồng, cây nhị phân Được biểu diễn ở các mức trừu tượng hoá khác nhau. 2.2.6. Thủ tục Thủ tục tập trung vào chi tiết xử lý của mỗi module. Cung cấp đặc tả chi tiết của: Chuỗi sự kiện; Vòng lặp; Quyết định rẽ nhánh; Có thể cả cấu trúc dữ liệu. 2.2.7. Nguyên lý che dấu thông tin Che dấu thông tin là một trong những nguyên lý quan trọng của việc phân chia module. Các module giao tiếp với nhau bằng những thông tin thật sự cần thiết. Những thông tin về thủ tục và dữ liệu cục bộ của mỗi module phải được che dấu khỏi các module khác. Lợi ích: Kiểm soát được thay đổi và sửa lỗi dễ dàng. Tại sao phải che dấu thống tin: • Giảm hiệu ứng lề. • Giới hạn những tác động toàn cục lên những quyết định thiết kế cục bộ. • Nhấn mạnh tới việc thông tin qua những giao diện có điều khiển. • Ngăn cản việc dùng dữ liệu cục bộ • Đưa tới việc đóng gói là một thuộc tính của thiết kế chất lượng cao. • Làm cho phần mềm chất lượng cao hơn. 2.2.8. Thiết kế dữ liệu 117
- Nhằm tìm kiếm biểu diễn luận lý cho các phần tử dữ liệu đã được nhận diện trong giai đoạn phân tích yêu cầu. Giai đoạn này bao gồm cả thiết kế các cấu trúc dữ liệu của chương trình và cơ sở dữ liệu. Gai đoạn này chúng ta cần thực hiện tinh chế từng bước. Một số nguyên tắc: + Nhận diện cả cấu trúc dữ liệu và tác vụ truy xuất. + Chú ý sử dụng từ điển dữ liệu. + Trì hoãn thiết kế dữ liệu mức thấp cho đến cuối giai đoạn này. + Che dấu biểu diễn bên trong của cấu trúc dữ liệu. + Phát triển một thư viện các cấu trúc dữ liệu và các tác vụ thường gặp. Các vấn đề của thiết kế cơ sở dữ liệu đã được đề cập nhiều trong môn học Nhập môn Hệ cơ sở dữ liệu, nên chúng tôi không đưa chi tiết vào đây. Mời bạn đọc xem lại trong các tài liệu tham khảo liên quan. 3. Thiết kế kiến trúc 3.1. Khái niệm kiến trúc Kiến trúc phần mềm chỉ cấu trúc tổng thể của một phần mềm và cách thức tổ chức qua đó cho ta một sự tích hợp về mặt khái niệm của một hệ thống [SHA95a]. Ở mức thông thường, kiến trúc phần mềm được thể hiện bằng một biểu đồ phân cấp của các thành phần vàquan hệ giữa chúng. Ở mức đầy đủ cần thể hiện cầu trúc hệ thống theo nhiều góc nhìn khác nhau: góc nhìn tĩnh, động, dữ liệu, triển khai. Mô hình kiến trúc không phải là mô hình hoạt động mà là mô hình phân hoạch theo những cách nhìn khác nhau (chức năng, dữ liệu, tiến trình, tĩnh hay động ) nhằm giúp kỹ sư hệ thống: Phân tích tính hiệu quả của thiết kế đáp ứng được yêu cầu của phần mềm và tìm các giải pháp thay thế kiến trúc ở giai đoạn sớm cũng như giảm các rủi ro liên quan tới kiến trúc. 3.2. Khái niệm thiết kế kiến trúc Giai đoạn thiết kế kiến trúc là giai đoạn đầu của quá trình thiết kế nhằm biểu diễn sự kết nối giữa đặc tả yêu cầuvà các tiến trình thiết kế. Giai đoạn này thường 118
- tiến hành song song với các hoạt động đặc tả phần mềm. Bao gồm việc xác định các thành phần hệ thống và giao tiếp giữa chúng. Các bƣớc thiết kế kiến trúc 1. Cấu trúc hóa hệ thống: phân chia hệ thống thành các hệ con (sub- system) độc lập và xác định trao đổi thông tin giữa các hệ con xác d?nh các giao diện của chúng. 2. Mô hinh hóa điều khiển: xác lập mô hinh điều khiển giữa các phần khác nhau của hệ thống đã được xác định. 3. Phân rã thành các module: phân rã các hệ con thành các module. Ở đây hệ con: phần hệ thống hoạt động độc lập với các dịch vụ mà các hệ con khác cung cấp. Module: phần hệ thống cung cấp dịch vụ và tương tác cùng phần khác để tạo ra dịch vụ hay sản phẩm. 3.3. Mô hình kiến trúc Các mô hình kiến trúc khác nhau được tạo ra trong quá trình thiết kế. Mỗi mô hình biểu diễn một cách nhìn của kiến trúc. Mô hình kiến trúc tĩnh chỉ ra các thành phần chính của hệ thống (biểu đồ phân rã). Mô hình động chỉ ra cấu trúc tiến trình của hệ thống (biểu đồ luồng dữ liệu). Mô hình giao diện xác định hệ thống giao diện của hệ thống (hệ thống giao diện tương tác). Mô hình mối quan hệ như mô hình khái niệm thực thể xác định miền dữ liệu của hệ thống. Mô hình kiến trúc bao gồm: Mô hình cấu trúc; Mô hình điều khiển; Mô hình phân rã module. 3.3.1. Mô hình cấu trúc Mô hình cấu trúc bao gồm: + Kiến trúc dữ liệu tập trung + Kiến trúc khách-dịch vụ + Kiến trúc phân tầng. + Kiến trúc dữ liệu tập trung 119
- Hình 4.4. Mô hình kiến trúc dữ liệu tập trung Ưu điểm: • Tiện lợi cho chia sẻ dữ liệu lớn. • Các phân hệ không cần quan tâm tổ chứcdữ liệu. Nhược điểm: • Các phân hệ phải thống nhất mô hình dữ liệu. • Khó thay đổi cấu trúc dữ liệu. • Các phân hệ không thể đưa ra chính sách riêng. • Khó khăn trong quản lý giao dịch. + Kiến trúc khách dịch vụ (Client-Server Architecture) Hệ thống được phân tán về dữ liệu và xử lý thông qua quan hệ các thành phần. Các máy dịch vụ (server) độc lập cung cấp các dịch vụ đặc biệt như: in ấn, quản lý dữ liệu. Các máy khách (client) sử dụng dịch vụ server. Hệ thống mạng để client truy cập dịch vụ server. 120
- Hình 4.5. Kiến trúc khách dịch vụ (Client-Server Architecture) Ưu điểm: • Sử dụng hiệu quả mạng, giảm chi phí thiết bị. • Dễ dàng mở rộng, thêm dịch vụ. Nhược điểm: • Khó tích hợp dữ liệu. • Cần cơ chế bảo toàn dữ liệu cho từng server. Đây là mô hình phát triển ứng dụng phổ biến hiện nay. + Kiến trúc phân tầng (Layered Architecture/Abstract machine model) Kiểu kiến trúc nhằm mô hình hóa tương tác các hệ con. Mô hình này phân rã hệ thống thành các tầng, mỗi tầng cung cấp một tập các dịch vụ. Mô hình này hỗ trợ sự phát triển tăng trưởng của các tầng, khi giao diện mỗi tầng thay đổi thi chỉ ảnh hưởng tới các tầng liền kề. Tuy vậy không phải hệ thống nào cũng xây dựng cấu trúc hệ thống được theo kiến trúc này. Một ví dụ minh họa cho mô hình này là kiến trúc mô hình tham chiếu OSI trong mạng máy tính. 121
- Hình 4.6. Mô hình kiến trúc phân tầng 3.3.2. Mô hình điều khiển Mô hình điều khiển bao gồm: + Kiến trúc gọi và trả lại. + Kiến trúc xử lý hướng sự kiện. + Kiến trúc gọi và trả lại (Call and Return Architecture ) Mô hình này sử dụng một hệ con để điều khiển, khởi động và dừng các hệ thống con khác, truyền điều khiển hoạt động của hệ thhống trên xuống. Mô hình thường dùng cho các hệ thống tuần tự. 122
- Hình 4.7. Kiến trúc gọi và trả lại + Kiến trúc xử lý sự kiện (Event-driven Architecture) Kiến trúc xử lý hướng sự kiện là cơ chế gửi yêu cầu xử lý đến các hệ con. Bao gồm hai mô hình chính: Điều khiển quảng bá (Broadcast) và Điều khiển hướng ngắt (Interupt). Mô hình điều khiển quảng bá (Broadcast): Một sự kiện được truyền đến tất cả các hệ con. Hệ con được truyền điều khiển khi đã có đăng ký. Mô hình này hiệu quả trong tích hợp các hệ con. Tuy vậy thường thì các hệ con không biết sự kiện đã được xử lý chưa. Hình 4.8. Mô hình điều khiển quảng bá Mô hình điều khiển hƣớng ngắt (Interupt): Mô hình này thường được dùng trong các hệ thời gian thực. Bộ điều khiển ngắt nhận sự kiện và truyền đến các thành phầnxử lý. Mô hình này đáp ứng phản hồi nhanh nhưng sẽ khó lập trình và đánh giá. 123
- Hình 4.9. Mô hình điều khiển hƣớng ngắt 3.3.3. Mô hình phân rã module Đây là cấp độ kiến trúc mà các hệ thống con được phân rã thành các module. Mô hình phân rã module bao gồm: + Mô hình đối tượng: hệ thống được phân rã dựa trên sự tương tác giữa các đối tượng. + Mô hình luồng dữ liệu: hệ thống được phân tách thành các module chức năng chuyển các đầu vào thành các đầu ra (mô hình đường ống - pipeline). + Mô hình hƣớng đối tƣợng Mô hình là tập hợp đối tượng ghép nối lỏng lẻo qua giao diện. Trong mô hình này chúng ta cần xác định: lớp, đốitượng, thuộc tính, phương thức và xác định mô hình điều khiển để phối hợp thao tác giữa các đối tượng. + Mô hình luồng dữ liệu Trong mô hình này, mỗi chức năng là một tiến trình chuyển hóa các đầu vào thành các đầu ra. Các biến thể của nó là: pipe/filter model, batch model Mô hình này không phù hợp với hệ thống tương tác. 3.3.3.1. Phƣơng pháp tạo kiến trúc 124
- Có các cách phân hoạch kiến trúc sau: • Phân hoạch ngang • Phân hoạch dọc Phương pháp tạo kiến trúc từ DFD Hình 4.10. Tạo kiến trúc từ lƣợc đồ DFFD Phân hoạch kiến trúc Tại sao cần phân hoạch kiến trúc: nhằm tạo ra phần mềm dễ kiểm thử, dễ bảo trì, hạn chế hiệu ứng phụ khi sửa đổi, dễ mở rộng. Phân hoạch dọc: Xác định các nhánh rẽ riêng biệt cho các chức năng chủchốt. Sử dụng các module điều khiển để điều phối thông tin giữa các chức năng. Hình 4.11. Phân hoạc dọc kiến trúc Phân hoạch ngang: 125
- Phân tầng các module ra các mức: module điều khiển (ra quyết định) và module thao tác (workers). Module ra quyết định được xếp ở tầng cao. Hình 4.12. Phân hoạch ngang kiến trúc 3.3.3.2. Tạo kiến trúc phần mềm Mục tiêu: Tạo ra kiến trúc được phân hoạch. Tạo kiến trúc phần mềm từ các biểu đồ DFD. Trong DFD có hai loại luồng dữ liệu tiêu biểu: Luồng chuyển đổi: xử lý tập trung Luồng giao dịch (Transaction Flow): định tuyến phân phối Phƣơng pháp chuyển đổi luồng chuyển đổi. Phân tích luồng chuyển đổi: 126
- Ở giai đoạn này cần cô lập, xác định biên của các module vào/ra; xác định các module xử lý tập trung. Chuyển chúng thành các module kiến trúc tương ứng. Thêm các module điều khiển nếu cần thiết. Vi chỉnh (refining) kiến trúc để nâng cao tính module. Luồng chuyển đổi tập trung là luồng mà có các dữ liệu đầu vào được tập trung xử lý ở một số tiến trình rồi cho kết quả đầu ra là các tiến trình thực hiện lưu trữ, truyền đi hay biểu diễn thông tin. Hình 4.13. Chuyển đổi luồng chuyển đổi Phƣơng pháp chuyển đổi luồng giao dịch. Phân tích luồng giao dịch: Xác định các luồng vào; Xác định các luồng thực hiện; Xác định trung tâm giao dịch (module phân phối). Biến đổi riêng rẽ từng luồng hành động. Luồng giao dịch là luồng mà mỗi đầu vào được nhận dạng và chuyển cho các tiến trình xử lý tương ứng với nó. Tiến trình làm nhiệm vụ nhận dạng và chuyển dữ liệu đến nơi cần gọi là trung tâm giao dịch. 127
- Hình 4.14. Chuyển đổi luồng giao dịch 4. Thiết kế giao diện ngƣời dùng Phần mềm cần có giao diện thân thiện với người sử dụng. Thiết kế giao diện là một khâu trong thiết kế phần mềm. Với đầu vào là tài liệu đặc tả yêu cầu. Đặc trưng của thiết kế giao diện: Hướng người dùng, cần làm bản mẫu, cần được người dùng đánh giá. Vai trò, tầm quan trọng: Một khâu không thể thiếu trong thiết kế phần mềm, người dùng đánh giá phần mềm qua giao diện. Thiết kế giao diện: Cần hướng đến người dùng; Che dấu chi tiết kỹ thuật bên trong; Kết hợp 3 mặt: người dùng, chức năng, công nghệ. Giao diện là phương tiện để người dùng sử dụng hệ thống. Giao diện thiết kế nghèo nàn người dùng dễ mắc lỗi. Giao diện thiết kế tồi là lý do nhiều phần mềm không được sử dụng. Giao diện trợ giúp người dùng làm việc với khả năng của họ. Giao diện trợ giúp tốt người dùng thành công. Giao diện trợ giúp tồi, người dùng khó khăn, thất bại. 128
- Hình 4.15. Các bƣớc thực hiện thiết kế giao diện 4.1. Nguyên tắc thiết kế giao diện: Cần phản ảnh vào thiết kế: + Kinh nghiệm, năng lực, nhu cầu của người dùng khả năng dùng bàn phím, mouse, tốc độ phản ứng, khả năng nhớ thao tác, sở thích, văn hóa, lứa tuổi:mầu sắc, ngôn ngữ, + Những hạn chế về mặt vất chất và tinh thần của người dùng (trí nhớ, vụng về, có thể mắc lỗi) Luôn bao gồm việc làm bản mẫu để người dùng đánh giá. Giao diện cần có các tính chất sau đây: + Tính thân thiện: thuật ngữ, khái niệm, thói quen, trình tự nghiệp vụ của người dùng. + Tính nhất quán: ví trí hiển thị, câu lệnh, thực đơn, biểu tượng, màu sắc, - cùng dạng + Ít gây ngạc nhiên + Có cơ chế phục hồi tình trạng trước lỗi + Cung cấp kịp thời phản hồi và trợ giúp mọi lúc, mọi nơi + Tiện ích tương tác đa dạng. Một số thiết bị giao diện phổ biến: Màn hình, bàn phím, mouse, bút từ, màn hình cảm biến, mic/speaker, smart cards, một số thiết bị đang phát triển (nhận dạng tiếng nói, chữ viết). 129
- Có các kiểu tương tác sau thông dụng: Thao tác trực tiếp, chọn thực đơn, chọn biểu tượng, điền vào mẫu biều, ngôn ngữ lệnh, ngôn ngữ tự nhiên Các loại giao diện gồm: + Giao diện dòng lệnh, đây là phương thức tương tác có sớm nhất, được thực hiện thông qua hàm chuẩn của ngôn ngữ do đó ít tốn tài nguyên hệ thống, do đó có khả năng tổ hợp lệnh để tạo các lệnh phức tạp. Với loại giao diện này chúng ta cần nhập lệnh/dữ liệu từ bàn phím. Với ưu điểm là dễ cài đặt so với giao diện đồ họa (GUI). Nhưng hạn chế là thao tác thực hiện tuần tự dẫn đến khó sửa thao tác trước và không phù hợp người dùng ít kinh nghiệm. + Giao diện đồ họa, đây là giao diện thông dụng trên máy tính PC, Apple, Unix . Với ưu điểm là dễ học, dễ sử dụng, hợp với người ít kinh nghiệm. Một lúc có thể thao tác trực tiếp trên nhiều cửa sổ hay nhiều vị trí trên cửa sổ, dó đó có thể thao tác song song. Người dùng có thể tương tác trực tiếp với thông tin: soạn thảo; nhập dữ liệu vào các form, nhận được tức thời kết quả thao tác. Tuy vậy phải cài đặt phức tạp, tốn tài nguyên phần cứng 4.2. Các kiểu tƣơng tác + Ngôn ngữ lệnh (Command Language) + Thực đơn (Menu) + Biểu mẫu (Form-fill) + Biểu tượng (Icon) + Ngôn ngữ tự nhiên (Natural Language) +Thao tác trực tiếp (Direct Manipulation) Tương tác bằng ngôn ngữ lệnh có ưu điểm là đơn giản, linh hoạt, thao tác nhanh. Nhưng đòi hỏi người dùng có kinh nghiệm. Loại tương tác này dễ gây nhầm lẫn, cần phải nhớ cú pháp, ngữ nghĩa lệnh Kiểu tương tác thực đơn (menu) đó là thao tác trực tiếp trên danh sách tùy chọn. Kiểu tương này có tính dễ hiểu, dễ dùng, có cấu trúc logic, hạn chế dùng bàn phím, tránh các lỗi kiểu gõ lệnh, dễ dàng tạo trợ giúp theo ngữ cảnh Kiểu tương tác biểu mẫu, với kiểu này người dùng cần điền thông tin vào các mục của biểu mẫu. Kiểu tương tác này khá hiệu quả với việc cập nhật và biểu 130
- diễn thông tin. Một biểu mẫu tốt cần có tiêu đề rõ ràng, nhóm hợp lý các trường, có các giá trị ngầm định, có màn hình tĩnh tại, ít thay đổi Kiểu tương tác biểu tượng sử dụng ký họa mang ý nghĩa trực quan. Người dùng tương tác qua con trỏ, do đó chiếm ít không gian trên màn hình, tiện lợi, dễ sử dụng, nhanh chóng hiểu được nội dung. Tuy vậy cũng khó tạo các biểu tượng, nhiều khi dễ gây lúng túng, hiểu nhầm Kiểu tương tác ngôn ngữ nói sử dụng ngôn ngữ tự nhiên để tương tác với hệ thống, do đó có tính tiện lợi cao. Nhưng rất khó thiết kế, đây là một hướng nghiên cứu thú vị của ngành trí tuệ nhân tạo. Các vẫn đề cần quan tâm khi thiết kế giao diện là: Phương pháp hiển thị thông tin; Thời gian phản hồi; Hiển thị thông báo; Tiện ích 4.3. Phƣơng pháp hiển thị thông tin Hiển thị bằng văn bản thường chính xác, dễ cài đặt. Hiển thị bằng đồ họa thì có tính trực quan, dễ dàng nhận ra mối quan hệ Với thời gian phản hồi cần quan tâm đến thời gian trung bình phản hồi với các thao tác, vì người dùng không thể đợi quá lâu, vì vậy cần làm sao để chứng tỏ hệ thống đang hoạt động. Độ biến thiên thời gian phản hồi không đồng đều cũng sẽ gây cảm giác hệ thống gặp lỗi. Khi xây dựng thông báo, đó là các phản hồi hệ thống đối với thao tác vì vậy các thông báo cần có nghĩa, dễ hiểu, hữu ích, tránh đưa ra các số hiệu, định dạng thông báo phải nhất quán. Thông báo lỗi cần chính xác, có tính hướng dẫn, xây dựng. Số lượng thông báo: khi đưa ra càng nhiều càng tốt, càng thân thiện. Nhưng cũng nên đưa ra một lượng tối thiểu là phù hợp. Thời điểm và thứ tự đưa ra thông báo, cũng như các yêu cầu phản hồi đối với thông báo cũng nên quan tâm. Cần có nhiều tiện ích trợ giúp người sử dụng. Các loại tiện ích là: trợ giúp trực tuyến và theo ngữ cảnh hay chú giải các thao tác, giao diện Các tài liệu trực tuyến như tra cứu chức năng hệ thống. Các macro nhằm tự động hóa thao tác. Tính kỹ nghệ của giao diện: Giao diện là phần tử dễ thay đổi như thay đổi qui trình, phương thức thao tác, thay đổi môi trường (phần cứng, hệ điều hành), 131
- nâng cấp (đẹp hơn, dễ sử dụng hơn). Giao diện phải dễ sửa đổi. Giao diện phải có tính khả chuyển. Giao diện nên độc lập với xử lý thông tin 4.4. Một số tiêu chuẩn giao diện Thời gian đáp ứng của hệ thống: giá trị trung bình và độ lệch phù hợp. Phương tiện trợ giúp người sử dụng: tích hợp và add-on Kiểm soát thông tin lỗi: hiển thị cả nguyên nhân lỗi và cách khắc phục. Đặt tên nhãn: ngắn gọn và gợi nhớ. Công cụ thiết kế giao diện nên có những tính năng sau: Quản lý thiết bị nhập (bàn phím, chuột) Hiệu chỉnh thông tin input Kiểm soát lỗi và hiển thị thông báo lỗi Cung cấp trợ giúp và hiển thị thông báo nhắc nhở Cung cấp feedback (ví dụ như tự động hiển thị ký tự gõ vào) Kiểm soát cửa sổ và vùng, khả năng cuộn Thiết lập giao tiếp giữa chương trình với giao diện (ví dụ: các hàm đáp ứng) Cách ly chương trình với các hàm quản lý giao diện Cho phép tuỳ biến giao diện Một số hướng dẫn chung: Nên đồng nhất (menu, lệnh, hiển thị ) Nên cung cấp feedbackcho người dùng Yêu cầu xác nhận những tác vụ mang tính phá hoại (xoá file, account) Nên hỗ trợ UNDO, REDO Hạn chế lượng thông tin phải ghi nhớ giữa 2 tác vụ liên tiếp. 132
- Tối ưu trong trình bày hộp thoại và di chuyển mouse Chấp nhận lỗi từ phía người sử dụng Cung cấp trợ giúp trực tuyến Dùng động từ đơn giản và ngắn gọn để đặt tên các lệnh Đối với thông tin hiển thị Chỉ hiển thị những thông tin phù hợp với ngữ cảnh hiện tại Dùng tên, từ viết tắt và màu gợi nhớ Cho phép tương tác trực quan Tạo thông báo lỗi có ý nghĩa Hiển thị dữ liệu ở nhiều dạng khác nhau trong cửa sổ Thiết lập biểu diễn tượng tự Sử dụng không gian màn hình một cách tối ưu Đối với thông tin input Hạn chế input trực tiếp (có thể chọn lựa từ một số dữ liệu có sẵn) Nên đồng nhất giữa thông tin input và hiển thị Nên cho phép tuỳ biến input Cấm các chức năng không thích hợp trong ngữ cảnh hiện tại Cho phép input ở nhiều dạng khác nhau Để cho người sử dụng kiểm soát dòng sự kiện tương tác Tự động tính các giá trị inputcho người sử dụng nếu có thể 5. Thiết kế thủ tục Đây là giai đoạn thiết lập thuật giải cho các module đã kiến tạo sao cho có thể dễ dàng mã hoá bằng ngôn ngữ lập trình có cấu trúc. Có thể biểu diễn thuât giải bằng: Lưu đồ thuật giải; Ký hiệu dạng bảng; Ngôn ngữ tựa lập trình (Pseudo Language - PDL). 133
- NGÔN NGỮ PDL Ngôn ngữ PDL vay mượn từ vựng của ngôn ngữ tự nhiên và cú pháp của ngôn ngữ lập trình có cấu trúc. Nó có các tính chất sau: Cú pháp chặt chẽ của các từ khoá hỗ trợ đặc tả cấu trúc, khai báo dữ liệu, phân chia module. Cú pháp tự do của ngôn ngữ tự nhiên giúp miêu tả xử lý. Có phương tiện mô tả dữ liệu đơn cũng như dữ liệu có cấu trúc. Có cơ chế định nghĩa chương trình con và phương cách gọi các chương trình con. Ví dụ: Procedure AnalyzeTriangle( a, b, c: in real; type: out string) begin sort a, b, c so that a >= b >= c; if (c>0and a<b+c) if (a=c) type := “Equilateral” else if (a=bor b=c) type := “Isosceles” else if ( a*a = b*b + c*c ) type := “Right” else type := “Scalene” else type := “Error” end 134
- 6. Thiết kế hƣớng đối tƣợng Trong phần này trình bày một số vấn đề cốt lõi của thiết kế phần mềm theo mô hình RUP. Để hiểu rõ hơn, các bạn có thể đọc thêm trong các tài liệu môn Phân tích thiết kế hệ thống với UML. 6.1. Giới thiệu Trong giai đoạn này, như trên đã nói chúng ta cần trả lời câu hỏi: How? Ở giai đoạn này cần thiết lập mô hình động (dynamic modeling) và chi tiết hoá mô hình tĩnh. Giai đoạn thiết kế quan tâm đến “HOW”, đó là: Thứ tự các thông điệp trao đổi, thông số của thông điệp Thuật giải của các tác vụ đáp ứng Cấu trúc dữ liệu cho các thuộc tính Framework (console, document/view, 3-tier ) Thiết kế cũng chịu ảnh hưởng từ: Ngôn ngữ lập trình và thư viện lập trình (Hỗ trợ Vector, List, Map hay không? Hỗ trợ template hay không? ) Kiến trúc hệ thống (COM, CORBA hay EJB) Do đó cần thiết lập mô hình động (dynamic modeling) và chi tiết hoá mô hình tĩnh. Một số vấn đề của thiết kế hướng thủ tục gặp phải: Với thiết kế hướng thủ tục thì dữ liệu là chung cho cả hệ thống, do đó mọi thủ tục thao tác trên cơ sở dữ liệu chung sẽ đặc trưng cho trạng thái toàn hệ thống. Thao tác sai của một thủ tục lên dữ liệu sẽ gây sai lan truyền sang phần khác sử dụng dữ liệu này. Sửa đổi một thủ tục sẽ có nguy cơ ảnh hưởng tới phần khác liên quan. Thay đổi cấu trúc dữ liệu dẫn đến thay đổi tổng thể hệ thống, vì vậy dữ liệu cần tổ chức tốt. Với các hệ thống lớn, phức tạp việc bảo trì sẽ rất khó khăn. Thiết kế hướng đối tượng (OOD) là một phương pháp hiện đang trở nên phổ biến. Phương pháp này là một cách tiếp cận khác, nhìn nhận hệ thống theo các quan điểm: 135
- Hệ thống là một tập các đối tượng có tương tác với nhau, mỗi đối tượng bao gói cả dữ liệu và các xử lý trên chúng. Tương tác giữa các đối tượng bằng truyền thông báo. Các đối tượng có thể kế thừa nhau. Phương pháp OOD có các ưu điểm sau: Dễ bảo trì: các đối tượng được hiểu như các thực thể hoạt động độc lập o Bao gói thông tin o Liên kết lỏng lẻo (trao đổi bằng truyền thông báo) Dễ tái sử dụng: o •Độ độc lập cao o •Có khả năng kế thừa Dễ hiểu: một vài hệ thống, có sự ánh xạ tường minh giữa thực thể trong thế giới thực và đối tượng của hệ thống. Nội dung của OOD bao gồm: o Xác định các tập đối tượng (gọi là lớp) và các đặc trưng của chúng. o Phân định vai trò và trách nhiệm của chúng trong hệ thống o Thiết lập được sự tương tác của chúng để thực hiện chức năng của hệ thống phần mềm đặt ra. Nhìn chung có thể thấy, phân tích hướng đối tượng bao gồm: 1. Mô tả nghiệp vụ 2. Xây dựng mô hình nghiệp vụ + Mô hình miền lĩnh vực + Mô hình ca sử dụng 3. Phân tích xác định cấu trúc (khởi thảo) + Làm mịn mô hình ca sử dụng + Xác định các gói ca sử dụng, giao diện 4. Phân tích một ca sử dụng + Tìm các lớp phân tích 136
- + Xác định liên kết giữa các lớp 2. Phân gói lại các lớp phân tích (nhằm tăng cường kiến trúc) + Tách các lớp dịch vụ và ứng dụng + Phân gói các lớp phân tích theo tầng 3. Xác định và mô tả các giao diện + Xác định giao diện giữa các gói + Xác định liên kết giữa các gói Thiết kế hướng đối tượng bao gồm: 1. Thiết kế biểu đồ tương tác mỗi gói + Xác định lại các lớp + Xây dựng biểu đồ tương tác 2. Phát triển biểu đồ lớp thiết kế + Chuyển biểu đồ cộng tác sang biểu đồ lớp + Hoàn thiện các quan hệ công tác 3. Thiết kế các lớp + Thiết kế các thuộc tính + Thiết kế các phương thức + Thiết kế cơ sở dữ liệu 4. Thiết kế giao diện người dùng. Các mô hình thiết kế sẽ bao gồm: Mô hình cấu trúc gói; Mô hình cộng tác; Mô hình lớp; Đặc tả lớp, giao diện. 6.2. Khái niệm mô hình động Lược đồ lớp chỉ mô tả khía cạnh tĩnh của hệ thống. Hành vi của hệ thống được mô tả bằng mô hình động bao gồm: Tương tác giữa các đối tượng: cộng tác hay trình tự; Trạng thái của đối tượng/lớp; Quá trình hoạt động của lớp/đối tượng. 137
- Tƣơng tác giữa các đối tƣợng Đối tượng tương tác với nhau (interaction) bằng cách gửi/nhận các thông điệp kích hoạt. Các actor cũng có thể gửi thông điệp kích hoạt đến đối tượng. Kích hoạt khiến một tác vụ thực thi, một đối tượng được tạo ra hay huỷ đi, hoặc gây ra một tín hiệu. Ở đây cụ thể các thông điệp (message) là đặc tả của kích hoạt. Các loại thông điệp: Đơn giản; Đồng bộ; Bất đồng bộ; Thông điệp trả về của lời gọi hàm. Thông điệp bao gồm: Tên dịch vụ được yêu cầu và thông tin dùng để thực hiện dịch vụ. Thường thì, thông điệp được cài đặt bằng lời gọi hàm, có dạng: •Tên = tên hàm •Thông tin = danh sách tham số. Sự cộng tác Sự cộng tác (collaboration) được hiểu là tập hợp các thành phần tham gia và quan hệ giữa chúng. Các thành phần tham gia là vai trò mà đối tượng/lớp đóng vai khi tương tác với nhau. Các vai trò của đối tượng thường chỉ có nghĩa đối với một mục đích nào đó. Các lược đồ cộng tác (collaboration diagram) sẽ được thiết lập để cụ thể hoá một use-case hoặc một tác vụ. Lược đồ cộng tác là một đồ thị liên kết các vai trò. Các quan hệ liên kết được dùng để kết nối các vai trò với nhau. Chúng ta có thể chỉ ra tên vai trò cho các liên kết. Các tương tác được thể hiện bằng gửi/nhận thông điệp. Mỗi thông điệp được thể hiện bằng mũi tên (tùy theo loại thông điệp mà nét vẽ của các mũi tên trong UML có khác đi) cộng với phần đặc tả của thông điệp. Các thông điệp được đánh số theo kiểu phân cấp: + 3.4.2 xảy ra sau 3.4.1 và cả hai được lồng trong 3.4 + 3.4.3a và 3.4.3b xảy ra đồng thời và được lồng trong 3.4 Cú pháp tổng quát của thông điệp: precedessor guard-condition sequence-expression returnvalue := message-name argument-list. Ví dụ: 2/ 1.3.1: p := find(specs) 138
- 1.1, 4.2/ 3.2 *[i:=1 6]: invert(x, color) Lược đồ cộng tác có thể được thiết lập ở một trong 2 dạng: + Dạng cụ thể: mỗi vai trò được biểu diễn bằng một ký hiệu của đối tượng cụ thể, các thông điệp được trao đổi trên các đường liên kết + Dạng đặc tả: mô tả các lớp; các đường liên kết được ánh xạ vào các thông điệp. Việc thiết lập lược đồ cộng tác giúp cụ thể hoá (realize) các use-case và nhận diện thêm một số tác vụ của các đối tượng/lớp phân tích. Ví dụ: lược đồ cộng tác mức cụ thể cho use-case Login của hệ thống đăng ký môn học tín chỉ qua WEB. Ví dụ: Lược đồ cộng tác mức cụ thể cho use-case Registers course của hệ thống đăng ký môn học tín chỉ qua WEB 139
- 6.3. Mô tả trình tự Lược đồ cộng tác mô tả sự tương tác giữa các đối tượng và actor trong use- case theo khía cạnh không gian. Để nhấn mạnh tính trình tự của các tương tác, chúng ta dùng lược đồ tuần tự (sequence diagram). Lược đồ tuần tự miêu tả các đối tượng tương tác với nhau theo thời gian sống của nó. Các thông điệp được trao đổi theo trình tự thời gian. Các mối liên kết không được thể hiện trong lược đồ. Lược đồ tuần tự có 2 dạng: + Dạng tổng quát: thể hiện cả vòng lặp và rẽ nhánh + Dạng cụ thể: miêu tả một kịch bản cụ thể Thời gian sống của mỗi đối tượng được mô tả theo một đường thẳng đứng. Thông thường thời gian trôi theo chiều từ trên xuống dưới. Thường thì chúng ta ít khi quan tâm đến khoảng thời gian, thường chỉ quan tâm đến trình tự mà thôi. Thanh hình chữ nhật mô tả sự thực thi của một tác vụ để đáp ứng lại thông điệp gửi đến. Độ dài của thanh chữ nhật phản ánh thời gian thực thi của tác vụ và tính chất lồng nhau giữa chúng. Các dòng text phụ trợ (mô tả tác vụ, ràng buộc thời gian ) được viết ở lề trái. Ví dụ: Lược đồ tuần tự dạng cụ thể cho use-case Login của hệ thống đăng ký môn học tín chỉ qua WEB. 140
- Ví dụ: Lược đồ tuần tự dạng cụ thể cho use-case Register Courses 6.4. Lƣợc đồ trạng thái Chuẩn UML đưa ra lược đồ trạng thái để biểu diễn hành vi của một phần tử bất kỳ bằng cách chỉ ra đáp ứng của nó đối với các sự kiện bên ngoài. Thông thường lược đồ trạng thái được áp dụng cho đối tượng/lớp nhằm biểu diễn hành vi của lớp. Trạng thái của mỗi đối tượng ít nhiều sẽ bị thay đổi trong suốt chu kỳ sống của đối tượng. Trạng thái đơn giản là một tình trạng trong đời sống đối tượng hoặc một tương tác của đối tượng mà theo đó đối tượng thoả một điều kiện, thực hiện một công việc hoặc đợi một sự kiện nào đó. Thông thường mỗi đối tượng nằm ở một trạng thái trong một khoảng thời gian nhất định nó sẽ dịch chuyển từ 141
- trạng thái này sang trạng thái khác. Trạng thái tổng hợp là trạng thái có thể được phân rã về các trạng thái đơn giản. Trong UML ký hiệu của trạng thái là một hình chữ nhật tròn góc và được chia làm nhiều phần phân cách nhau bằng các đoạn thẳng nằm ngang gồm: + Phần tên. + Phần miêu tả các hành động bên trong Ở đây, tên trạng thái là duy nhất trong lược đồ; có thể không có (trạng thái vô danh). Các hành động bên trong: các hành động hoặc tác vụ được thực hiện khi đối tượng nằm ở trạng thái đang xét; có cú pháp như sau: action-label ’/’ action- expression. Một số nhãn hành động (action-label) được quy ước trước: + entry: thực hiện hành động tại thời điểm bắt đầu trạng thái + exit: thực hiện hành động tại thời điểm kết thúc trạng thái + do: thực hiện hành động suốt trạng thái hoặc cho đến khi kết thúc nó + include: triệu gọi một máy trạng thái con khác Các nhãn hành động khác chỉ ra sự kiện kích hoạt hành động tương ứng trong biểu thức hành động (action-expression). Cú pháp của biểu thức hành động: event-name ’(‘ parameter-list ’)’ ’[‘guard-condition’]’ ’/’ action- expression Trạng thái bắt đầu: khi đối tượng được tạo ra hoặc trạng thái tổng hợp được xác định; ký hiệu: Trạng thái kết thúc: khi đối tượng bị huỷ bỏ hoặc trạng thái tổng hợp trở nên không xác định; ký hiệu: Trạng thái tổng hợp (composite) được phân rã thành nhiều trạng thái con đồng thời hoặc các trạng thái con loại trừ nhau. Sự kiện (event) kích hoạt dịch chuyển trạng thái, có thể là: + Một điều kiện trở nên đúng (chú ý khác với guard-condition) + Một đối tượng nhận tín hiệu từ đối tượng khác + Một phép gọi tác vụ 142
- + Một khoảng thời gian đã trôi qua kể từ một sự kiện nào đó Cú pháp của sự kiện: event-name ’(’ parameter-list ’)’ Sự kiện có tầm vực thuộc về package chứa lớp đang mô tả lược đồ trạng thái, chứ không chỉ thuộc về riêng lớp đó. Dịch chuyển trạng thái là quan hệ giữa hai trạng thái theo đó đối tượng đang ở trạng thái thứ nhất sẽ chuyển sang trạng thái thứ hai đồng thời sẽ thực hiện một số hành động khi sự kiện tương ứng xảy ra và thoả mãn một số điều kiện nhất định. Được ký hiệu như một mũi tên hướng từ trạng thái nguồn đến trạng thái đích và được gán nhãn. Nhãn có cú pháp: event-signature ’[’ guard-condition ’]’ ’/’ action- expression Ví dụ: lược đồ trạng thái của lớp Message 6.5. Lƣợc đồ hoạt động Lược đồ hoạt động (activity diagram) là một biến thể của lược đồ trạng thái trong đó trạng thái là sự thực thi một hành động và sự dịch chuyển được kích hoạt khi hành động hoàn tất. Được dùng để mô tả một thủ tục hay thuật giải, tập trung vào các hành động. Mỗi hành động được ký hiệu bằng hình vẽ như sau: . Quyết định rẽ nhánh: hình thoi có một đường vào và nhiều nhánh ra, mỗi nhánh được gán một guard-condition. Các nhánh ra được nhập lại bằng một hình thoi khác. Mỗi “đường bơi” (swimlane) đại diện cho một lớp hoặc một actor. Ví dụ: lược đồ hoạt động cho tác vụ submit của LoginForm 143
- Ví dụ: lược đồ hoạt động cho tác vụ submit của RegisterForm 6.6. Nhận diện thêm một số lớp thiết kế Mô hình thiết kế phần nào chịu ảnh hưởng từ ngôn ngữ lập trình, thư viện hỗ trợ, framework, hệ điều hành và loại máy tính. Một số lớp sẽ xuất hiện khi áp dụng những yếu tố trên. + Ngôn ngữ lập trình: template, CObject + Thư viện hỗ trợ: lớp Date, Time, List, Map, vector, iostream + Framework: Applet, Panel, CDocument, CView, HttpServlet 144
- + Hệ điều hành: các lớp thao tác file, mở cầu nối network, các phần tử giao diện . Một số lớp khác xuất hiện làm chức năng duyệt (iterate) một lớp khác hay thực hiện các tính toán phức tạp Chúng ta sử dụng trực tiếp các lớp do thư viện hay ngôn ngữ cung cấp, hoặc tạo ra lớp mới bằng cách thừa kế hay tích hợp các lớp có sẵn, ví dụ: CArray . Hay bổ sung các lớp mới vào lược đồ lớp đồng thời cập nhật các mối quan hệ mới (bao gộp, phụ thuộc). 6.7. Đặc tả chi tiết các thuộc tính Trong mô hình phân tích cần phải chỉ rõ kiểu (hoặc cấu trúc dữ liệu) và mức độ truy xuất của các thuộc tính. Có thể chọn một lớp cung cấp bởi thư viện lập trình để cụ thể hoá kiểu hay cấu trúc dữ liệu nhằm bổ sung lớp của thư viện và quan hệ bao gộp vào lược đồ lớp 6.8. Nhận diện chính xác các tác vụ Các lược đồ mô tả hành vi (cộng tác, tuần tự, trạng thái, hành động) giúp nhận diện chính xác các tác vụ của các lớp. Dựa vào các thông điệp hay hành động để xác định signature của các tác vụ. Ví dụ: nhận diện một số tác vụ của lớp Database: setReg( reg: Registration ); fetchReg( crsOff: CourseOffering) : Registration; Ví dụ: nhận diện một tác vụ của lớp ChildView 145
- render( ); store( ); load( ); model( map: FieldMap, param ); Ví dụ: nhận diện một tác vụ của lớp LoginForm submit( uname: String; psswd: String ); makeWelcome( ); 6.9. Hoàn chỉnh lƣợc đồ lớp Nhằm cập nhật các lớp mới, thuộc tính, tác vụ và các mối quan hệ mới. Trong UML định nghĩa quan hệ phụ thuộc (dependency) giữa 2 lớp hoặc package có nghĩa là: việc thay đổi ở một lớp, package kéo theo thay đổi ở lớp, package kia. Ký hiệu của quan hệ phụ thuộc là mũi tên đứt nét: lớp, package ở phía đuôi mũi tên phụ thuộc vào lớp, packagephía đầu mũi tên. Một số stereotype quy ước trước: >, >, >, >, >, >, >. Ví dụ: thêm lược đồ lớp cho hệ thống đăng ký môn học 146
- Chúng ta nên sử dụng package để tổ chức các phần tử liên quan với nhau, chẳng hạn như trong các ví dụ trên là: các lớp về bản đồ địa hình, về thông tin sinh viên/giảng viên, về cửa sổ giao diện, về các servlet Các package thể hiện kiến trúc phần mềm, thông thường chịu ảnh hưởng từ framework (Document/View, 3-tiers ). Mỗi package chứa một hoặc một vài lược đồ lớp, trong đó có thể tham chiếu đến một số lớp thuộc các package khác. 6.10. Mẫu thiết kế (Pattern) Khi thiết kế có nhiều trường hợp có sự tương tự. Khi đó một mô tả giải pháp của một trường chung có thể áp cho các trường hợp khác tương tự, gọi là mẫu thiết kế. Mô tả một mẫu bao gồm: + Vấn đề đặt ra (ngữ cảnh) + Giải pháp + Kết quả + Các mẫu liên quan + Mô hình mẫu Năm mẫu phần mềm thường được sử dụng nhiều nhất là: + Expert (chuyên gia) + Creator (bộ tạo lập) + Low Coupling (ghép nối thâp) + High Cohension (kết dính cao) 147
- + Controller (bộ điều khiển) Mô hình mẫu của mẫu chuyên gia: + Vấn đề: Nguyên tắc gán trách nhiệm cho 1 đối tượng là gì? + Giải pháp: Hãy gán trách nhiệm cho đối tượng có đủ thông tin để thực hiện trách nhiệm đó. + Kết quả: Giảm sự phụ thuộc vào lớp khác + Mẫu liên quan: kết dính cao, ghép nối lỏng. Ví dụ: Mô hình mẫu chuyên gia: bài toán bán hàng + Gán trách nhiệm cho mathangban tính tổng tiền bán một mặt hàng tienIterm(upc, soluong) vì nó có thông tin soluong và lấy giá từ danhmuc + Gán trách nhiệm cho lanban tính tổng tiền một lần bántongtien(sott) vì nó biết số tiền từng mặt hàng thuộc lần bán Lợi ích của việc sử dụng mẫu thiết kế là: + Cho ta giải pháp của vấn đề không cần tìm kiếm + Dùng lại cái đã có, đỡ tốn thời gian vàcông sức + Cho thiết kế tốt và chất lượng hệ thống cao. Tổng kết Các cơ sở của thiết kế phần mềm: trừu tượng hoá, tính chế từng bước, phân chia module, cấu trúc dữ liệu, chương trình con, che dấu thông tin. Phân chia module hiệu quả: tăng độ kết dính và giảm sự liên kết Thiết kế cổ điển bao gồm 4 công đoạn: thiết kế dữ liệu, thiết kế kiến trúc, thiết kế giao diện người máy và thiết kế thủ tục. 148
- Mô hình thiết kế bao trùm cả khía cạnh tĩnh và động của hệ thống phần mềm cần xây dựng. Trong UML hỗ trợ một số lược đồ giúp mô tả khía cạnh động: cộng tác, tuần tự, trạng thái, hành động. Việc mô tả chính xác thuộc tính và tác vụ, bổ sung một số lớp thiết kế nhằm hoàn thiện khía cạnh tĩnh. Việc thiết lập các package hướng đến việc tạo thành kiến trúc phần mềm. Câu hỏi và bài tập 1. Thiết kế phần mềm là gì? 2. Nêu các nguyên lý thiết kế phần mềm? 3. Nêu các loại thiết kế và giải thích nội dung của nó? 4. Giải thích mộ số khái niệm cơ bản của thiết kế: + trừu tượng? + làm mịn? + module hoá? + thủ tục? + che dấu thông tin? 5. Vẽ sơ đồ mô tả mỗi quan hệ giữa số module và chi phí phát triển 6. Các đặc trưng của một thiết kế tốt? 7. Các tiêu chí kỹ thuật đánh giá một thiết kế tốt 8. Lợi ích của hệ thống có kiến trúc tốt 9. Lợi ích của việc module hoá trong thiết kế phần mềm làgì? 10. Lợi ích của việc che dấu thông tin làgì? 11. Có các độ đo chất lượng thiết kế nào? 12. Ghép nối là gì? Kể các loại ghép nối theo mức độ chặt (tồi) dần? 13. Kết dính là gì? Kể các loại kết dính theo mức độ chặt giảm (kém) dần? 14. Thế nào là tính hiểu được? 15. Thế nào là tính thích nghi được? 149
- 16. Thiết kế hướng đối tượng hướng đến chất lượng tốt ở những mặt nào? 17. Tầm quan trọng của giao diện phần mềm? 18. Những yếu tố người dùng nào cần quan tâm khi thiết kế giao diện? 19. Mô tả tiến trình thiết kế giao diện? 20. Các nguyên tắc thiết kế giao diện? 21. Có những loại thiết bị nào có thể sử dụng để tương tác với hệ thống? 22. Có những loại giao diện nào? Giải thích nội dung, ý nghĩa, ưu nhược điểm của nó? 23. Có những đặc trưng gì cần quan tâm khi thiết kế giao diện? Giải thích nội dung của nó? 24. Hãy thực hiện so sánh đối chiếu các vấn đề đã học lý thuyết với hồ sơ thực tiễn mà nhóm của bạn đã thu thập được. 25. Thực hiện các thiết kế dữ liệu, thiết kế thuật toán, thiết kế giao diện trên một phần mềm tự chọn của nhóm. 150
- Chƣơng 5. LẬP TRÌNH Mục đích Trình bày các nguyên lý của lập trình. Yêu cầu + Hiểu được các nguyên lý lập trình. + Vận dụng các khái niệm như phong cách lập trình, đặc trưng ngôn ngữ vào việc học tập các môn học lập trình Các vấn đề liên quan đến lập trình đã được sinh viên học khá nhiều trong các môn học về ngôn ngữ, kỹ thuật lập trình Vì vậy trong chương này chúng tôi chỉ đề cập khái quát đến các nguyên lý lập trình, giúp cho người đọc một cái nhìn tổng quan lại các vấn đề lập trình mà một kỹ sư phần mềm cần nắm. 1. Khái niệm 1.1. Khái niệm lập trình hiệu quả Lập trình là giai đoạn cài đặt phần mềm, đây là giai đoạn thể hiện các kết quả của bước thiết kế bằng các công cụ của ngôn ngữ lập trình để đi đến sản phẩm phần mềm. Có thể xem đây là giai đoạn thực thi ở mức vật lý của quá trình xây dựng phần mềm. Tiến trình lập trình là một sự liên lạc thông qua ngôn ngữ lập trình. Lập trình là bước cốt lõi trong tiến trình kỹ nghệ phần mềm. Sản phẩm phần mềm tốt khi có phân tích tốt, thiết kế tốt và lập trình tốt, kiểm thử chặt chẽ. Do đó cần có kỹ thuật lập trình tốt chuyên nghiệp (tuân theo các chuẩn), ổn định, hiệu quả. Lập trình hiệu quả hơn thì sản phẩm rẻ tiền hơn, do tốc độ phát triển cao hơn (như: năng lực biểu diễn cao hơn, khả năng sử dụng lại cao hơn), dễ bảo trì hơn (dễ hiểu, dễ sửa đổi, thích nghi), chất lượng cao hơn (sử dụng các cấu trúc an toàn hơn). 1.2. Tiến hóa của phƣơng pháp lập trình 151
- + Lập trình tuần tự (tuyến tính) + Lập trình có cấu trúc (thủ tục) + Lập trình hướng chức năng + Lập trình hướng đối tượng + Kỹ thuật thế hệ thứ 4 2. Ngôn ngữ lập trình Các đặc trưng của ngôn ngữ lập trình sẽ quyết định miền ứng dụng của ngôn ngữ. Miền ứng dụng là yếu tố chính để chúng ta lựa chọn ngôn ngữ cho một dự án phần mềm. Lựa chọn ngôn ngữ, dựa vào: + Đặc trưng của ngôn ngữ - Năng lực của ngôn ngữ (kiểu biến, các cấu trúc ) - Mức độ hỗ trợ của các công cụ - Dễ dịch thiết kế sang chương trình, - Có trình biên dịch hiệu quả, - Khả chuyển chương trình gốc, - Dễ bảo trì. + Miền ứng dụng của ngôn ngữ •- Lập trình hệ thống •- Nghiệp vụ, kinh doanh •- Khoa học kỹ thuật •- Trí tuệ nhân tạo + Năng lực, kinh nghiệm của nhóm phát triển + Yêu cầu của khách hàng Tính khả chuyển Đây là một yếu tố quan trọng của ngôn ngữ, giúp cho chương trình viết bởi nó có thể thực thi được khi thay đổi phần cứng, thay đổi hệ điều hành 152
- Năng lực của ngôn ngữ • Có cấu trúc, câu lệnh phong phú • Hỗ trợ nhiều kiểu dữ liệu • Hỗ trợ con trỏ, đệ qui • Hỗ trợ hướng đối tượng • Thư viện phong phú Hỗ trợ công cụ +Trình biên dịch hiệu quả •biên dịch tốc độ cao khả năng tối ưu cao khai thác các tập lệnh, kiến trúc phần cứng mới + Các công cụ trợ giúp hiệu quả •editor, debugger, linker, make •IDE (Integrated Develop Environment) Miền ứng dụng và ngôn ngữ + Phần mềm hệ thống: hiệu quả, vạn năng, dễ mở rộng chẳng hạn như ngôn ngữ C, C++ + Hệ thời gian thực: C, C++, Ada, Assembly + Phần mềm nhúng: C++, Java, C# + Phần mềm khoa học kỹ thuật cần tính toán chính xác, có thư viện toán học mạnh, dễ dàng song song hóa thì ngôn ngữ Fortran vẫn là phổ biến. + Phần mềm nghiệp vụ: Oracle, DB2, SQL Server, MySQL + Trí tuệ nhân tạo: Lisp, Prolog, OPS5, + Lập trình Web/CGI: •Perl, ASP, PHP, Java, Java script, Python, ASP.NET, PHP C thường là một ngôn ngữ hay được chọn cho việc phát triển phần mềm hệ thống. 153
- Trong các ứng dụng thời gian thực chúng ta hay gặp các ngôn ngữ nh-Ada, C, C++ và cả hợp ngữ do tính hiệu quả của chúng. Các ngôn ngữ này và Java cũng được dùng cho phát triển phần mềm nhúng. Trong lĩnh vực khoa học kỹ thuật thì FORTRAN với khả năng tính toán với độ chính xác cao và thư viện toán học phong phú vẫn còn là ngôn ngữ thống trị. Tuy vậy, PASCAL và C cũng được dùng rộng rãi. COBOL là ngôn ngữ cho ứng dụng kinh doanh và khai thác CSDL lớn nhưng cácngôn ngữ thế hệ thứ tư đã dần dần chiếm ưu thế. BASIC vẫn đang tiến hóa (VB.NET, C# ) và được đông đảo người dùng máy tính cá nhân ủng hộ mặc dù ngôn ngữ này rất hiếm khi được những người phát triển hệ thống dùng. Các ứng dụng trí tuệ nhân tạo thường dùng các ngôn ngữ như LISP, PROLOG hay OPS5, tuy vậy nhiều ngôn ngữ lập trình (vạn năng) khác cũng được dùng. Xu hướng phát triển phần mềm hướng đối tượng xuyên suốt phần lớn các miền ứng dụng đã mở ra nhiều ngôn ngữ mới và các dị bản ngôn ngữ qui ước. Các ngôn ngữ lập trình hướng đối tượng được dùng rộng rãi nhất là Smalltalk, C++, Java. Ngoài ra còn có Eiffel, Object- PASCAL, Flavos và nhiều ngôn ngữ khác. Với đặc trưng hướng đối tượng, tính hiệu quả thực hiện cũng như có nhiều công cụ và thư viện, C# hiện đang được sử dụng rộng rãi trong lĩnh vực phát triển các ứng dụng nghiệp vụ. Java cũng là một ngôn ngữ hướng đối tượng đang được sử dụng rộng rãi cho phát triển các dịch vụ Web và phần mềm nhúng vì các lý do độ an toàn cao,tính trong sáng, tính khả chuyển và hướng thành phần. Theo một số thống kê thì tốc độ phát triển một ứng dụng mới bằng Java cao hơn đến 2 lần so với các ngôn ngữ truyền thống như C hay thậm chí C++. Các ngôn ngữ biên dịch (script) với những câu lệnh và thư viện mạnh hiện đang rất được chú ý. VBScript, JavaScript, PERL đang được sử dụng rộng rãi trong lập trình Web. 3. Phong cách lập trình Phong cách lập trình được hiểu đó là các yếu cố cần có của người lập trình để xây dựng nên phong cách lập trình hữu hiệu như: cách đặt tên hàm và biến; cách xây dựng câu lệnh, cấu trúc chương trình; cách viết chú thích Mục đích là 154
- nhằm hướng tới phong cách làm cho mã nguồn dễ hiểu, dễ sửa đổi, an toàn (ít lỗi), do đó người khác có thể hiểu được, bảo trì được. Tại sao một chương trình cần phải dễ phải hiểu, bởi vì phần mềm luôn cần sửa đổi như sửa lỗi, nâng cấp để kéo dài tuổi thọ, nâng cao hiệu quả kinh tế. Do đó nếu không dễ hiểu, sẽ làm cho việc bảo trì tốn thời gian, chi phí cao Xét về cách viết chú thích trong chương trình, thì mọi điều nên được chú thích trong chương trình. Đó là: Mục đích sử dụng của các biến; Chức năng của khối lệnh, câu lệnh như các lệnh điều khiển, các lệnh phức tạp. Chú thích các module: Mục đích, chức năng của module Tham số, giá trị trả lại (giao diện) Các module thuộc cấp Cấu trúc, thuật toán Nhiệm vụ của các biến cục bộ Tác giả, người kiểm tra, thời gian Với cách đặt tên, chúng ta cần: Đặt tên biến, tên hàm có nghĩa, gợi nhớ Sử dụng các ký hiệu, từ tiếng Anh có nghĩa Làm cho dễ đọc Tránh đặt tên quá dài Thống nhất cách dùng Khi viết các câu lệnh, nên lưu ý: Các câu lệnh phải mô tả cấu trúc Làm đơn giản các lệnh, như mỗi lệnh nên viết trên một dòng, triển khai các biểu thức phức tạp, hạn chế truyền tham số là kết quả của hàm, biểu thức Tránh các cấu trúc phức tạp, như không sử dụng các lệnh if lồng nhau, điều kiện phủ định if not 155
- Hàm và biến cục bộ Chương trình cần được chia thành nhiều module (hàm) Không viết hàm quá dài Không dùng quá nhiều biến cục bộ Xử lý lỗi Phải lưu ý khi viết chương trình cần làm sao để có thể phát hiện lỗi trong khi thực hiện. Khi xử lý lỗi cần nhất quán trong xử lý: phân loại lỗi; thống nhất định dạng thông báo, phân biệt output và thông báo lỗi, các hàm thư viện nên tránh việc tự xử lý, tự đưa ra thông báo lỗi Output và thông báo lỗi Output là dữ liệu, còn được dùng để làm input cho các phần khác. Thông báo (lỗi) là các thông tin nhất thời về trạng thái hệ thống, lỗi và cách khắc phục. Cần tách output và thông báo lỗi. Ngoại lệ Đây là cách thức xử lý lỗi tiên tiến trong các ngôn ngữ hướng đối tượng. Module xử lý ném ra một ngoại lệ (đối tượng chứa thông tin lỗi), module điều khiển bắt ngoại lệ (nếu có). Với cách làm này tách phần xử lý lỗi khỏi phần cài đặt thuật toán thông thường, làm cho chương trình dễ đọc hơn, dễ dùng hơn, an toàn hơn. Đối với giao tiếp giữa các module cần thống nhất định dạng, kiểm tra tính hợp lệ của dữ liệu, làm đơn giản giao diện (giảm độ ghép nối). Tóm lại một phong cách lập trình tốt cần tuân theo các chuẩn thông dụng, chuẩn được chấp nhận rộng rãi hơn thì sẽ dễ hiểu hơn, cần chú giải đầy đủ mỗi khi lập trình không tuân theo chuẩn 4. Kỹ thuật lập trình Kỹ thuật lập trình tránh lỗi Kỹ thuật lập trình tốt dựa trên các yếu tố: 156
- + Lập trình cần có cấu trúc, chẳng hạn dùng các lệnh có cấu trúc; module hóa, hạn chế dùng các cấu trúc nguy hiểm. + Lập trình cần đóng gói/che dấu thông tin, như xây dựng kiểu dữ liệu trừu tượng, hạn chế thao tác trực tiếp lên thuộc tính + Tránh các cấu trúc nguy hiểm. Có hai cách tiếp cận chính hỗ trợ tránh lỗi là: - Lập trình có cấu trúc. - Phân quyền truy cập dữ liệu. Lập trình phòng thủ (Defensive programming) Lập trình phòng thủ là cách phát triển chương trình mà người lập trình giả định rằng các mâu thuẫn hoặc các lỗi chưa được phát hiện có thể tồn tại trong chương trình. Phải có phần mềm kiểm tra trạng thái hệ thống sau khi biến đổi và phải đảm bảo rằng sự biến đổi trạng thái là kiên định. Nếu phát hiện một mâu thuẫn thì việc biến đổi trạng thái là phải rút lại và trạng thái phải trở về trạng thái đúng đắn trước đó. Nhiều lệnh có khả năng sinh lỗi: – lệnh vào/ra – các phép toán – thao tác với bộ nhớ – truyền tham số sai kiểu Vì vậy với phong cách lập trình phòng thủ cần dự đoán khả năng xuất hiện lỗi. Khắc phục lỗi bằng cách: lưu trạng thái an toàn, quay lại trạng thái an toàn gần nhất, có một số trường hợp cần lưu ý: + Lệnh vào ra dữ liệu không hợp lệ, tràn bộ đệm (kiểu ký tự), lỗi thao tác file (sai tên, chưa được mở) + Các phép toán lỗi chia 0, tràn số, so sánh số thực (bằng nhau) + Thao tác bộ nhớ 157
- quên cấp phát, quên giải phóng bộ nhớ, thiếu bộ nhớ, sai địa chỉ, tràn bộ nhớ Lập trình thứ lỗi (Fault tolerance programming) Khi lập trình thì không thể loại trừ hoàn toàn lỗi vì vậy cần có các hệ thống có độ tin cậy đặc biệt. Lập trình dunng thứ lỗi có nghĩa là chấp nhận sự xuất hiện lỗi lập trình và cần phát hiện, khắc phục lỗi. Điều này khởi nguyên từ sự thứ lỗi của phần cứng. Thông thường, thứ lỗi được thực hiện bằng cách song song hóa các chức năng, kết hợp với bộ điều khiển thứ lỗi. Bộ điều khiển sẽ so sánh kết quả của các khối chương trình thực hiện cùng nhiệm vụ và sử dụng nguyên tắc đa số để chọn kết quả. Có bốn hoạt động cần phải tiến hành nếu hệ thống là thứ lỗi: i) Phát hiện lỗi. ii) Định ra mức độ thiệt hại. iii) Hồi phục sau khi gặp lỗi: Hệ thống phải hồi phục về trạng thái mà nó biết là an toàn. Cũng có thể là chỉnh lý trạng thái bị hủy hoại (hồi phục tiến), cũng có thể là lui về một trạng thái trước mà an toàn (hồi phục lùi). iv) Chữa lỗi: Cải tiến hệ thống để cho lỗi đó không xuất hiện nữa. Tuy nhiên trong nhiều trường hợp phát hiện được đúng nguyên nhân gây lỗi là rất khó khăn vì nó xẩy ra bởi một tổ hợp của thông tin vào và trạng thái của hệ thống. 5. Lập trình hƣớng hiệu quả thực hiện Phần mềm ngày càng phức tạp, đa dạng như mô phỏng, ứng dụng thời gian thực, phần mềm nhúng, trò chơi Vì vậy hiệu quả thực hiện chương trình luôn cần được xem xét, chẳng hạn thuật toán hiệu quả, kỹ thuật lập trình hiệu quả, ngôn ngữ lập trình hiệu quả, hiệu quả bộ nhớ, hiệu quả vào/ra Tính hiệu quả của chương trình gốc có liên hệ trực tiếp với tính hiệu quả của thuật toán được xác định trong thiết kế chi tiết. Tuy nhiên, phong cách lập trình có thể có một tác động đến tốc độ thực hiện và yêu cầu bộ nhớ. Tập hợp các hướng dẫn sau đây bao giờ cũng có thể áp dụng được khi thiết kế chi tiết được dịch thành chương trình: 158
- - Đơn giản hóa các biểu thức số học và lôgic trước khi đi vào lập trình. -Tính cẩn thận từng chu kỳ lồng nhau để xác định liệu các câu lệnh hay biểu thức có thể được chuyển ra ngoài hay không. - Khi có thể, hãy tránh dùng mảng nhiều chiều - Khi có thể hãy tránh việc dùng con trỏ và danh sách phức tạp. - Dùng các phép toán số học “nhanh” - Không trộn lẫn các kiểu dữ liệu, cho dù ngôn ngữ có cho phép điều đó - Dùng các biểu thức số học và logic bất kì khi nào có thể được. Bên cạnh đó 2 yếu tố quan trọng trong việc lập trình hướng đến hiệu quả là: + Hiệu quả bộ nhớ: Tính hiệu quả bộ nhớ phải được tính đến. Nói chung, tính cục bộ của chương trình hay việc bảo trì lĩnh vực chức năng qua các kết cấu có cấu trúc là một phương pháp hay làm giảm bộ nhớ và do đó làm tăng tính hiệu quả. Hạn chế bộ nhớ trong phát triển phần mềm nhúng là mối quan tâm rất thực tế. Nếu yêu cầu hệ thống cần tới bộ nhớ tối thiểu (như sản phẩm giá thấp, khối lượng lớn) thì trình biên dịch ngôn ngữ cấp cao phải được quan tâm đến. + Hiệu quả vào ra: Các thiết bị vào ra thường có tốc độ chậm hơn rất nhiều so với khả năng tính toán của máy tính và tốc độ truy cập bộ nhớ trong. Việc tối ưu vào ra có thể làm tăng đáng kể tốc độ thực hiện. Bước lập trình là một tiến trình chuyển hóa thiết kế chi tiết thành chương trình được biến đổi thành các lệnh mã máy thực hiện được. Các đặc trưng của ngôn ngữ lập trình có ảnh hưởng lớn đến quá trình xây dựng, kiểm thử cũng như bảo trì phần mềm. Phong cách lập trình quyết định tính dễ hiểu của chương trình gốc. Các yếu tố của phong cách bao gồm việc làm tài liệu bên trong, phương pháp khai báo dữ liệu, thủ tục xây dựng câu lệnh, và kỹ thuật lập trình vào/ra. 159
- Lập trình cần hướng tới hiệu quả thực hiện, tức là tiết kiệm tài nguyên phần cứng (mức độ sử dụng CPU, bộ nhớ ). Mặc dầu tính hiệu quả có thể là yêu cầu cực kì quan trọng, chúng ta nên nhớ rằng một chương trình hoạt động hiệu quả mà lại không dễ hiểu dẫn đến khó bảo trì thì giá trị của nó cũng bị hạn chế. TỔNG KẾT Các kỹ thuật lập trình được cập nhật liên tục và rất phong phú. Ở đây chỉ trình bày các nguyên lý của lập trình. Các khái niệm như phong cách lập trình, đặc trưng ngôn ngữ cũng đã được đưa ra. Vấn đề được đặt ra ở đây là: Chúng ta cần vận dụng vào việc dạy và học các môn học lập trình ở trường phổ thông như thế nào? Đồng thời một vấn đề lớn là làm thế nào để tạo nên một phong cách lập trình cho kỹ sư lập trình? Câu hỏi và bài tập 1. Kỹ thuật lập trình tốt thể hiện ở chỗ nào? Hệ quả của nó? 2. Nêu các kỹ thuật lập trình đã có? đặc trưng của mỗi loại là gì? 3. Tiêu chuẩn lựa chọn ngôn ngữ lập trình? 4. Thế nào là ngôn ngữ khả chuyển? Cho ví dụ? 5. Nêu các miền ứng ứng dụng và ngôn ngữ thích hợp với nó? 6. Các yếu tố tạo ra phong cách lập trình là gì? Nó hướng tới phần mềm có đặc trưng gì? 7. Giải thích cách làm: chú thích?, đặt tên?, viết câu lệnh?, đặt hàm và biến cục bộ?, xử lý lỗi và thông báo? Xử lý lỗi trong thư viện? Xử lý ngoại lệ? 8. Tiêu chuẩn cho phong cách lập trình tốt? 160
- Chƣơng 6. KIỂM THỬ Mục đích Khái niệm kiểm thử phần mềm. Qui trình kiểm thử. Mức độ, giai đoạn và các hoạt động kiểm thử phần mềm. Các phương pháp và kỹ thuật kiểm thử. Các chiến thuật kiểm thử tích hợp. Yêu cầu : Biết được các khái niệm liên quan đến kiểm thử (testing) Biết được mức độ, giai đoạn và các hoạt động kiểm thử phần mềm. Vận dụng được các phương pháp và kỹ thuật kiểm thử Hiểu các chiến thuật kiểm thử tích hợp. 1. Đại cƣơng về kiểm thử phần mềm Mặc dù được tự động hoá một phần bởi các công cụ CASE, rất nhiều công đoạn trong quá trình sản xuất phần mềm vẫn được thực hiện bởi con người. Lỗi có thể xảy ra trong tất cả các giai đoạn: phân tích yêu cầu, thiết kế, mã hoá. Do đó phải kiểm thử chương trình trước khi chính thức sử dụng. Kiểm thử phần mềm là hoạt động thực thi chương trình với mục đích tìm ra lỗi nhằm phê phán, không mang tính xây dựng. 1.1. Xác minh (verification) và thẩm định (validation) Xác minh (Verification): là quá trình xem xét phần mềm được thực hiện có đúng đặc tả không, có đúng thiết kế không và phát hiện lỗi lập trình, tức đi trả lời câu hỏi: “Chúng ta đang xây dựng sản phẩm theo đúng cách?/Are we building the product right?” Thẩm định (Validation): là dãy các hành động để đảm bảo cho phần mềm được xây dựng theo nhu cầu người dùng, hoạt động hiệu quả và nhằm phát hiện 161
- lỗi phân tích, lỗi thiết kế (lỗi mức cao), tức nhằm trả lời câu hỏi: “Chúng ta đang xây dựng sản phẩm đúng?/Are we building the right product?”. Nói cách khác ta cần trả lời câu hỏi: “Phần mềm phải thực hiện những gì người dùng thật sự cần?” Trong quá trình xác minh và thẩm định có thể chia thành 2 loại tĩnh và động. Thẩm định/xác minh tĩnh (kiểm tra - software inspection), lúc này chúng ta không thực hiện chương trình mà chỉ xét duyệt yêu cầu, thiết kế, mã nguồn. Quá trình này được tiến hành ở mọi công đoạn phát triển. Tuy nhiên phương pháp này khó đánh giá tính hiệu quả của sản phẩm. Thẩm định/xác minh động (kiểm thử– software testing), lúc này chúng ta cần thực hiện chương trình. Việc kiểm thử cần có mã nguồn, nhằm phát hiện lỗi lập trình và đánh giá tính hiệu quả phần mềm. Đây là cách duy nhất để kiểm tra các yêu cầu phi chức năng. 1.2. Mục tiêu của kiểm thử Mục tiêu của kiểm thử là nhằm xác định được mức độ phần mềm đáp ứng được các yêu cầu (độ tin cậy) và tìm ra lỗi (nếu có) với chi phí thấp nhất. Kiểm thử phần mềm giúp phát hiện được lỗi trong chương trình (nếu có). Chứng minh được phần mềm hoạt động đúng như đã thiết kế. Chứng minh được phần mềm đáp ứng yêu cầu của người sử dụng. Nhằm góp phần chứng minh chất lượng của phần mềm. Quá trình kiểm thử phần mềm là tốt khi: + Có khả năng tìm ra lỗi cao. + Không dư thừa. + Biết chọn lọc: chỉ kiểm thử những phần nào có khả năng tìm ra lỗi đặc trưng. + Không quá phức tạp cũng không quá đơn giản. Chú ý: Kiểm thử phần mềm không khẳng định được phần mềm không còn khiếm khuyết, chỉ khẳng định được phần mềm có lỗi. Quá trình này được tiến hành ở mọi công đoạn của quá trình làm phần mềm. + Phân tích 162
- - Xét duyệt đặc tả yêu cầu + Thiết kế - Xét duyệt đặc tả thiết kế + Mã hóa - Kiểm thử chương trình 1.3. Yêu cầu đối với kiểm thử + Tính lặp lại - Kiểm thử phải lặp lại được (kiểm tra xem lỗi đã được sửa hay chưa) - Dữ liệu/trạng thái phải mô tả được + Tính hệ thống - Đảm bảo kiểm tra hết các trường hợp + Được lập tài liệu - Kiểm soát tiến trình/kết quả 2. Các mức độ kiểm thử Việc kiểm thử nên hướng về yêu cầu của khách hàng. Nên được hoạch định trước một thời gian dài. Khi phân tích lỗi cần áp dụng nguyên lý Pareto: 80% lỗi có nguyên nhân từ 20% các module khác, vì vậy cần cô lập và kiểm tra những module khả nghi nhất. Nên tiến hành từ nhỏ đến lớn: bắt đầu từ những module riêng biệt rồi sau đó tích hợp cácmodule lại. Không thể kiểm thử triệt để một phần mềm. Việc kiểm thử nên được thực hiện bởi những đối tượng KHÔNG tham gia vào quá trình phát triển phần mềm. 163
- Hình 6.1. Các mức độ kiểm thử Mô hình sau cho thấy mối quan hệ giữa các mức độ kiểm thử và các giai đoạn của quá trình phát triển phần mềm. Hình 6.2. Mô hình chữ V 2.1. Các giai đoạn kiểm thử Trong môi trường của phía phát triển Kiểm thử đơn vị Kiểm thử tích hợp 164
- Kiểm thử hệ thống phần mềm Kiểm thử chấp nhận Trong môi trường của phía khách hàng Kiểm thử alpha Kiểm thử beta Kiểm thử hệ thống thông tin CÁC GIAI ĐOẠN KIỂM THỬ - Phía phát triển Các giai đoạn Công việc Thành phần Kiểm thử đơn vị Dựa vào tài liệu thiết kế chi tiết Đội dự án test (Tester) (Unit test/Module Test 1 module (hàm, thủ tục, test) đoạn code, component ) Test tích hợp Dựa vào tài liệu thiết kế tổng Đội dự án test (Integration test) thể Kiểm tra sự tương tác giữa các module Test hệ thống Dựa vào tài liệu đặc tả yêu cầu Đội dự án test (tester) phần mềm phần mềm (System test) Kiểm tra toàn bộ hệ thống Test chấp nhận Dựa vào yêu cầu nghiệp vụ của Khách hàng (Acceptance test) khách hàng CÁC GIAI ĐOẠN KIỂM THỬ - Phía khách hàng Kiểm thử chấp nhận - thẩm định Với mục tiêu là xem xét phần mềm có đáp ứng được yêu cầu khách hàng/người dùng không? Quá trình này được thực hiện thông qua một loạt các kiểm thử hộp đen. Các kế hoạch và thủ tục kiểm thử được thiết kế nhằm bảo đảm rằng: •+ Tất cả các yêu cầu được thoả mãn, •+ Các yêu cầu thi hành đã chính xác, 165
- •+ Tài liệu đúng đắn và •+ Các yêu cầu khác là thoả đáng. Có hai loại: kiểm thử Alpha và kiểm thử Beta Kiểm thử alpha: là việc kiểm thử chấp nhận được tiến hành ở môi trường khách hàng. Quá trình này được tiến hành ngay tại nơi sản xuất phần mềm. Nhà phát triển phần mềm sẽ quan sát người sử dụng sản phẩm và ghi nhận lại những lỗi phát sinh để sửa chữa. Kiểm thử beta: là sự mở rộng của kiểm thử Alpha, thường được tiến hành với một lượng lớn người sử dụng. Phần mềm được kiểm tra bên ngoài phạm vi của đơn vị sản xuất. Người sử dụng tiến hành kiểm thử không có sự hướng dẫn của người phát triển và thông báo lại kết quả cho người phát triển. 2.2. Kiểm thử hệ thống Kiểm thử hệ thống là một sự mở rộng phạm vi kiểm thử, nhìn nhận phần mềm như một yếu tố trong một hệ thống phức tạp. Hệ thống dựa trên máy tính (phần cứng và phần mềm) do nhiều bên xây dựng, người phát triển phần mềm chỉ là một, vì vậy chúng cần được kiểm tra tổng thể. Kiểm thử hệ thống hướng đến, kiểm thử các yếu tố sau: • Khả năng phục hồi sau khi có lỗi • Độ an toàn • Hiệu năng và giới hạn phần mềm Các loại kiểm thử hệ thống 1. Kiểm thử chức năng (mức hệ thống) Bao gồm các chức năng giao diện, các chức năng mức người dùng hay đầu ra cuối cùng khỏi hệ thống. 2. Kiểm thử phục hồi (chịu lỗi) Kiểm thử phục hồi là buộc phần mềm phải thất bại để xem khả năng phục hồi của nó đến đâu. Có 2 mức phục hồi: phục hồi tự động hay cần đến sự can 166
- thiệp của con người. Để đánh giá khả năng phục hồi, người ta sử dụng đến khái niệm độ đo độ tin cậy. 3. Kiểm thử an ninh (sức chịu được tấn công) Nhằm kiểm tra mọi cơ chế bảo vệ có thể xây dựng cho hệ thống, để xem xét liệu có đạt hiệu quả đề ra trước các đột nhập hay không? Ở đây người kiểm thử đóng vai trò của kẻ đột nhập thực hiện mọi đột nhập có thể được, để đánh giá. 4. Kiểm thử thi hành (thông suốt, kịp thời) Kiểm thử thi hành được thiết kế để kiểm tra sự vận hành của phần mềm khi hệ thống được tích hợp. Việc thi hành đúng bao gồm cả số lượng, chất lượng (hoạt động và hiệu năng) 5. Kiểm thử chịu tải (qui mô, giá trị nhạy cảm) là việc vận hành hệ thống khi sử dụng nguồn lực với số lượng, tần suất và cường độ cực cao. Ví dụ: vận hành một cơ sở dữ liệu với số bản ghi cực lớn, vận hành hệ điều hành mạng với số máy tăng nhanh nhiều lần. 3. Các hoạt động kiểm thử Bắt đầu Lập kế hoạch Kế hoạch test Test Test case Test Thiết kế Test procedure Test scrip Cài đặt và Test data chuẩn bị Test Môi trƣờng testt Test tích hợp Bcáo KQ test Lỗi Xem xét và Đánh Đề xuất Biên bản giá kết quả test test Test hệ thống Tổng hợp, báo cáo Bcáo tổng hợp test Hồ sơ Kết thúc 167
- Kế hoạch kiểm thử tổng thể, có thể gồm các mục tiêu biểu sau: 1. Giới thiệu chung - Mô tả hệ thống cần kiểm thử - Các mục tiêu kiểm thử - Phương pháp sử dụng - Tài liệu hỗ trợ 2. Kế hoạch - Thời gian, địa điểm - Tài liệu kiểm thử: các ca kiểm thử, tiến trình, lịch trình - Điều kiện kiểm thử 3. Các yêu cầu: phần cứng, phần mềm, nhân sự 4. Kiểm soát quá trình kiểm thử 3.1. Các phƣơng pháp kiểm thử Kiểm thử chức năng (functional testing), phương pháp này dựa trên đặc tả chức năng, nhằm phát hiện các sai sót về chức năng mà không quan tâm nhiều đến cách cài đặt (phương pháp kiểm thử hộp đen). Kiểm thử cấu trúc (structured testing) là phương pháp kiểm thử có nghiên cứu đến mã nguồn nhằm phân tích thứ tự thực hiện các lệnh thực thi trong phần mềm (phương pháp kiểm thử hộp trắng). Kiểm thử hộp đen (black-box) là phương pháp kiểm tra các chức năng cụ thể của phần mềm, không quan tâm cấu trúc bên trong, phương pháp này thường được áp dụng cho những module lớn. Đối tượng kiểm thử xem như là một hộp đen, thông qua giao diện để đưa dữ liệu vào và nhận thông tin ra. Đối tượng quan tâm là các module, hệ con hay toàn hệ thống. Cơ sở của việc kiểm thử này là các đặc tả, điều kiện vào/ra và các cấu trúc dữ liệu lưu trữ Các kỹ thuật kiểm thử hộp đen: Phân hoạch tương đương; Phân tích giá trị biên; Đồ thị nhân quả. Kiểm thử hộp trắng (white-box) là phương pháp kiểm tra cấu trúc điều khiển bên trong chương trình, thường dùng cho những những module nhỏ. Đối 168
- tượng của nó là mã nguồn. Mức kiểm thử là các module đơn vị. Nội dung kiểm thử là xem xét: các chi tiết thủ tục (thuật toán), các con đường logic (luồng điều khiển) và các trạng thái của chương trình (dữ liệu). Mỗi loại kiểm thử có khả năng tìm ra những nhóm lỗi khác nhau do đó nên kết hợp cả hai (phương pháp kiểm thử hộp xám / gray-box) 3.2. Các công việc của một nhân viên kiểm thử (tester). Tham gia phân tích yêu cầu của khách hàng Lập kế hoạch test Xây dựng tiêu chuẩn nghiệm thu Xây dựng hướng dẫn test (bản thiết kế test, kịch bản test) Thực hiện test Hỗ trợ các vấn đề liên quan đến test Báo cáo và tổng hợp kết quả test Lập và lưu trữ các hồ sơ liên quan đến test Thu thập và kiểm soát các dữ liệu liên quan đến các hoạt động test Tính toán và phân tích các chỉ tiêu liên quan đến các hoạt động test 4. Chiến thuật kiểm thử phần mềm Chiến thuật kiểm thử phần mềm là một sự tích hợp các phương pháp tạo ra test-case tạo nên một chuỗi các bước có thứ tự nhằm có thể kiểm thử phần mềm thành công. Quá trình kiểm thử bao gồm các công việc: + Lập kế hoạch kiểm thử. + Sinh test-case. + Thực hiện kiểm thử, thu thập kết quả và đánh giá. + Tích hợp các module. 4.1. Một chiến thuật kiểm thử phổ biến – mô hình chữ V. 169
- Mô hình trên nhằm giải thích sự tương quan giữa các công đoạn xây dựng phần mềm và các loại kiểm thử. Ở mỗi công đoạn xây dựng phần mềm sẽ tương ứng với một loại kiểm thử và cần có một hồ sơ kiểm thử tương ứng được thành lập để phục vụ cho việc kiểm thử. Ví dụ: Công đoạn Loại kiểm thử Hồ sơ Yêu cầu phần mềm Kiểm thử chấp nhận Hồ sơ kiểm thử chấp nhận Mô tả chi tiết phần Kiểm thử hệ thống Hồ sơ kiểm thử hệ thống mềm Hồ sơ kiến trúc Kiểm thử tích hợp Hồ sơ kiểm thử tích hợp Thiết kế chi tiết Kiểm thử khối hồ sơ kiểm thử khối Viết mã Kiểm thử đơn vị Hồ sơ kiểm thử đơn vị Với chiến thuật này, dự án kiểm thử được bắt đầu tại từng module rồi tích hợp lớn dần đến toàn bộ hệ thống. Các kỹ thuật khác nhau sẽ được sử dụng thích hợp tại các giai đoạn khác nhau. Kiểm thử có thể được tiến hành bởi người phát triển phần mềm, nhưng đối với các dự án lớn thì việc kiểm thử phải được tiến hành bởi một nhóm độc lập. Kiểm thử và sửa lỗi là các hoạt động độc lập nhưng việc sửa lỗi phải phù hợp với các chiến thuật kiểm thử. 4.2. Kiểm thử từng module Chúng ta sẽ tiến hành kiểm thử trên từng đơn vị nhỏ nhất của phần mềm, đó là module mã nguồn, sau khi đã thiết kế, mã hoá và biên dịch thành công. Với giai đoạn này thường dùng kỹ thuật kiểm thử white-box. Có thể tiến hành kiểm thử cùng lúc nhiều module. Mỗi module mã nguồn không phải là một chương trình hoàn chỉnh và đôi khi phải gọi các module chưa được kiểm thử khác, do đó có thể 170
- phải thiết lập driver và/hoặc stub, do đó phí tổn cho việc kiểm thử module là khá lớn. Ở đây driver được hiểu là một chương trình chính có nhiệm vụ nhận dữ liệu kiểm thử, chuyển dữ liệu đó xuống cho module để kiểm tra và in ra các kết quả kiểm tra tương ứng. Còn các module tạm thời (Stub) sẽ thay thế các module được gọi bởi module đang kiểm tra. 4.3. Kiểm thử tích hợp Giả sử từng module mã nguồn đã hoạt động đúng. Nhưng liệu khi kết hợp chúng lại thành một nhóm lớn chúng có hoạt động đúng không? Do đó phải tiến hành kiểm thử tích hợp để phát hiện lỗi liên quan đến giao tiếp giữa các module. Khi kiểm thử tích hợp, nên tránh tích hợp kiểu big-bang, đó là tất cả các module được kết hợp lại, và toàn bộ chương trình sẽ được kiểm thử một lúc mà nên tích hợp tăng dần: từ trên xuống hoặc từ dưới lên. Tích hợp từ trên xuống Module chính được dùng như là driver, và stub được thay thế bởi các module con trực tiếp của của module chính này. Tuỳ thuộc vào cách tích hợp theo chiều sâu (depth-first) hoặc chiều ngang (breath-first), mỗi stub con được thay thế một lần bởi module tương ứng đã kiểm thử. Cần tiến hành kiểm thử khi có sự thay thế mới và tiến hành kiểm thử hồi quy để phát hiện các lỗi khác trong từng module. Tích hợp từ dƣới lên 171
- Các module mức thấp nhất được kết hợp thành các nhóm thể hiện một chức năng con đặc biệt của phần mềm. Một driver được tạo ra để thao tác các test-case. Mỗi nhóm module được kiểm thử. Driver được bỏ đi và các nhóm module được kết hợp dần lên phía trên trong sơ đồ phân cấp của chương trình. 4.4. Kiểm thử hồi quy Việc kết hợp các module lại với nhau có thể ảnh hưởng đến vòng lặp điều khiển, cấu trúc dữ liệu hay dữ liệu input/output chia sẻ trong một số module. Điều đó làm lộ ra một số lỗi không thể phát hiện được khi tiến hành kiểm thử theo đơn vị. Do đó cần tiến hành kiểm thử lặp lại các module này, ta gọi là kiểm thử hồi quy. Kiểm thử hồi quy có thể được tiến hành thủ công bằng cách thực hiện lại các test-case đã tạo ra. Hoặc có thể dùng một công cụ capture-playback để thực hiện tự động. 4.5. Kiểm thử tính năng Kiểm thử tính năng hiểu theo cách đơn giản nhất là: kiểm thử các chức năng của phần mềm đáp ứng được nhu cầu của khách hàng vốn đã được xác định trong văn bản đặc tả yêu cầu của phần mềm hay không. Người ta thường áp dụng kỹ thuật kiểm thử hộp đen cho bước kiểm thử này. Kiểm thử tính năng bao gồm: + Xem xét lại cấu hình phần mềm. + Kiểm thử alpha. + Kiểm thử beta. 5. Kỹ thuật kiểm thử 5.1. Thiết lập các trƣờng hợp kiểm thử (ca kiểm thử/test-case). 172
- Mục tiêu thiết kế ca kiểm thử nhằm tìm ra nhiều sai sót có thể với nỗlực và thời gian nhỏ nhất. Các phương pháp kiểm thử tốt phải cho một cơ chế nhằm bảo đảm tính đầy đủ (không sót phần nào) và cung cấp khả năng thật sự phát hiện được các lỗi của phần mềm. Ca kiểm thử hiệu quả là ca kiểm thử phát hiện ra ít nhất một lỗi. Một test-case bao gồm: + Tên module/chức năng muốn kiểm thử + Dữ liệu vào - Dữ liệu thông thường: số, xâu kí tự, file, - Môi trường thử nghiệm: phần cứng, hệ điều hành để thực thi hệ thống, - Thứ tự thao tác (khi kiểm thử giao diện) + Thao tác kiểm thử. + Kết quả mong muốn - Thông thường: số, xâu kí tự, file, - Màn hình, thời gian phản hồi + Kết quả thực tế Test-case cho kiểm thử black-box: chủ yếu dựa vào các yêu cầu cụ thể của chức năng phần mềm. Test-case cho kiểm thử white-box: chủ yếu dựa vào cấu trúc điều khiển của phần mềm do đó vấn đề đặt ra: số lượng test-case cần thiết là quá lớn. 5.2. Phân hoạch tƣơng đƣơng (Equivalence partitioning) Khi sử dụng phương pháp kiểm thử hộp đen, chúng ta không thể kiểm thử được cho mọi trường hợp, vì vậy cần chia dữ liệu thành các miền có cùng hành vi nhằm tạo test case cho từng miền và tạo test case cho biên của các miền (do nhiều lỗi xuất hiện với giá trị biên). Vấn đề này còn có mối liên quan mật thiết với các nguyên lý thống kê trong Toán học. Ví dụ: Với hàm tính trị tuyệt đối, chúng ta có thể phân hoạch các miền tương đương để kiểm thử là: 173
- - Miền dữ liệu là các số bằng 0 - Miền dữ liệu là các số bé hơn 0 - Miền dữ liệu là các số lớn hơn 0 Ngoài ra nên tạo test case cho các trường hợp đặc biệt: biên của số trong máy tính chẳng hạn: (32767, -32768); số không (0); số âm, số thập phân; dữ liệu sai kiểu; dữ liệu ngẫu nhiên đó là các trường hợp biên. 5.3. Đƣờng đi trong module Khi sử dụng phương pháp kiểm thử hộp xám (phương pháp hộp trắng kết hợp với phương pháp hộp đen), ta cần xem xét để thực hiện các test-case cho các trường hợp của chương trình. Đường đi là thứ tự thực hiện các lệnh từ điểm bắt đầu đến điểm kết thúc của module. Chúng ta cần thiết kế các test case để kiểm thử mọi đường đi này. Cách xác định đường đi như sau: + Đánh số các khối lệnh - Đánh số các khối lệnh, câu lệnh điều kiện - Đánh số các hợp điểm của luồng lệnh + Rút gọn - Các khối tuần tự được tích hợp thành một khối. - Tích hợp khối tuần tự vào câu lệnh điều kiện kế tiếp. Ví dụ: Với lưu đồ thuật giải sau: 174
- Chúng ta có các đường đi là: Các đường đi là: 1; 2; 3; 8; 1; 9. 1; 2; 4; 6; 7; 8; 1; 9 5.3.1. Đƣờng đi độc lập Do không thể chọn mọi đường đi để kiểm thử, vì vậy cần chọn các đường đi độc lập. Ở đây đường đi độc lập được hiểu là đường đi có ít nhất một cặp khối lệnh (một cạnh của đồ thị) và chưa xuất hiện trong các đường đi đã có. Khi đó tập các đường đi độc lập là một tập hợp thỏa mãn: - Mọi khối lệnh đều được thực hiện ít nhất một lần - Mọi điều kiện đều được kiểm thử với hai trường hợp true và false. Ví dụ với hình vẽ trên thì có các đường đi độc lập sau: 1-9 1-2-3-8-1-9 1-2-4-6-7-8-1-9 1-2-4-5-7-8-1-9 Với một thuật giải có thể tồn tại nhiều bộ đường đi độc lập. Số đường đi tối thiểu cần kiểm tra được đo dựa vào độ phức tạp thuật toán. Do đó độ phức tạp của thuật giải càng lớn thì xác suất xuất hiện lỗi cao. 5.3.2. Kiểm thử các đƣờng độc lập cơ bản 175
- Kiểm thử white-box dựa vào cấu trúc điều khiển của thiết kế thủ tục để sinh các test-case với tiêu chí. + Tất cả các đường thực thi độc lập được thử qua ít nhất một lần. + Thử các điều kiện rẽ nhánh ở cả 2 nhánh true và false. + Thử qua vòng lặp tại biên cũng như bên trong + Thử qua cấu trúc dữ liệu để đảm bảo tính toàn vẹn của nó Kiểm thử các đường độc lập cơ bản là một trong những phương cách kiểm thử white-box. 5.3.3. Xây dựng đồ thị dòng chảy Mỗi node của đồ thị hình tròn biểu diễn một hoặc một vài tác vụ. Cạnh có hướng miêu tả đường thực thi. Đồ thị dòng chảy được xây dựng từ lưu đồ thuật giải. Sau đây là một số hình ảnh minh họa. 176
- Khi phân rã tất cả các điều kiện phức trở thành các điều kiện đơn. Mỗi node mô tả một điều kiện đơn được gọi là predicate. 5.3.4. Liệt kê các đƣờng độc lập cơ bản Từ node bắt đầu đến node kết thúc, các đường thực thi cơ bản được liệt kê theo một thứ tự nào đó để đảm bảo rằng: đường đang liệt kê ít nhất đi qua một cạnh chưa được duyệt qua bởi các đường đã liệt kê trước đó. Tổng số đường thực thi cơ bản độc lập nhau được tính bằng: V = P + 1; trong đó P là số node phân nhánh (predicate). Ví dụ: Đối với chương trình con có đồ thị dòng chảy ở hình trên thì: Tổng số đường : V = 3 + 1 = 4 Đường 1: 1-9 Đường 2: 1-2-3-8-1 Đường 3: 1-2-4-5-7-8-1 Đường 4: 1-2-4-6-7-8-1 Chú ý: dấu 3 chấm ( ) mang ý nghĩa “không quan tâm”, từ đó có thể đi theo bất kỳ cạnh nào bởi vì các cạnh sau đó đã được duyệt qua rồi. 5.3.5. Thiết lập các test-case 177
- Thiết lập một test-case cho mỗi đường thực thi cơ bản. Dựa vào thuật giải để tìm ra một dữ liệu input, sau đó tính ra dữ liệu output hay các đáp ứng mong đợi của thuật giải. Chú ý: có thể không tạo ra được test-case cho một đường thực thi nào đó. Ví dụ: Hãy thiết lập các test-case cho bài toán xét ba đoạn thẳng có phải là ba cạnh của một tam giác. 5.4. Phƣơng pháp kiểm thử gây áp lực Đối với một số hệ thống quan trọng, người ta còn tiến hành thử nghiệm gây áp lực (stress testing). Đây là loại (bước) thử nghiệm được tiến hành khi đã có phiên bản làm việc, nhằm tìm hiểu hoạt động của hệ thống trong các trường hợp tải trọng lớn (dữ liệu lớn, số người sử dụng lớn, tài nguyên hạn chế ). Mục đích của thử nghiệm áp lực là: - Tìm hiểu giới hạn chịu tải của hệ thống - Tìm hiểu về đặc trưng của hệ thống khi đạt và vượt giới hạn chịu tải (khi bị sụp đổ) Ngoài ra thử nghiệm áp lực còn nhằm xác định các trạng thái đặc biệt như tổ hợp một số điều kiện dẫn đến sự sụp đổ của hệ thống; tính an toàn của dữ liệu, của dịch vụ khi hệ thống sụp đổ. Mục tiêu của kiểm thử phần mềm là tìm ra lỗi. Có hai loại kiểm thử: white- boxvà black-box. Kiểm thử các đường độc lập cơ bản dùng trong kiểm thử white- box, bao gồm các bước: + Thiết lập đồ thị dòng chảy + Liệt kê các đường thực thi độc lập cơ bản + Sinh các test-case cho các đường thực thi đó. 6. Kiểm thử hƣớng đối tƣợng Về cơ bản chiến thuật kiểm thử hướng đối tượng cũng theo thứ tự giống như kiểm thử cổ điển: kiểm thử đơn vị - kiểm thử tích hợp - kiểm thử chức năng -kiểm thử toàn bộ hệ thống. Với việc kiểm thử đơn vị, chúng ta không thể tách rời từng tác vụ của đối tượng/lớp để kiểm thử. Tác vụ được đóng bao trong lớp. Các lớp con có thể 178
- override một tác vụ nào đó. Kiểm thử đơn vị hướng đối tượng tập trung vào các lớp nhằm kiểm thử hành vi của lớp. Với việc kiểm thử tích hợp, do khái niệm sơ đồ phân cấp không còn nhiều ý nghĩa trong chương trình hướng đối tượng, vì vậy cần kiểm thử tích hợp theo cách khác. Hai hình thức kiểm thử tích hợp hướng đối tượng: + Kiểm thử trên cơ sở thread: tích hợp các lớp tạo thành một thread để phục vụ cho một input nào đó của chương trình. + Kiểm thử trên cơ sở sử dụng: các lớp client sẽ được tích hợp để sử dụng dịch vụ nào đó cung cấp bởi các lớp server. 6.1. Kiểm thử theo kịch bản Dựa vào các use-case để soạn ra các kịch bản. Ví dụ: một kịch bản cho hệ thống đăng ký môn học qua WEB 1. Login với username= “e59306547”, password= “6547” 2. Chọn chức năng đăng ký môn học 3. Chọn 5 nhóm môn học của 5 môn: CNPM, AI, XLTHS, PTTK, XLSS trong đó có 2 nhóm trùng thời khoá biểu 4. Nhấn nút Submit Chương trình phải báo lỗi và liệt kê 2 nhóm bị trùng thời khoá biểu. 6.2. Nghệ thuật gỡ rối Gỡ rối là một quá trình nhằm loại bỏ các lỗi được phát hiện trong quá trình kiểm tra. Gỡ rối được thực hiện như là một kết quả của việc kiểm tra: lỗi phát hiện được cần tìm kiếm nguyên nhân từ đó sửa lỗi. Có 3 hình thức gỡ rối: brute force, loại trừ nguyên nhân và theo vết. Nên dùng kết hợp cả 3 hình thức này. Gỡ rối là công việc khó khăn và dễ gây tâm lý chán nản bởi nguyên nhân gây ra lỗi nhiều khi lại mơ hồ: do time-out, do độ chính xác, do chủ quan lập trình Khả năng gỡ rối gần như là bẩm sinh của mỗi người. 6.3. Brute force 179
- Brute force là phương pháp phổ biến nhất nhưng lại ít hiệu quả nhất cho việc phát hiện nguyên nhân gây lỗi phần mềm. Triết lý của phương pháp này là: “Hãy để máy tính tìm ra lỗi”. Có 3 cách thực hiện: + Lấy dữ liệu trong bộ nhớ để xem xét. + Dùng run-time trace để tìm lỗi. + Dùng lệnh WRITE để xuất dữ liệu cần kiểm tra ra màn hình. Chúng ta áp dụng phương pháp này khi tất cả các phương pháp khác đều thất bại. 6.4. Loại trừ nguyên nhân Phương pháp này dựa trên nguyên tắc phân chia nhị phân. Cách thực hiện: + Khi một lỗi được phát hiện, cố gắng đưa ra một danh sách các nguyên nhân có thể gây ra lỗi. + Danh sách này được nghiệm lại để loại bỏ dần các nguyên nhân không đúng cho đến khi tìm thấy một nguyên nhân khả nghi nhất. Khi đó dữ liệu kiểm thử sẽ được tinh chế lại để tiếp tục tìm lỗi. 6.5. Theo vết Theo vết là một phương pháp gỡ lỗi khá phổ biến có thể dùng thành công trong các chương trình nhỏ nhưng khó áp dụng đối với các chương trình rất lớn. Cách thực hiện: bắt đầu tại dòng mã nguồn có triệu chứng lỗi, thực hiện lần ngược trở lại từng dòng mã nguồn cho đến khi tìm thấy dòng gây ra lỗi. 7. Chứng minh toán học tính đúng đắn của chƣơng trình. Mục tiêu của chứng minh toán học là để có thể khẳng định tính đúng của chương trình thông qua chính văn bản của chương trình. 7.1. Khái niệm chung Như ta đã biết, chương trình P là một bộ biến đổi tuần tự P để chuyển cái vào x thành cái y; ở đây x và y hoàn toàn được xác định trước. 180
- Như vậy, một chương trình P được gọi là đúng nếu nó thực hiện chính xác những mục tiêu do người thiết kế đặc ra. Ta gọi: + Giả thiết {A} là mệnh đề được phát biểu để thể hiện tính chất của cái vào, gọi tắt là mệnh đề dữ liệu vào. + Kết luận {B} là mệnh đề được phát biểu để tính chất cần có của dữ liệu ra, gọi tắt là mệnh đề dữ liệu ra. Do P có tính tuần tự và hữu hạn nên có thể biểu diễn P là một dãy liên tiếp các cấu trúc điều khiển P1, P2, ,Pn. Do vậy, bằng cách nào đó mà ta khẳng định được: P1 biến đổi {A} thành {A1} P2 biến đổi {A1} thành {A2} Pn biến đổi {An-1} thành {An} Dựa vào quy tắc toán học, {An} có thể suy ra {B}, thì ta có thể nói rằng P là đúng với cái vào {A} và cái ra {B}. Lúc này ký hiệu {A}P{B}. Lưu ý: Trong trường hợp qua các phép suy diễn logic, từ {A} ta có {B} ({B} là hệ quả logic của {A}), ta viết {A} => {B}. Ví dụ 1: Cho mệnh đề dữ liệu vào {A: x,y R; 0 y+3} Lúc này ta có dãy biến đổi tính chất dữ liệu vào/ ra như sau: {A} P1{A1: x,y R; x>2} {A1}P1{A2: x,y R; x>2} 181
- {A2}P1{A3: x,y R; x>4} {A3}P1{A4: x,y R; x>y+4} Vậy ta có kết luận {A}P{B} hay nói cách khác là P đúng với dữ liệu vào {A} và dữ liệu ra {B}. Lưu ý: Xét ví dụ đã cho ở trên, ta có dãy biến đổi như sau: {A} P1{A'1: x,y R; x>0} {A'1}P1{A'2: x,y R; x>0} {A'2}P1{A'3: x,y R; x>2} {A'3}P1{A'4: x,y R; x>y+2} Mặc dù {A’4} không thể suy ra {B} nhưng ta vẫn có kết luận {A}P{B}.Trong trường hợp này, ta thấy các mệnh đề {A'1}{A'2}{A'3}{A'4} rõ ràng là các mệnh đề hệ quả của các mệnh đề {A1}{A2}{A3}{A4}. Ví dụ 2: Cho mệnh đề dữ liệu vào {A: x,y N; x=3y}, đoạn trình P =P1P2 như sau: x:=x+5; (P1) y:=y+5; (P2) và mệnh đề dữ liệu ra {B: x,y R; x=3y}. Ở đây, rõ ràng ta có {A} không thể suy ra {B} hay P là sai. 7.2. Hệ tiên đề Hoare Tiên đề 1: Tiên đề tuần tự (H1) Nếu mệnh đề {A} sau khi chịu tác động của khối cấu trúc điều khiển P ta được {B} và mệnh đề {B} sau khi chịu tác động của cấu trúc điều khiển Q ta được {C} thì {A} chịu tác động tuần tự P, Q sẽ thu được {C} Hay nói cách khác, đây chính là tiên đề về dãy thao tác: Nếu {A} P {B} và {B} Q {C} thì {A} P,Q {C} Tiên đề gán: tính chất của phép gán (H2) 182