Author: Gia Đào
Trước khi diễn giải khái niệm tính kế thừa (Inheritance) trong lập trình hướng đối tượng, chúng ta cùng đến với một vài ví dụ trong thực tế để nắm bắt sơ qua về khái niệm này. Trong cuộc sống, tính kế thừa xuất hiện ở khắp mọi nơi. Chúng ta kế thừa những đặc điểm của bố mẹ chúng ta, chẳng hạn như màu mắt, màu tóc, thậm chí là tính cách. Ngoài tự nhiên, loài khỉ đột, khỉ đầu chó, đười ươi, hay vượn má trắng… mặc dù chúng có bề ngoài khác nhau, nhưng đều được các nhà sinh vật học xếp vào bộ Linh Trưởng bởi khả năng leo trèo tuyệt vời, não bộ lớn, sử dụng chi trước linh hoạt,… Chúng đều kế thừa những đặc điểm này từ loài vượn cổ xưa, tổ tiên của chúng trong lịch sử. Dù chưa đề cập tới khái niệm tính kế thừa trong lập trình nhưng với những ví dụ trên, chúng ta có thể hình dung qua nội dung cơ bản của nó: khi một tập con sở hữu những đặc điểm sẵn có từ tập cha và phát triển những đặc điểm riêng của mình, gọi là kế thừa. Hổ là sinh vật hoạt động cá nhân, có vằn, sinh trưởng thích hợp trong môi trường rừng rậm, trong khi đó sư tử sinh hoạt theo bầy và chỉ xuất hiện ở vùng đồng cỏ với khí hậu khô. Hổ và sư tử là hai loài vật hoàn toàn khác nhau với những đặc điểm khác nhau đã tiến hóa, tuy vậy, chúng vẫn được xếp chung vào lớp họ Mèo bởi những đặc tính chung được kế thừa như săn mồi bằng cách mai phục, hoạt động mạnh về đêm, là động vật có vú ăn thịt đi bằng bốn chân…
Trong ảnh là 1 ví dụ về Animal class, lớp Thú nói chung có các loài 4 chân như chó, mèo, và bò.
Tính kế thừa thú vị ở chỗ một cá thể được phát triển dựa trên những gì sẵn có và tự tạo nên những đặc tính riêng của mình. Trong khoa học, tính kế thừa được áp dụng để tận dụng những phát minh đã có sẵn nhằm tiết kiệm thời gian và công sức. Chẳng hạn, tập đoàn Apple sẽ không phát triển iPhone 13 bằng cách bắt đầu từ con số 0 mà chắc chắn, họ sẽ sử dụng lại những thiết kế, chức năng cơ bản của những đời iPhone trước để phát triển những tính năng mới cho iPhone 13. Ta có thể nói, iPhone 13 ‘kế thừa’ iPhone 11 hay 12 và được thêm những tính năng như chụp ảnh ‘nét’ hơn, giao diện thân thiện hơn hay pin dùng được lâu hơn.
Tính kế thừa trong lập trình hướng đối tượng được định nghĩa là, khi một lớp con (derived or child class) được thừa hưởng các thuộc tính (property) và phương thức (method) của lớp cha (base or parent class) mà nó kế thừa.
Trước khi đến với cách hoạt động của tính kế thừa, chúng ta cùng tìm ôn lại hai khái niệm cơ bản trong lập trình hướng đối tượng, đó là: Lớp (Class) và Đối tượng (Object).
Chúng ta có thể liên kết khái niệm trên thông qua một ví dụ: ‘Ngôi nhà’ được coi là một ‘đối tượng’ và khi chúng ta nhắc tới ‘Ngôi nhà’, chúng ta đều biết ‘ngôi nhà’ phải có những đặc điểm như ‘có cửa sổ’, ‘có cửa ra vào’, ‘mái che’, ‘phòng ngủ’… Những đặc điểm chung cơ bản nêu trên của một ngôi nhà mà tất cả chúng ta đều biết được gọi là ‘thuộc tính’ (Attributes). Tuy vậy, để xây được ngôi nhà, chúng ta cần có bản thiết kế. Lớp (Class), chính là bản thiết kế này, là nơi chúng ta sẽ thiết kế ngôi nhà trông ra sao, có màu gì, có bao nhiêu phòng…
class House:
color = “”
house_number = 0
Sample_house = House()
Sample_house.color = ‘blue’
Sample_house.house_number = 10
Ở đây chúng ta thấy, sau khi có bản thiết kế ngôi nhà, nói cách khác, khi khởi tạo class House, chúng ta có 2 đặc tính là color, house_number. Sample_house là 1 đối tượng được xây dựng dựa trên bản thiết kế House, và chúng ta có thể cho chúng các đặc điểm cụ thể.
Biến color và house_number được gọi bằng dấu ‘.’ và chúng ta lưu giá trị ‘blue’ cho color và 10 cho house_number.
Ở bức ảnh trên, ta thấy 3 lớp, xe buýt (Bus), xe con (Car), và xe tải (Truck) đều có chung các phương thức hoạt động như fuelAmount() (kiểm tra xăng), capacity() (số người có thể chứa), applyBrakes() (có phanh). Ta có thể thấy, đây là một vài đặc điểm cơ bản chung của các loại xe được nêu trên, tuy nhiên, nếu khai báo tất cả các lớp này với các phương thức lặp đi lặp lại như vậy sẽ tốn rất nhiều thời gian và công sức, do đó, ta sẽ sử dụng tính kế thừa, bằng cách tạo 1 lớp chung, gọi là ‘Phương tiện’ (Vehicle) với các phương thức như trên, và xe buýt, xe con, và xe tải sẽ là những lớp con kế thừa từ lớp cha này:
Trong ảnh là một ví dụ đơn giản để mô phỏng tính kế thừa. Lớp cha đại diện cho các phương tiện giao thông nói chung (Vehicle) mà trong đó, những phương thức của lớp cha bao gồm fuelAmount(), capacity(), applyBrakes(), sẽ được thừa hưởng bởi lớp con. Việc tạo ra lớp cha rồi sử dụng tính kế thừa khi tạo những lớp con giúp ta tiết kiệm rất nhiều thời gian và tránh được việc lặp lại các phương thức khi phải tạo ra nhiều lớp.
Mô phỏng tính kế thừa bằng Python:
# define a superclass
class super_class:
# attributes and method definition
# inheritance
class sub_class(super_class):
# attributes and method of super_class
# attributes and method of sub_class
Super class ở đây là lớp cha và sub_class là lớp con kế thừa từ lớp cha. Ví dụ trên là cách khai báo lớp nói chung và dưới đây là ví dụ cụ thể:
class Animal:
# attribute and method of the parent class
name = “”
def eat(self):
print(“I can eat”)
# inherit from Animal
class Dog(Animal):
# new method in subclass
def display(self):
# access name attribute of superclass using self
print(“My name is “, self.name)
# create an object of the subclass
labrador = Dog()
# access superclass attribute and method
labrador.name = “Rohu”
labrador.eat()
# call subclass method
labrador.display()
Ở đây ta khởi tạo lớp cha là Animal với 1 biến name và phương thức eat(). Phương thức eat khi được gọi sẽ in ra dòng “I can eat”. Dog là 1 lớp con cụ thể được tạo ra bằng việc kế thừa từ lớp cha. Để khởi tạo lớp con Dog, ta truyền lớp cha Animal vào trong 2 dấu ngoặc tròn. Lưu ý rằng, bởi lớp con Dog được kế thừa từ lớp cha và thông qua những ví dụ ở trên, ta biết rằng lớp con Dog có thể thừa hưởng những biến và phương thức của lớp cha.
Thật vậy, phương thức display() trong lớp Dog, có thể gọi biến name trong lớp cha thông qua biến self: self.name. Nói 1 cách cụ thể, khi phương thức display() được gọi, kết quả sẽ in ra “My name is “ với biến name sở hữu bất kỳ giá trị nào.
Cuối cùng, ta khởi tạo labrador = Dog() để có thể sử dụng lớp con Dog. Labrador chính là 1 đối tượng (Object) mà chúng ta đã đề cập tới trong bài học. Một lần nữa, ta thấy 3 dòng:
Labrador.name = “Rohu” => Lưu trữ giá trị xâu “Rohu” vào trong biến name
Labrador.eat() => gọi phương thức eat (sử dụng dấu ‘.’)
Labrador.display() => gọi phương thức display (sử dụng dấu ‘.’)
Ba dòng code trên đều cho thấy rằng, labrador là 1 đối tượng của lớp con nhưng bởi tính kế thừa, nó hoàn toàn có thể sở hữu những phương thức và biến của lớp cha và truy cập tới chúng. Khi chạy toàn bộ đoạn code trên, chúng ta có kết quả như sau:
Output:
I can eat
My name is Rohu