Bài giảng Lập trình hướng đối tượng - Các lớp trừu tượng
Tổng quan
Tạo thể hiện – Instantiation
Thiết kế thừa kế
Cài đặt các lớp trừu tượng
Các hàm thuần ảo
Nhận xét
Tóm tắt nội dung Bài giảng Lập trình hướng đối tượng - Các lớp trừu tượng, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
Các lớp trừu tượng Lập trình hướng đối tượng Lớp trừu tượng – Abstract class Tổng quan Tạo thể hiện – Instantiation Thiết kế thừa kế Cài đặt các lớp trừu tượng Các hàm thuần ảo Nhận xét Tổng quan Ta đã bàn chi tiết về đa hình – góc tam giác cuối cùng của mô hình hướng đối tượng Đến đây là chủ đề cuối cùng cần thiết trước khi ta có thể chính thức nói rằng ta có thể cài đặt mọi cây thừa kế mà ta muốn Sau chủ đề này là các chủ đề đa dạng phục vụ cho việc phát triển các chương trình hướng đối tượng bằng C++ Tạo thể hiện của lớp Khi mới giới thiệu khái niệm đối tượng, ta nói rằng chúng có thể được nhóm lại thành các lớp, trong đó mỗi lớp là một tập các đối tượng có cùng thuộc tính và hành vi. Ta cũng nói theo chiều ngược lại rằng ta có thể định nghĩa một đối tượng như là một thể hiện của một lớp Nghĩa là: lớp có thể tạo đối tượng Hầu hết các lớp ta đã gặp đều tạo được thể hiện ta có thể tạo thể hiện của Point hay Circle ta có thể tạo thể hiện của Cow hay MadCow Tạo thể hiện của lớp một đối tượng tài sản chính xác là cái gì? phương thức computeNetWorth() (tính giá trị) sẽ tính theo kiểu gì nếu không biết đó là ngân khoản, chứng khoán, hay ô tô? ta có thể nói rằng một đối tượng ngân khoản là một thể hiện của tài sản, nhưng thực ra không phải, nó là một thể hiện của lớp dẫn xuất của tài sản Tuy nhiên, với một số lớp, có thể không hợp lý khi nghĩ đến chuyện tạo thể hiện của các lớp đó Ví dụ, một hệ thống quản lý tài sản (asset): chứng khoán (stock), ngân khoản (bank account), bất động sản (real estate), ô tô (car), v.v.. Lớp trừu tượng – abstract class Lớp trừu tượng (Abstract Base Class – ABC) là một lớp không thể tạo thể hiện Thực tế, ta thường phân nhóm các đối tượng theo kiểu này chim và ếch đều là động vật, nhưng một con động vật là con gì? bia và rượu đều là đồ uống, nhưng một thứ đồ uống chính xác là cái gì? Có thể xác định xem một lớp có phải là lớp trừu tượng hay không khi ta không thể tìm được một thể hiện của lớp này mà lại không phải là thể hiện của một lớp con có con động vật nào không thuộc một nhóm nhỏ hơn không? có đồ uống nào không thuộc một loại cụ thể hơn không? Lớp trừu tượng Giả sử Asset là lớp trừu tượng, nó có các ích lợi gì? Mọi thuộc tính và hành vi của lớp cơ sở (Asset) có mặt trong mỗi thể hiện của các lớp dẫn xuất (BankAccount, Car, Stock,...) Ta vẫn có thể nói về quan hệ anh chị em giữa các thể hiện của các lớp dẫn xuất qua lớp cơ sở. nói về một cái ngân khoản cụ thể và một cái xe cụ thể nào đó như đang nói về tài sản ta có thể tạo một hàm tính tổng giá trị tài sản của một người, trong đó tính đa hình được thể hiện khi gọi hành vi “compute net worth” của các đối tượng tài sản Lớp trừu tượng Các lớp trừu tượng chỉ có ích khi chúng là các lớp cơ sở trong cây thừa kế Ví dụ, nếu ta cho BankAccount là lớp trừu tượng, và nó không có lớp dẫn xuất nào, vậy nó để làm gì? Các lớp cơ sở trừu tượng có thể được đặt tại các tầng khác nhau của một cây thừa kế. có thể coi BankAccount là lớp trừu tượng với các lớp con (chẳng hạn tài khoản tiết kiệm có kỳ hạn và tài khoản thường...) cây phả hệ động vật Yêu cầu duy nhất là mỗi lớp trừu tượng phải có ít nhất một lớp dẫn xuất không trừu tượng Lớp trừu tượng MAMMAL REPTILE PIGEON OWL EAGLE Thiết kế thừa kế Các cây thừa kế có thể rất phức tạp và có nhiều tầng Vấn đề là: phức tạp đến đâu thì vừa? Ta sẽ bàn về thiết kế các cây thừa kế nói chung, trong đó có một số điểm cụ thể về ứng dụng của các lớp trừu tượng Thiết kế thừa kế Đối với một thiết kế hệ thống bất kỳ, khi thiết kế các cây thừa kế, điều quan trọng là phải chú trọng vào mục đích của cây thừa kết (các lớp/đối tượng này sẽ dùng để làm gì?) Khi quyết định nên biểu diễn một nhóm đối tượng bằng một lớp hay một cây thừa kế gồm nhiều lớp, hãy nghĩ xem liệu có đối tượng nào chứa các thuộc tính và hành vi mà các đối tượng khác không có hay không. Hơn nữa, ta cần nghĩ xem chính xác những thể hiện nào sẽ được sinh ra trong hệ thống. Thiết kế thừa kế Ví dụ, xét nhóm đối tượng đồ uống – “beverage” Ta có thể nghĩ tới các thông tin sau trong việc tạo thể hiện: Loại đồ uống (cà phê, chè, rượu vang,...) Các ly/cốc đồ uống cụ thể (cốc chè của tôi, ly rượu của anh...) Bước đầu tiên: xác định xem hệ thống phải xử lý những gì Có thể hệ thống của ta quản lý các loại đồ uống bán tại một quán ăn. Do đó, ta sẽ tập trung vào dạng thông tin đầu tiên (loại đồ uống). Thiết kế thừa kế Tiếp theo, ta cần làm theo hướng dẫn chung về thiết kế hướng đối tượng - bắt đầu bằng dữ liệu Để bắt đầu, ta có thể muốn quản lý loại đồ uống và giá bán Nếu chỉ có vậy, ta sẽ chỉ cần đến đúng một lớp đồ uống: Đây rõ ràng là một lớp có thể tạo thể hiện, thí dụ: {coffee, $1.50} {tea, $1.25} {wine (glass), $5.00} {wine (bottle), $20.00} Tuy nhiên, nếu ta muốn lưu trữ nhiều thông tin hơn thì sao? Thiết kế thừa kế Có thể, ta còn muốn lưu loại vang (trắng, đỏ ...) Trong trường hợp đó, ta có hai lựa chọn: Thêm một thuộc tính “wine type” vào mọi đồ uống, để trống thuộc tính đó đối với các đồ uống không phải rượu vang Thêm một lớp con “wine” để chứa thuộc tính bổ sung đó Do đang tập sử dụng thiết kế hướng đối tượng tốt, ta sẽ chọn cách thứ hai. Thiết kế thừa kế Trong trường hợp này, việc tạo thể hiện từ cả hai lớp là hợp lý Lớp Beverage có thể có các thể hiện như {coffee, $1.50} và {tea, $1.25} Lớp Wine có thể có các thể hiện {Meridian (glass), $5.00, chardonay} và {Lindeman’s (bottle), $20.00, sauvignon} (giá trị thứ ba biểu thị loại vang) Vậy, trong trường hợp này, ta không muốn lớp cơ sở là lớp trừu tượng. Thiết kế thừa kế Bây giờ, thay cho quán ăn, ta làm việc với một cửa hàng : chuyên bán bia và rượu, không bán loại đồ uống nào khác. cần theo dõi nhãn hiệu và giá cần lưu loại vang (như ví dụ trước) cho rượu vang, và nước sản xuất đối với bia Mọi loại rượu bia đều được tạo thể hiện từ hai lớp Wine và Beer chỉ quản lý rượu và bia Vậy, không có lý do để tạo thể hiện từ Beverage ta coi Beverage là lớp trừu tượng. Thiết kế thừa kế Ví dụ cho thấy một lớp cơ sở nên được khai báo là lớp trừu tượng nếu và chỉ nếu ta không bao giờ có lý do gì để tạo thể hiện từ lớp đó. Tuy nhiên, trong thiết kế hệ thống, ta chỉ xét trong phạm vi mà hệ thống quan tâm đến có nhiều loại đồ uống không phải rượu hay bia (thí dụ chè, cà phê...), nhưng hệ thống của ta chỉ quan tâm đến rượu và bia do đó, lớp Beverage là trừu tượng trong phạm vi của thiết kế của ta. Do vậy, khi quyết định có nên khai báo một lớp là trừu tượng hay không, ta không nên chú trọng đến nhu cầu tạo thể hiện từ lớp đó trong mọi tình huống, mà nên chú trọng vào nhu cầu tạo thể hiện từ lớp đó trong phạm vi hệ thống cụ thể của ta. Thiết kế thừa kế Vậy, có hai bước để tạo một cây thừa kế: Quyết định xem ta có thực sự cần một cây thừa kế hay không Xác định các thuộc tính/hành vi thuộc về các lớp cơ sở (lớp cơ sở là bất kỳ lớp nào có lớp dẫn xuất, không chỉ là lớp tại đỉnh cây) Nói chung, nếu có một thuộc tính/hành vi mà lớp dẫn xuất nào cũng dùng thì nó nên được đặt tại lớp cơ sở Nói cách khác, nếu ta đang tạo một lớp cơ sở và ta muốn ép mọi lớp dẫn xuất của nó phải cài đặt hành vi nào, thì ta nên đặt hành vi đó vào lớp cơ sở đang tạo. Cài đặt lớp trừu tượng Để tạo các lớp trừu tượng, C++ đưa ra khái niệm hàm “thuần” ảo – pure virtual function Hàm thuần ảo (hay phương thức thuần ảo) là một phương thức được khai báo nhưng không được định nghĩa. Cú pháp hàm thuần ảo: đặt “ = 0” vào cuối khai báo phương thức virtual void MyMethod(...) = 0; không cần định nghĩa phương thức nếu không có “ = 0” và không định nghĩa, trình biên dịch sẽ báo lỗi Hàm thuần ảo Nếu một lớp có một phương thức không có định nghĩa, C++ sẽ không cho phép tạo thể hiện từ lớp đó, nhờ vậy, lớp đó trở thành lớp trừu tượng các lớp dẫn xuất của lớp đó nếu cũng không cung cấp định nghĩa cho phương thức đó thì cũng trở thành lớp trừu tượng nếu một lớp dẫn xuất muốn tạo thể hiện, nó phải cung cấp định nghĩa cho mọi hàm thuần ảo mà nó thừa kế Do vậy, bằng cách khai báo một số phương thức là thuần ảo, một lớp trừu tượng có thể “bắt buộc” các lớp con phải cài đặt các phương thức đó Hàm thuần ảo Khai báo work() là hàm thuần ảo Monkey trở thànhlớp trừu tượng class Monkey{ public: Monkey(...); virtual ~Monkey(); virtual void work() = 0; void eatABanana(); ... }; Như vậy, nếu các lớp LazyMonkey, CircusMonkey, MusicalMonkey... muốn tạo thể hiện thì phải tự cài đặt phương thức work() cho riêng mình Hàm thuần ảo Cũng có thể cung cấp định nghĩa cho hàm thuần ảo Điều này cho phép lớp cơ sở cung cấp mã mặc định cho phương thức, trong khi vẫn cấm lớp trừu tượng tạo thể hiện Để sử dụng đoạn mã mặc định này, lớp con cần cung cấp một định nghĩa gọi trực tiếp phiên bản định nghĩa của lớp cơ sở một cách tường minh Hàm thuần ảo Ví dụ, trong file chứa định nghĩa Monkey (.cpp), ta có thể có định nghĩa phương thức thuần ảo work() như sau: void Monkey::work() { cout << “No.” << endl; } Tuy nhiên, vì đây là phương thức thuần ảo nên lớp Monkey không thể tạo thể hiện Nếu muốn lớp LazyMonkey tạo được thể hiện và sử dụng định nghĩa mặc định của work(), ta có thể định nghĩa phương thức work() của LazyMonkey như sau: void LazyMonkey::work() { Monkey::work(); } Nhận xét Sử dụng hàm thuần ảo là một cách xây dựng chặt chẽ các cây thừa kếNó cho phép người tạo lớp cơ sở quy định chính xác những gì mà các lớp dẫn xuất cần làm, mặc dù ta chưa biết rõ chi tiết Ta có thể khai báo mọi phương thức mà ta muốn các lớp con cài đặt (quy định về các tham số và kiểu trả về), ngay cả khi ta không biết chính xác các phương thức này cần hoạt động như thế nào. Do làm việc với các hàm ảo, nên ta có đầy đủ ích lợi của đa hình động Nên khai báo các lớp cơ sở là trừu tượng mỗi khi có thể, điều đó làm thiết kế rõ ràng mạch lạc hơn đặc biệt là khi làm việc với các cây thừa kế lớn hoặc các cây thừa kế được thiết kế bởi nhiều người
File đính kèm:
- Bài giảng Lập trình hướng đối tượng - Các lớp trừu tượng.ppt