CHƯƠNG 2. CẤU TRÚC DỮ LIỆU HƯỚNG ĐỐI TƯỢNG
2.1.2. Cấu trúc dữ liệu hướng đối tượng
Thuật ngữ kiểu dữ liệu đề cập đến các loại dữ liệu cơ bản mà các biến có thể chứa, như được chỉ định bởi ngôn ngữ lập trình đã chọn. Để phù hợp với quan điểm rằng các kiểu dữ liệu có thể bao gồm các kiểu không có cấu trúc và có cấu trúc, thì kiểu dữ liệu là các đối tượng dữ liệu. Định nghĩa cơ bản về một cấu trúc dữ liệu được trình bày lại dưới dạng tóm tắt đơn giản sau.
Cấu trúc dữ liệu là một tập hợp các thứ tự các đối tượng dữ liệu và một tập hợp các thao tác trên các đối tượng này
Ví dụ về mảng, chúng ta có thể thay thế về các mục dữ liệu, phần tử dữ liệu và đối tượng dữ liệu và tham chiếu đến cùng một thực thể. Đối với cấu trúc mảng, tổ chức tuần tự của các đối tượng dữ liệu là ngầm định vì dữ liệu được tự động gán vào các vị trí bộ nhớ liền kề. Các ứng dụng xử lý thông tin về cơ bản quan tâm đến việc tổ chức các nhóm dữ liệu có liên quan lẫn nhau. Thuật ngữ cơ sở dữ liệu được sử dụng để chỉ một tổ chức hoặc cấu trúc như vậy (cũng như phần cứng máy tính mà nó được xử lý và các chương
trình thao tác với nó). Các nhóm dữ liệu liên quan thường được gọi là bản ghi. Các thành phần hoặc mục dữ liệu riêng lẻ của một bản ghi là các thuộc tính của nó. Các hoạt động của cơ sở dữ liệu nguyên tử thường liên quan đến việc sắp xếp và tìm kiếm các bản ghi dựa trên các thuộc tính đã chọn của các bản ghi được gọi là khóa. Ví dụ, mỗi bản ghi của cơ sở dữ liệu nhân viên có thể chứa tên thuộc tính ngày thuê và chức danh. Nếu tập hợp các bản ghi này được tìm kiếm một bản ghi (hoặc số lượng bản ghi) khớp với một tên nhất định thì thuộc tính name được coi là khóa tìm kiếm hoặc mục tiêu của tìm kiếm.
Định nghĩa về cấu trúc dữ liệu trừu tượng
Khái niệm về kiểu dữ liệu trừu tượng (ADT, abstract data type) hay cấu trúc dữ liệu trừu tượng lần đầu tiên được Liskov và Ziles đưa ra như một phương tiện cung cấp cho việc bảo mật dữ liệu. ADT là một tập hợp dữ liệu và một tập hợp các hoạt động được phép (hành động) được sử dụng để xác định và thao tác dữ liệu.
Tập hợp các phần tử dữ liệu của một ADT được gọi là trừu tượng hóa dữ liệu trong khi tập hợp các hoạt động được phép được gọi là trừu tượng hóa chương trình. Khái niệm ADT kết hợp dữ liệu và chương trình trừu tượng với nhau. ADT được chỉ định bởi:
• Một loại lên duy nhất
• Một tập hợp các giá trị của một số loại
• Một tập hợp các thao tác hoạt động trên kiểu dữ liệu đó
Kiểu dữ liệu có thể đơn giản như các kiểu dựng sẵn như int, char và float hoặc các kiểu phức tạp hơn do người dùng định nghĩa. Khái niệm ADT là nền tảng cho các cấu trúc dữ liệu được giới thiệu và triển khai trong suốt văn bản này. Các tính năng chính của ADT là:
• Biểu diễn bên trong của một kiểu dữ liệu có thể được thay đổi mà không cần làm thay đổi các hoạt động được sử dụng bởi các chương trình bên ngoài hoặc các hàm để trừu tượng hóa dữ liệu không thay đổi.
• Một ADT được xem như một thực thể duy nhất
• Dữ liệu không cần thiết trong ADT được che giấu khỏi bất kỳ chương trình nào không được quyền truy cập vào dữ liệu đó.
Thiết kế hướng đối tượng
Thiết kế hướng đối tượng (ODD – object-oriented design) là một thiết kế dựa trên khái niệm trừu tượng ADT. Một cách đơn giản để hiểu các nguyên tắc của thiết kế hướng đối tượng là phân biệt nó với thiết kế hướng chức năng (FOD – function-oriented design) truyền thống hơn.
Thiết kế hướng đối tượng coi hệ thống phần mềm như một nhóm các đối tượng tương tác chứ không phải là một tập hợp các chức năng tương tác như trong thiết kế hướng chức năng. Chương trình hướng đối tượng (OOP) là một thiết kế hướng đối tượng. Nói cách khác, các phương pháp lập trình hướng đối tượng được sử dụng để triển khai các khái niệm ADT. Trong OOP, dữ liệu và hoạt động cho một ADT được kết hợp thành một thực thể duy nhất, được gọi là một đối tượng. Các ngôn ngữ lập trình hướng đối tượng như C ++, Smalltalk và Object Pascal hỗ trợ trực tiếp mô hình đối tượng và đơn giản hóa việc triển khai OOD. Trong C ++, cấu trúc lớp được sử dụng để triển khai một ADT và do đó xác định kiểu của một đối tượng.
Lập trình hướng đối tượng có cấu trúc và mô-đun nhiều hơn so với lập trình hướng chức năng, tạo ra các chương trình dễ bảo trì, linh hoạt và mạnh mẽ. OOP cho phép lập trình viên mô phỏng các tình huống trong thế giới thực một cách gần gũi hơn.
Lập trình hướng đối tượng có 4 tính chất:
• Encapsulation – tính đóng gói: các phương thức và dữ liệu có mối quan hệ với nhau được lưu vào một lớp để thuận tiện cho việc quản lý, sử dụng. Chỉ có phương thức nội tại của chính đối tượng mới có thể thay đổi trạng thái nội tại của nó.
• Abstraction – tính trừu tượng: chỉ tập trung vào những thuộc tính và phương thức cần thiết cho việc giải quyết vấn đề trong lập trình và bỏ qua các thông tin không quan trọng.
• Inheritance – tính kế thừa: các đối tượng “con” có thể thừa hưởng các đặc tính có sẵn từ đối tượng “cha” mà không cần định nghĩa lại (tùy theo ngôn ngữ lập trình).
• Polymorphism – tính đa hình: các đối tượng không cùng một lớp, khi tiếp nhận cùng một thông điệp thì sẽ phản hồi theo những cách khác nhau.
a. Cấu trúc dữ liệu hướng chức năng
Cấu trúc dữ liệu hướng chức năng được xây dựng dựa trên khái niệm tập hợp các biến dữ liệu được xác định và các hoạt động được phép trên chúng mà không liên quan đến việc ẩn và bảo vệ dữ liệu. Các phương pháp lập trình hướng chức năng dựa trên một lược đồ.
• Xác định các yêu cầu dữ liệu
• Xây dựng cấu trúc dữ liệu
• Viết một tập hợp các hàm để xử lý dữ liệu
Các ngôn ngữ lập trình truyền thống như C, ALGOL và Pascal nhấn mạnh vào biểu diễn dữ liệu và sử dụng các kiểu dữ liệu cơ bản. Chúng ta thấy cấu trúc dữ liệu như vậy là mảng (một đơn vị của các kiểu dữ liệu liền kề, khác nhau ngụ ý việc sử dụng không gian bộ nhớ tương tự) và bản ghi (một đơn vị của các không gian bộ nhớ liền kề, không
giống nhau) bắt nguồn từ các kiểu dữ liệu cơ bản. Với cách tiếp cận này, các kiểu dữ liệu do người dùng xác định với việc ẩn dữ liệu sẽ bị bỏ qua.
Cấu trúc dữ liệu truyền thống và phương pháp lập trình này có một thiếu sót nghiêm trọng trong việc bảo vệ dữ liệu khỏi bị lạm dụng có thể xảy ra. Sự thiếu hụt này sẽ được giải quyết trong một phương pháp lập trình và cấu trúc dữ liệu hướng đối tượng.
b. Cấu trúc dữ liệu hướng đối tượng
Vì ADT được xem như một kiểu cho một đối tượng và thiết kế hướng đối tượng (OOD) phân tách hầu hết các ứng dụng trong thế giới thực như một tập hợp các đối tượng hợp tác, nên ADT (và do đó là cấu trúc dữ liệu hướng đối tượng) là một tính năng tích hợp của OOD. Khi một OOD được thực hiện bởi một chương trình bằng một số ngôn ngữ hỗ trợ OOD, chẳng hạn như C ++, chương trình đó được gọi là chương trình hướng đối tượng (OOP). Một OOP sử dụng các lớp và đối tượng. Các tính năng cơ bản của OOD bao gồm:
• Nhập dữ liệu trừu tượng: ADT chỉ định dữ liệu và hoạt động của một đối tượng.
• Dữ liệu trừu tượng: Sự trừu tượng hóa dữ liệu cung cấp một khái niệm về một đối tượng và một giao diện cho dữ liệu của một đối tượng mà không liên quan đến các chi tiết của việc triển khai dữ liệu. Như đã nói bởi Booch, “Một sự trừu tượng biểu thị các đặc điểm cơ bản của một đối tượng giúp phân biệt nó với tất cả các loại đối tượng khác và do đó cung cấp các ranh giới khái niệm được xác định rõ ràng, liên quan đến quan điểm của người xem.”
• Đóng gói và ẩn dữ liệu: Tính năng đóng gói che giấu (ẩn) chi tiết triển khai của dữ liệu và phương thức của một đối tượng khỏi chương trình hoặc chức năng người dùng của nó. Điều này giúp bảo vệ thông tin của đối tượng (dữ liệu và phương pháp) khỏi những người dùng trái phép. Do đó, trừu tượng bổ sung cho tính đóng gói.
• Tính mô-đun: Modularity là một phương tiện phân tách một ứng dụng thành một tập hợp các đối tượng hợp tác. Như Booch nói: Các nguyên tắc trừu tượng, đóng gói và mô-đun là hiệp lực. Theo mô đun, các phần trừu tượng liên quan đến logic được nén chặt lại.
• Các lớp: Các lớp xác định kiểu hoặc mẫu của các đối tượng. Khái niệm kiểu có nguồn gốc từ ADT. Một lớp được bắt nguồn từ một ADT ngoài việc bảo vệ dữ liệu và hoạt động. Các lớp là các mẫu mô tả cấu trúc dữ liệu và các hành động hợp lệ tác động lên dữ liệu.
• Xác định Đối tượng và Lớp: Một đối tượng là một thể hiện của một lớp; nó có một tên biến duy nhất. Nó được xác định bởi hành vi và trạng thái của nó. Một lớp chung được sử dụng để xác định hành vi và cấu trúc của các đối tượng cùng kiểu.
• Phương pháp: Các hành động hợp lệ được thực hiện với các hàm thành viên của lớp định nghĩa một đối tượng. Các hàm thành viên như vậy được gọi là các
phương thức (hoặc các phép toán hoặc các hành động). Các phương thức được sử dụng để gửi thông điệp đến một đối tượng chỉ có thể hoạt động dựa trên thông báo.
• Hệ thống phân cấp và kế thừa: Một vấn đề được chia thành một tập hợp các đối tượng hoặc lớp. Một tập hợp các lớp thể hiện một hệ thống phân cấp kế thừa vì một lớp này dẫn xuất từ một lớp khác (kế thừa các thành viên) theo kiểu phân cấp. Lớp gốc của một hệ thống phân cấp kế thừa là lớp cơ sở. Lớp cơ sở thiết lập các thành viên dữ liệu chung và các hàm thành viên cho tất cả các lớp. Các lớp kế thừa các thành viên từ lớp cơ sở là lớp con hoặc lớp dẫn xuất của lớp cơ sở gốc. Các lớp dẫn xuất này mở rộng khả năng của lớp cơ sở để cung cấp chức năng cụ thể của riêng chúng và/hoặc triển khai chức năng của lớp cơ sở. Tính kế thừa cho phép chia sẻ dữ liệu và phương thức giữa các lớp với mức độ trừu tượng.
• Truyền thông điệp giữa các đối tượng: Các đối tượng độc lập với nhau. Một đối tượng giống nhau hoặc các lớp khác nhau có thể gửi một thông điệp đến bất kỳ đối tượng nào, và đối tượng nhận thông điệp chỉ có thể hoạt động dựa trên thông điệp đó. Một thông báo có thể chứa một yêu cầu hành động như tạo một đối tượng, khởi tạo một đối tượng, in các mục dữ liệu nội bộ trong một đối tượng.
• Tính đa hình và liên kết động: Tính đa hình có nghĩa là một tên phương thức duy nhất có thể được sử dụng cho các hoạt động khác nhau trên các lớp dẫn xuất (lớp con). Vì hoạt động cụ thể được gọi phụ thuộc vào lớp của đối tượng mà thông điệp đang được gửi đến, địa chỉ của hoạt động sẽ không được biết cho đến thời gian chạy. Địa chỉ được đính kèm động, một quá trình được gọi là liên kết động hoặc liên kết muộn.
Mục đích của một thiết kế hướng đối tượng tốt là cung cấp cho người dùng một tập hợp phong phú các thành phần có thể thay thế và sử dụng lại có thể mở rộng mà trên đó hệ thống phần mềm có thể được xây dựng. Trong một môi trường như vậy, các đối tượng có thể được xem như là các mô-đun có sẵn, OOP là một mạng lưới các đối tượng như vậy được cắm vào nhau và giao tiếp bằng tin nhắn đi qua các giao diện công cộng được chỉ định của chúng. Tính năng đóng gói ẩn cấu trúc dữ liệu và các thuật toán được liên kết với một đối tượng khỏi người dùng. Vì vậy, trong một thiết kế hướng đối tượng, người dùng quan tâm đến những gì một đối tượng làm hơn là cách nó thực hiện.
Một thiết kế hướng chức năng truyền thống trình bày một cách tiếp cận bổ sung cho thiết kế hướng đối tượng. Trong thiết kế hướng chức năng, mô-đun cơ bản là một chức năng chứ không phải một thực thể dữ liệu trừu tượng. Trong một môi trường như vậy, người dùng thường liên quan đến chức năng làm gì và nó hoạt động như thế nào. Tuy nhiên, trong bất kỳ thiết kế nào, dù là hướng chức năng hay hướng đối tượng, người triển khai hoặc người dùng thường quan tâm đến hiệu suất của hệ thống phần mềm.
Cấu trúc dữ liệu và các thuật toán liên quan của chúng được đặc trưng bởi tính hiệu quả của chúng trong các điều kiện ứng dụng khác nhau. Do đó, trong một thiết kế hướng
đối tượng, người ta có thể không chỉ quan tâm đến những gì một đối tượng làm mà còn là nó hoạt động tốt như thế nào.
Các bước khai thác thiết kế theo định hướng đối tượng
Một số bước chính trong việc tạo OOD cho một vấn đề nhất định là:
• Bước 1. Phân vùng: Chia vấn đề thành một tập hợp các đối tượng. Xác định từng đối tượng bằng dữ liệu của nó và các hoạt động liên quan (ADT).
• Bước 2. Chia sẻ: Xác định tính chung giữa các đối tượng và thứ bậc của các lớp.
• Bước 3. Đóng gói: Xác định mức độ bảo vệ cho dữ liệu và hoạt động trong mỗi đối tượng.
• Bước 4. Kế thừa: Xác định các mức bảo vệ cho dữ liệu và hoạt động trong (các) lớp cơ sở được kế thừa bởi các lớp con.
• Bước 5. Tính đa hình và liên kết động: Xác định phương thức nào trong (các) lớp cơ sở sẽ có cùng tên giao diện trên (các) lớp con nhưng có thể cần phải hoạt động khác nhau trong (các) lớp con khác nhau.
• Bước 6. Sàng lọc: Lặp lại năm bước trên theo cách lặp đi lặp lại cho đến khi đạt được thiết kế để triển khai ban đầu.