Kế thừa lớp là một tính năng cơ bản của OOP (Lập Trình Hướng Đối Tượng) giúp bạn tạo phiên bản cụ thể & chuyên biệt hơn của bất kỳ lớp nào.
Ví dụ:
Food -> Fruit -> Orange
Có mối quan hệ giữa các lớp này!
Chúng ta có thể nói cam là một trái cây, nhưng trái cây cũng là thức ăn.
Lớp cha (còn gọi là super class hoặc base class) luôn tổng quát hơn các lớp con.
Fruit (tổng quát hơn) là lớp cha của Orange (cụ thể hơn).
Trong Ruby nó trông như sau:
class Food end class Fruit < Food end class Orange < Fruit end
Một trong những hàm ý của kế thừa trong Ruby là mọi phương thức & hằng số được định nghĩa trên Food sẽ có sẵn trên Fruit, và cũng trên Orange.
Các phương thức được truyền xuống từ lớp cơ sở, nhưng không ngược lại.
Mục Đích Của Kế Thừa Là Gì?
Bạn có thể nghĩ rằng tạo phân cấp đối tượng cung cấp某种 „cảm giác tốt“ tổ chức cho code của bạn, nhưng có nhiều hơn thế.
Kế thừa được dùng để tạo phiên bản khác của lớp cha phù hợp cho một mục đích cụ thể. Có thể nó cần thêm tính năng hoặc phương thức không có ý nghĩa trong lớp cha.
Ví dụ:
Tất cả trái cây đều có màu sắc, trọng lượng và tên.
Một số trái cây có thể có đặc điểm đặc biệt không chia sẻ với trái cây khác, vì vậy bạn tạo lớp mới kế thừa đặc điểm của TẤT CẢ trái cây (màu sắc, trọng lượng, v.v.) & sau đó thêm đặc điểm đặc biệt.
Đó là ý tôi nói về chuyên biệt hóa.
Ví dụ khác:
Bạn có một lớp ghi vào cơ sở dữ liệu, nhưng bạn muốn phiên bản khác của lớp đó (có thể để debug) ghi log tất cả hoạt động cơ sở dữ liệu.
Trong trường hợp này, bạn muốn mẫu decorator (decorator pattern).
Bạn có thể đọc giải thích chi tiết trong bài này, nhưng ý tưởng cơ bản là bạn dùng kế thừa để bao bọc lớp khác & thêm thứ mới vào nó.
Mà không phải thay đổi lớp gốc!
Kế Thừa Trong Thực Tế
Được.
Chúng ta đã học về kế thừa, nhưng bạn có biết rằng bạn đang dùng nó hàng ngày như một nhà phát triển Ruby?
Ruby bản thân dùng kế thừa để kích hoạt các phương thức như:
putsclasssuper
Điều này là vì tất cả đối tượng Ruby mặc định kế thừa từ lớp Object.
Vì vậy nếu bạn tạo lớp như sau:
class Apple end
Lớp cha của nó là Object:
Apple.superclass # Object
Đó là lý do bạn có thể dùng các phương thức như những cái trên.
Ví dụ, khi bạn gọi puts Ruby tìm phương thức này trong lớp của bạn.
Sau đó:
- Nó tìm phương thức trong một trong các lớp cha
- Nếu không tìm thấy, nó bắt đầu từ đối tượng lại & sẽ cố tìm
method_missing - Nếu không tìm thấy, nó sẽ ném
NoMethodError, hoặcNameErrornếu phương thức được gọi mà không có đối tượng tường minh (a.sizevssize, nơialà đối tượng tường minh)
Bạn có thể tìm thêm ví dụ về kế thừa trong Rails.
Ngay đây:
class ApplicationController < ActionController::Base end
Controller:
class SessionsController < ApplicationController end
Model:
class Comment < ApplicationRecord belongs_to :article end
Kế thừa נמצא ở khắp nơi trong Ruby, nhưng đôi khi nó không phải giải pháp đúng.
Composition: Một Lựa Chọn Thay Thế Cho Kế Thừa
Kế thừa có những hạn chế nhất định.
Ví dụ:
Bạn muốn xây dựng một chiếc máy tính từ các bộ phận.
Chúng ta nói rằng máy tính có các bộ phận, nhưng các bộ phận riêng lẻ không phải là máy tính tự thân.
Nếu bạn tháo rời chúng, chúng không thể thực hiện chức năng của mình.
Chúng ta cần thứ gì đó khác…
Chúng ta cần composition (hợp thành)!
Composition xây dựng lớp nơi các bộ phận khác nhau kết hợp để thực hiện một chức năng.
Giống như một chiếc máy tính.
Ví dụ composition trong hành động:
class Computer def initialize(memory, disk, cpu) @memory = memory @disk = disk @cpu = cpu end end
Máy tính được cấp các bộ phận nó cần để hoạt động.
Đây là composition.
Nguyên Lý Thay Thế Liskov
Kế thừa mạnh mẽ khi dùng trong tình huống đúng.
Nhưng như tất cả công cụ, nó có thể bị lạm dụng!
Thực tế, có một trích dẫn nổi tiếng từ cuốn Design Patterns gốc như sau:
„Ưu tiên composition hơn kế thừa.“
Design Patterns: Elements of Reusable Object-Oriented Software
Để đảm bảo bạn dùng kế thừa đúng cách có một nguyên lý bạn có thể tuân theo, chữ L từ SOLID.
Nó đại diện cho „Liskov Substitution Principle“ (Nguyên lý thay thế Liskov).
Nguyên lý này nói rằng các lớp con của bạn phải có thể được dùng thay cho lớp cơ sở của bạn.
Nói cách khác:
Nếu bạn kế thừa từ Fruit & color là một chuỗi, bạn không muốn thay đổi color để trả về symbol trong lớp con.
Ví dụ:
Người dùng của Fruit phụ thuộc vào color trả về chuỗi.
class Fruit def color "orange" end end
Điều này vi phạm LSP:
class Orange < Fruit def color :orange end end
Nếu bạn thay đổi color để trả về symbol, thì bạn không thể thay thế đối tượng Fruit bằng Orange.
Tại sao?
Vì nếu bạn gọi phương thức như split trên symbol bạn sẽ nhận lỗi.
Đó là phương thức symbol không có.
Nhưng chuỗi có.
Cờ đỏ khác là khi lớp con của bạn không phải là chuyên biệt hóa thực sự của lớp cha & bạn chỉ dùng lớp cha để chia sẻ phương thức tiện ích.
Tóm Tắt
Bạn đã học về kế thừa & composition trong Ruby!
Bạn có thể dùng kế thừa để tạo phiên bản chuyên biệt của lớp cha, và composition để lắp ráp các thành phần thành một toàn thể. Hãy nhớ tuân theo nguyên lý LSP nếu bạn không muốn làm rối.
Bây giờ bạn có thể viết code Hướng Đối Tượng tốt hơn 🙂
Cảm ơn bạn đã đọc.