Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 139 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
139
Dung lượng
6,33 MB
Nội dung
Chơng Thừa kế đa hình Hai nguyờn lý thừa kế đa hình lậptrìnhhướngđốitượng giúp ta xây dựng chương trình cách nhanh chóng hiệu hơn, thu kết mơ-đun chương trình mà lậptrình viên khác dễ mở rộng hơn, có khả đáp ứng tốt thay đổi liên tục yêu cầu khách hàng 7.1 QUAN HỆ THỪA KẾ Nhớ lại ví dụ lậptrìnhhướngđốitượng Ch-¬ng Trong đó, Dậu xây dựng lớp: Square (hình vng), Circle (đường tròn), Triangle (hình tam giác), Amoeba (hình trùng biến hình) Cả bốn hình với hai phương thức rotate() playSound() Do đó, dùng tư trừu tượng hóa để tách đặc điểm chung đưa chúng vào lớp có tên Shape (hình nói chung) Sau đó, kết nối lớp hình vẽ với lớp Shape quan hệ gọi thừa kế Ta nói "Square thừa kế từ Shape", "Circle thừa kế từ Shape", v.v Ta tháo gỡ rotate() playSound khỏi loại hình, phải quản lý đặt lớp Shape Shape gọi lớp cha (superclass) hay lớp sở (base class) bốn lớp Còn bốn lớp lớp (subclass) hay lớp dẫn xuất (derived class) lớp Shape Các lớp thừa kế phương thức lớp cha Nói cách khác, lớp Shape có chức lớp tự động có chức cỏ bốn lớp Shape lớp cha rotate() playSound() quan hệ thừa kế lớp Square Circle Triangle overriding Amoeba rotate() { // mã xoay hình // riêng cho amoeba } playSound() { // mã chơi nhạc // riêng cho amoeba} Vậy quan hệ thừa kế? Nếu ta cần xây dựng lớp đại diện cho hai loài mèo nhà hổ, mèo nhà nên thừa kế từ hổ, hay hổ nên thừa kế từ mèo, hay hai thừa kế từ lớp thứ ba? 103 Khi ta dùng quan hệ thừa kế thiết kế, ta đặt phần mã dùng chung lớp coi lớp cha – lớp dùng chung trừu tượng hơn, lớp cụ thể lớp Các lớp thừa kế từ lớp cha Quan hệ thừa kế có nghĩa lớp thừa hưởng thành viên (member) lớp cha Thành viên lớp biến thực thể phương thức lớp Ví dụ, Shape ví dụ có hai thành viên rotate() playSound(), Cow Hình 5.6 có thành viên name, age, getName(), getAge(), setName(), setAge() Ta nói lớp chun biệt hóa (specialize) lớp cha Nghĩa "chuyên biệt hóa" gồm có hai phần: (1) lớp loại lớp cha – thể chỗ lớp tự động thừa hưởng thành viên lớp cha, (2) lớp có đặc điểm riêng - thể chỗ lớp bổ sung phương thức biến thực thể riêng mình, cài đè (override) phương thức thừa kế từ lớp cha Ví dụ, hình trùng biến hình (Amoeba) hình (Shape), lớp Amoeba có tất mà Shape có Ngồi ra, Amoeba có thêm đặc điểm riêng thể loại hình trùng biến hình: biến thực thể đại diện cho tâm xoay để phục vụ cách xoay riêng nó, định nghĩa lại phương thức rotate để xoay theo cách riêng, định nghĩa lại playSound để chơi loại âm riêng Theo thuật ngữ, từ khóa, Java, lớp "nối dài" (extends) lớp cha Các biến thực thể khơng bị cài đè việc khơng cần thiết Biến thực thể không quy định hành vi đặc biệt lớp việc gán giá trị tùy chọn cho biến thừa kế 7.2 THIẾT KẾ CÂY THỪA KẾ Giả sử ta cần thiết kế chương trình giả lập cho phép người dùng thả đám động vật thuộc loài khác vào mơi trường để xem chuyện xảy Ta chưa phải viết mã mà giai đoạn thiết kế Ta biết vật đại diện đối tượng, đốitượng di chuyển loanh quanh mơi trường, thực hành vi lậptrình cho lồi vật Ta giao danh sách lồi vật đưa vào chương trình: sư tử, hà mã, hổ, chó, mèo, sói Và ta muốn rằng, cần, lậptrình viên khác bổ sung lồi vật vào chương trình Bước 1, ta xác định đặc điểm chung trừu tượng mà tất loài động vật có Các đặc điểm chung bao gồm: năm biến thực thể: picture – tên file ảnh đại diện cho vật 104 food – loại thức ăn mà vật thích Hiện giờ, biến có hai giá trị: cỏ (grass) thịt (meat) hunger – biến int biểu diễn mức độ đói vật Biến thay đổi tùy theo vật ăn ăn boundaries – giá trị biểu diễn chiều dọc chiều ngang (ví dụ 640 x 480) khu vực mà vật lại hoạt động location – tọa độ X Y vật khu vực bốn phương thức: makeNoise() – hành vi vật phát tiếng kêu eat() – hành vi vật gặp nguồn thức ăn ưa thích, thịt cỏ sleep() – hành vi vật coi ngủ roam() – hành vi vật ăn hay ngủ, lang thang đợi gặp ăn gặp biên giới lãnh địa Bước 2, thiết kế lớp với tất thuộc tính hành vi chung kể Đây lớp mà tất lớp động vật chuyên biệt hóa Các đốitượng ứng dụng vật (animal), đó, ta gọi tên lớp cha chung chúng Animal Ta đưa vào phương thức biến thực thể mà tất vật cần Kết ta lớp cha lớp tổng quát hơn, hay nói cách khác trừu tượng hơn, lớp mang tính đặc thù hơn, chuyên biệt lớp cha Các vật hoạt động có giống không? Ta biết loại Animal có tất biến thực thể khai báo cho Animal Một sư tử có giá trị riêng cho picture, food, hunger, boundaries, location Một hà mã có giá trị khác cho biến thực thể tương tự Cũng chó, hổ Thế hành vi chúng sao? 105 Bước 3: Xác định xem lớp có cần hành vi (cài đặt phương thức) đặc thù thể loại cụ thể hay khơng? Để ý lớp Animal Chắc chắn sư tử khơng ăn giống hà mã Còn tiếng kêu, ta viết phương thức makeNoise Animal chơi file âm có tên giá trị biến thực thể mà có giá trị khác tùy lồi, để vật kêu khác vật khác Nhưng làm chưa đủ tùy tình mà loài khác phát tiếng kêu khác nhau, chẳng hạn tiếng kêu ăn tiếng kêu gặp kẻ thù, v.v Do đó, ta định eat() makeNoise() nên cài đè lớp Tạm coi vật sleep roam không cần cài đè hai phương thức Ngồi ra, số lồi có hành vi riêng đặc trưng lồi đó, chẳng hạn chó có thêm hành vi đuổi mèo (chaseCats()) bên cạnh hành vi mà lồi động vật khác có Bước 4: Tiếp tục dùng trừu tượng hóa tìm lớp có hành vi giống nhau, với mục đích phân nhóm mịn cần Ví dụ, sói chó có họ hàng gần, thuộc họ Chó (canine) phân loại động vật học, chúng có xu hướng di chuyển theo bầy đàn nên dùng chung phương thức roam() Mèo, hổ sư tử thuộc họ Mèo (feline) Ba loài chung phương thức roam() di chuyển chúng có xu hướng tránh đồng loại Ta hà mã tiếp tục dùng phương thức roam() tổng quát thừa kế từ Animal Ta tạm hồn thành thiết kế Hình 7.1 quay lại tốn chương sau 106 Hình 7.1: Cây thừa kế loài động vật 7.3 CÀI ĐÈ – PHƯƠNG THỨC NÀO ĐƯỢC GỌI? Lớp Wolf có bốn phương thức: sleep() thừa kế từ Animal, roam() thừa kế từ Canine (thực phiên đè Animal), hai phương thức mà Wolf cài đè Animal - makeNoise() eat() Khi ta tạo đốitượng Wolf gán biến tham chiếu tới nó, ta dùng biến để gọi bốn phương thức Nhưng phiên chúng gọi? 107 Khi gọi phương thức từ tham chiếu đối tượng, ta gọi phiên đặc thù phương thức lớp đốitượng cụ thể Nếu hình dung thừa kế theo kiểu lớp cha phía lớp phía dưới, quy tắc là: phiên thấp gọi Trong ví dụ dùng biến w để gọi phương thức cho đốitượng Wolf trên, thứ tự từ thấp lên cao Wolf, Canine, Animal Khi gọi phương thức cho đốitượng Wolf, máy ảo Java bắt đầu tìm từ lớp Wolf lên, khơng tìm phiên phương thức Wolf chuyển lên tìm lớp bên Wolf thừa kế, tìm thấy phiên khớp với lời gọi phương thức Với ví dụ xét, minh họa hình vẽ, w.makeNoise() dẫn đến việc kích hoạt phiên Wolf, w.roam() gọi phiên Canine, v.v 7.4 CÁC QUAN HỆ IS-A VÀ HAS-A Như trình bày chương trước, lớp kế thừa từ lớp khác, ta nói lớp chuyên biệt hóa lớp cha Nhưng liệu nên chuyên biệt hóa lớp khác? Nhớ lại lớp cha loại tổng quát, lớp loại cụ thể chuyên biệt, loại lớp cha Nhìn từ khía cạnh khác, tập hợp đốitượng mà lớp đại diện tập đốitượng mà lớp cha đại diện Do đó, để đưa lựa chọn đắn cho vấn đề nên hay không nên để lớp X lớp chuyên biệt hóa lớp Y, ta có phương pháp hiệu quả: kiểm tra quan hệ IS-A, nghĩa xem thứ có thứ hay khơng Để xem X có nên lớp Y hay khơng, ta đặt câu hỏi theo dạng "Nếu phát biểu cách tổng quát loại X dạng/thứ/kiểu loại Y có lý hay khơng?" Nếu câu trả lời "Có", X lớp Y Ví dụ: Tam giác hình (Triangle IS-A Shape)? Đúng Mèo động vật họ Mèo (Cat IS-A Feline)? Đúng Xe tải phương tiện giao thơng (Truck IS-A Vehicle)? Đúng Nghĩa là, Triangle lớp Shape, Cat lớp Feline, Truck lớp Vehicle Ta xét tiếp: Phòng bếp nhà (Kitchen IS-A House)? Chắc chắn sai Ngược lại sao? Nhà phòng bếp (House IS-A Kitchen)? Đúng có số người phong tục hay điều kiện sống mà ngơi nhà họ có phòng nên vừa nơi nấu bếp vừa phòng cho nhiều chức khác Tuy nhiên, trường hợp "một số", nên câu trả lời tổng quát "Sai" Cho nên, Kitchen lớp House hay ngược lại Phòng bếp nhà rõ ràng có liên quan đến nhau, qua quan hệ thừa kế mà quan hệ chứa – HAS-A Câu hỏi là: Nhà có chứa phòng bếp hay khơng (House HAS-A Kitchen)? Nếu câu trả lời "Có", điều có nghĩa House có biến thực thể kiểu Kitchen Nói cách khác, House có tham 108 chiếu tới đốitượng Kitchen, House không chuyên biệt hóa Kitchen hay ngược lại Quan hệ HAS-A Java cài đặt tham chiếu đặt đốitượng chứa chiếu tới đốitượng thành phần Quan hệ HAS-A hai lớp thể ba quan hệ: kết hợp (association), tụ hợp (aggregation) hợp thành (composition) mà tài liệu thiết kế hướngđốitượng thường nói đến Giữa hai lớp có quan hệ kết hợp đốitượng thuộc lớp cần biết đến đốitượng thuộc lớp để thực cơng việc Chẳng hạn, người nhân viên chịu quản lý người quản lý, ta có quan hệ kết hợp nối từ Employee tới Manager, thể việc đốitượng Employee có tham chiếu boss kiểu Manager Hợp thành tụ hợp quan hệ đốitượng thành phần (cũng đối tượng) Khác chỗ, với quan hệ hợp thành, đốitượng thành phầnphần thiếu đốitượng chứa nó, với quan hệ tụ hợp ngược lại Ví dụ, sách bao gồm nhiều trang sách sách tồn khơng có trang Do Book (sách) Page (trang) có quan hệ hợp thành Thư viện có nhiều sách, thư viện khơng có sách thư viện, nên quan hệ Library (thư viện) Book quan hệ tụ hợp Java khơng có cấu trúc dành riêng để cài đặt quan hệ tụ hợp hay hợp thành Ta cài đặt đơn giản cách đặt vào đốitượng chủ tham chiếu tới đốitượng thành phần, hay nói cách khác phân rã thành quan hệ HAS-A, chẳng hạn quan hệ hợp thành Book Page phân rã thành 'Book HAS-A ArrayList' nhiều quan hệ 'ArrayList HAS-A Page' Các ràng buộc khác đảm bảo phương thức có nhiệm vụ khởi tạo hay sửa tham chiếu Quay lại quan hệ IS-A, có điểm cần lưu ý: quan hệ thừa kế IS-A có chiều Ví dụ: "Tam giác hình" phát biểu có lý, khẳng định theo chiều ngược lại, "Hình tam giác", khơng Có nhiều hình hình tam giác, có vơ số hình khơng phải hình tam giác Thực ra, lưu ý hiển nhiên, ta nhớ đến mô tả lớp mục trước: Lớp chuyên biệt hóa lớp cha Đến đây, chưa kết thúc câu chuyện quan hệ thừa kế Chương sau tiếp tục trình bày vấn đề hướngđốitượng Một số giải pháp thiết kế chương xem lại cải tiến 109 7.5 KHI NÀO NÊN DÙNG QUAN HỆ THỪA KẾ? Mục liệt kê số quy tắc hướng dẫn việc sử dụng quan hệ thừa kế thiết kế Tại thời điểm này, ta tạm lòng với việc biết quy tắc Việc hiểu quy tắc chưa trọn vẹn bồi đắp dần phần sau sách NÊN dùng quan hệ thừa kế lớp loại cụ thể lớp cha Ví dụ, tài khoản tiết kiệm (saving account) loại tài khoản ngân hàng (bank account), nên SavingAccount lớp BankAccount hợp lí NÊN cân nhắc việc thừa kế ta có hành vi (mã viết) nên dùng chung nhiều lớp thuộc kiểu tổng qt Ví dụ, Square, Circle Triangle toán Dậu Tuất cần xoay chơi nhạc, nên việc đặt chức lớp cha Shape hợp lí Tuy vậy, cần lưu ý thừa kế đặc điểm quan trọng lậptrìnhhướngđốitượng khơng thiết cách tốt cho việc tái sử dụng hành vi Quan hệ thừa kế giúp ta khởi động việc tái sử dụng, thường lựa chọn thiết kế, mẫu thiết kế giúp ta nhận lựa chọn khác tinh tế linh hoạt KHÔNG NÊN dùng thừa kế nhằm mục đích tái sử dụng mã lớp khác, quan hệ lớp cha lớp vi phạm hai quy tắc Ví dụ, giả sử ta viết cho lớp DoorBell (chuông cửa) đoạn mã dành riêng cho việc in, ta cần viết mã cho chức in lớp Piano Khơng nên nhu cầu mà cho Piano làm lớp DoorBell Đàn piano loại chuông gọi cửa (Giải pháp nên chọn cho tình là: phần mã cho chức in nên đặt lớp Printer, lớp cần có chức in hưởng lợi từ lớp Printer qua quan hệ HAS-A.) KHƠNG NÊN dùng quan hệ thừa kế lớp lớp cha không qua thử nghiệm IS-A Hãy tự kiểm tra xem lớp có phải kiểu chun biệt lớp cha hay khơng Ví dụ: Bike IS-A Vehicle (xe đạp phương tiện giao thông) hợp lí Nhưng Vehicle IS-A Bike (phương tiện giao thơng loại xe đạp) khơng 7.6 LỢI ÍCH CỦA QUAN HỆ THỪA KẾ Quan hệ thừa kế thiết kế mang lại cho ta nhiều điều Lợi ích thứ nhất: tránh lặp đoạn mã bị trùng lặp Ta loại bỏ đoạn mã trùng lặp cách tách hành vi chung nhóm lớp đốitượng đưa phần mã vào lớp cha Nhờ đó, ta cần sửa nó, ta cần cập nhật mã nơi, sửa đổi có hiệu lực tất lớp kế thừa hành vi Cơng việc gói gọn việc sửa dịch lớp cha Tóm lại: ta khơng phải động đến lớp con! 110 Với ngơn ngữ Java, chương trình tập lớp Do đó, ta khơng cần phải dịch lại lớp để dùng phiên lớp cha Đòi hỏi phiên lớp cha không phá vỡ lớp Nghĩa cụ thể từ "phá vỡ" ngữ cảnh trình bày chi tiết sau Tạm thời, ta cần hiểu hành động có nghĩa sửa lớp cha mà lớp bị phụ thuộc vào, chẳng hạn sửa kiểu tham số, hay kiểu trả về, tên phương thức Lợi ích thứ hai: ta định nghĩa giao thức chung cho tập lớp gắn kết với quan hệ thừa kế Quan hệ thừa kế cho phép ta đảm bảo tất lớp lớp có tất phương thức7 mà lớp có Đó dạng giao thức mà lớp tuyên bố với tất phần mã khác rằng: "Tất thể loại tơi (nghĩa lớp con) làm việc này, với phương thức trông " Nói cách khác, ta thiết lập hợp đồng (contract) Lưu ý rằng, nói Animal bất kì, ý ta nói đốitượng Animal hay đốitượng thuộc lớp có Animal tổ tiên phả hệ Khi ta định nghĩa kiểu tổng quát (lớp cha) cho nhóm lớp, lớp nhóm dùng thay cho vị trí lớp cha Ta có Wolf loại Animal; đốitượng Wolf có tất thành viên mà đốitượng Animal có Vậy lơ-gic hiển nhiên: đốitượng Wolf coi thuộc loại Animal; nơi dùng Animal dùng Wolf Ta bắt đầu chạm đến phần thú vị lậptrìnhhướngđối tượng: đa hình 7.7 ĐA HÌNH Trước trình bày đa hình, ta nhắc lại chút cách khai báo tham chiếu tạo đốitượng Nếu muốn nói thật xác phải "tất phương thức thừa kế được" Tạm thời, có nghĩa "các phương thức public", ta tinh chỉnh định nghĩa sau 111 Trong ví dụ trên, tham chiếu w khai báo lệnh Wolf w, đốitượng lớp Wolf khai báo lệnh new Wolf Điểm đáng ý kiểu biến tham chiếu kiểu đốitượng Wolf Với đa hình sao? Đây ví dụ: w khai báo thuộc kiểu Animal, đốitượng tạo theo kiểu Wolf: Animal w = new Wolf(); :Wolf w đốitượng Wolf Animal tham chiếu kiểu Animal, đốitượng kiểu Wolf Với đa hình, tham chiếu thuộc kiểu lớp cha lớp đốitượng tạo Khi ta khai báo biến tham chiếu thuộc kiểu lớp cha, gắn vớiđốitượng thuộc lớp Đặc tính cho phép ta có thứ thú vị kiểu mảng đa hình Ví dụ, Hình 7.2, ta khai báo mảng kiểu Animal, nghĩa mảng để chứa đốitượng thuộc loại Animal Nhưng sau ta lại gắn vào mảng đốitượng thuộc lớp tùy ý Animal Và vòng lặp duyệt mảng sau phần thú vị liên quan đến đa hình – ý trọng tâm ví dụ Tại đó, ta duyệt từ đầu đến cuối mảng, vớiphần tử mảng, ta gọi phương thức Animal từ tham chiếu kiểu Animal Khi i chạy từ tới 4, animals[i] chiếu tới đốitượng Dog, Cat, Wolf, Hippo, Lion Kết animals[i].eat() hay animals[i].roam() là: đốitượng thực phiên thích hợp với loại Hình 7.2: Mảng đa hình Tính đa hình thể kiểu liệu đối số giá trị trả 112 Hình 13.9: Lỗi run-time sử dụng TreeSet cho Contact Tương tự tình so sánh bằng, TreeSet, hay Collections tự biết cách so sánh đốitượng thuộc lớp mà lậptrình viên tự xây dựng Chương trình Hình 13.9 biên dịch khơng có lỗi add() khơng u cầu tham số kiểu Comparable, chạy gặp lỗi run-time lệnh gọi đến phương thức Tóm lại, phần tử cấu trúc danh bạ phải thuộc lớp đốitượng có cung cấp phương tiện so sánh Ta chọn hai cách sau để giải vấn đề đó: Các phần tử danh sách phải thuộc lớp có cài interface Comparable Ta sửa lớp Contact để bổ sung phần in đậm Hình 13.10, chương trình Hình 13.9, sau chạy khơng có lỗi Hình 13.10: Cài interface Comparable Sử dụng phương thức chồng có lấy tham số kiểu Comparator Ta viết thêm lớp ContactCompare theo interface Comparator dùng chương trình TestTreeSet dòng in đậm Hình 13.11 Theo đó, ContactCompare loại Comparator riêng dành cho việc so sánh đốitượng Contact Còn danh bạ đốitượng TreeSet tạo kèm với loại Comparator đặc biệt để 227 biết cách đối xử vớiphần tử danh bạ (cContact đối số gọi hàm khởi tạo TreeSet) Hình 13.11: Sử dụng Comparator Cả hai cách áp dụng cho phương thức sort() Collection tiện ích tổng quát tương tự thư viện Java 13.6 KÍ TỰ ĐẠI DIỆN TRONG KHAI BÁO THAM SỐ KIỂU Quan hệ thừa kế hai lớp khơng có ảnh hưởng đến quan hệ cấu trúc tổng quát dùng cho hai lớp Chẳng hạn, Dog Cat lớp Animal, ta đưa đốitượng Dog Cat vào ArrayList, tính chất đa hình Dog, Cat, Animal hoạt động bình thường (xem ví dụ Hình 13.12) Tuy nhiên, ArrayList, ArrayList lại khơng có quan hệ với ArrayList Vậy cho nên, dùng ArrayList làm đối số cho phương thức yêu cầu đối số kiểu ArrayList, ví dụ Hình 13.13, trình biên dịch báo lỗi sai kiểu liệu 228 Hình 13.12: Đa hình bên cấu trúc tổng quát Hình 13.13: Khơng có đa hình cấu trúc tổng qt Tóm lại, ta khai báo phương thức lấy đối số kiểu ArrayList, lấy đối số kiểu ArrayList lấy kiểu ArrayList hay ArrayList Ta khơng hài lòng vớivới việc thỏa hiệp, nghĩa dùng ArrayList thay ArrayList cho danh sách chứa tồn Dog Vì trình biên dịch khơng kiểm tra kiểu liệu để ngăn chặn tình chẳng hạn danh sách chó nghiệp vụ lính cứu hỏa lại có mèo 229 Hình 13.14: Nguy cho mèo vào danh sách chó Vậy làm để làm cho phương thức nhận đối số thuộc kiểu ArrayList, ArrayList,…nghĩa ArrayList dành cho kiểu lớp Animal? Giải pháp sử dụng kí tự đại diện (wildcard) Ta sửa phương thức makeASymphony() sau, chương trình Hình 13.13 chạy chạy ? extends Animal có nghĩa kiểu thuộc loại Animal Nhớ từ khóa extends có nghĩa "là lớp của" "cài đặt", tùy vào việc theo sau từ khóa extends tên lớp hay tên interface Vậy nên muốn makeASymphony() lấy đối số ArrayList loại cài interface Pet, ta khai báo sau: Nhưng ArrayList khai báo, trình biên dịch khơng cho ta thêm vào danh sách mà tham số phương thức chiếu tới Ta gọi phương thức phần tử danh sách, ta thêm phần tử vào danh sách Do đó, ta n tâm chương trình chạy Ví dụ, makeASymphony() với nội dung khơng gặp lỗi biên dịch, takeAnimals() với nội dung Hình 13.14 khơng biên dịch 230 Hai cú pháp sau tương đương: public void foo( ArrayList