Từ Khoá Super Trong Ruby?
Đặt vấn đề
Kế thừa là một 1 trong 4 yếu tố cốt lõi tạo nên đặc tính hướng đối tượng của một ngôn ngữ (như những gì chúng ta đã được dạy ở trường). Riêng điều đó thôi đã đủ để thể hiện tầm quan trọng của nó (có thật không nhỉ :grimacing:).
Trong ngôn ngữ ruby có một từ khoá thường xuyên xuất hiện khi cần kế thừa hay override lại phương thức lớp cha đó là super
.
Qua bài viết chúng ta sẽ cùng tìm hiểu về nó.
Luận bàn
1. Định nghĩa
1 | It calls a method on the parent class with the same name as the method that calls super. |
Ví dụ:
1 | class Dog < Pig |
=> Khi ta gọi phương thức testing
với một instance của lớp Dog, ruby sẽ đi đến class cha/ông/cụ/kỵ… của Dog
để tìm và chạy phương thức testing
được implement ở lớp cha/ông/cụ/kỵ… đó.
2. So sánh super và super()
Với những bạn mới học, chúng ta đôi lúc sẽ nhầm lẫn là 2 cách gọi này thực chất là một, cũng bởi vì ruby cho phép sử dụng ()
như một optional, tức là có cũng được mà không có cũng được.
Nhưng thực ra không phải vậy, 2 cách sử dụng này sẽ dẫn đến các kết qủa khác nhau.
Cùng xét một ví dụ: Giả sử tôi có class Cat và một class con của Cat tên là YellowCat, mã nguồn như sau.
Sử dụng super
1 | class Cat |
Sử dụng super()
1 | class Cat |
Kết quả là tương tự nhau, điều này giúp chúng ta ghi nhớ một ý, đó là khi không có tham số truyền vào method thì dùng super
hay super()
là tương đương nhau
Ta xét một ví dụ khác:
Sử dụng super
1 | class Cat |
Sử dụng super()
1 | class Cat |
Đến bây giờ chúng ta đã phát sinh một lỗi wrong number of arguments (given 0, expected 1)
.
Điều này xảy ra là vì:
Với từ khoá super
bạn sẽ truyền TẤT CẢ tham số lên method đó ở lớp cha. Còn với super()
bạn chỉ truyền lên lớp cha những tham số mà bạn bỏ vào trong super()
.
Như ta thấy trong ví dụ trên, ta không bỏ gì vào super()
=> khi lên lớp cha (lớp Cat) method initialize yêu cầu tham số name nhưng chúng ta không truyền gì lên và từ đó dẫn đến lỗi wrong number of arguments.
Để khắc phục chúng ta chỉ cần truyền tham số vào super() để lớp Cat nhân được tham số đó.
1 | class YellowCat < Cat |
3. Sự hữu ích của super()
Tôi có một bài toán:
:one: Tôi có 1 lớp Cat với 2 instance variable là name và age.
:two: Lớp YellowCat là lớp con của Cat.
:three: Lớp YellowCat có thêm 1 instance variable là role.
Xây dựng lớp Cat có vẻ đơn giản, ta làm như sau:
1 | class Cat < Animal |
Tiếp theo đến lớp YellowCat, xem chừng cũng không khó:
1 | class YellowCat < Cat |
Cách này cũng hoàn thành được yêu cầu của bài toán, nhưng nó không ổn tí nào. Vì sao? Vì khi bạn tạo một instance thuộc lớp YellowCat nó sẽ có tổng cộng 5 instance variable bao gồm 3 instance variable khai báo trong lớp YellowCat, và 2 trong lớp Cat (bị thừa 2 instance variable).
Điều này đi ngược lại với các quy tắc lập trình quan trọng chúng ta cần tuần thủ như DRY.
Đến đây sức mạnh super()
sẽ được thời cơ để thể hiện. Ta sẽ optimize lại mã nguồn của YellowCat như sau:
1 | class YellowCat < Cat |
Ở lớp con chúng ta có thêm 1 instance variable, chúng ta khai báo cho mình nó và duy nhất mỗi nó :arrow_right: không lãng phí vùng nhớ và tận dụng được mã nguồn cũ.
Trong phương thức khởi tạo đối tượng YellowCat chúng ta khởi tạo biến instance mới, còn 2 instance variable được kế thừa từ lớp cha thì ta truyền tham số vào super()
để truyền lên lớp cha và mặc kệ lớp cha xử lý thế nào thì tuỳ.
Kết luận
Tài liệu tham khảo:
[1] odetocode.com