Kế thừa (Inheritance)

Bài viết được viết được viết trong chuyên mục OOP và có liên quan đến những kiến thức trong những bài cùng chuyên mục. Nếu các bạn lần đầu tìm hiểu về OOP có thể nên quay lại chuyên mục đọc từ những bài viết đầu. Vì mục đích nói về OOP cơ bản, không theo chuẩn mực của ngôn ngữ C++ nên ví dụ trong bài viết không đặt nặng vấn đề cú pháp chuẩn mực khi kế thừa và một số thể hiện của C++ trong kế thừa, mục đích chính mình muốn trình bày là kiến thức chung về kế thừa.

Khi các bạn sử dụng class của một người khác cho mục đích cá nhân (code reusing) có hai phương cách để làm điều này:

  • Composition
  • Inheritance

Composition có thể dịch thành “quan hệ” hoặc “liên kết”, tuy nhiên đừng quá quan tâm đến thuật ngữ, ta sẽ có thể hiểu composition là hành động kết hợp sử dụng code của người khác thông qua việc sử dụng các đối tượng của một lớp do người đó viết.

image

Lớp A do người khác viết, bạn sử dụng nó trong lớp Composition của bạn. Đây được gọi là Composition, khi bạn thực hiện composition, bạn chỉ có quyền thao tác với các thành viên/phương thức public của lớp mà bạn thực hiện composition. Xảy ra tình huống:

bạn muốn thay đổi định nghĩa của một số phương thức trong lớp A ? Câu trả lời là không thực hiện được.

vd: Bạn muốn thay đổi định nghĩa của hàm func() trong lớp A là return a*10.0; Đây là một hành động không được hỗ trợ.

Bên cạnh Composition có thể giúp bạn thực hiện sử dụng lại code, thì Inheritance (kế thừa) có thể khắc phục nhược điểm nêu trên. (được viết trong bài tiếp theo “Đa hình”)

image

Trước khi đọc phần nội dung, ta hãy bỏ qua cú pháp Inheritance : A vì lát nữa ta sẽ đi sâu tìm hiểu, hãy tập trung và các hành động trong hàm f()

Nhìn vào ví dụ, ta thấy trong lớp Inheritance không có thành viên nào có tên là a, nhưng trình biên dịch không hề báo lỗi.  Và bạn nghĩ rằng nếu có thể sử dụng biến a trong lớp A thì cũng có thể sử dụng func()? câu trả lời là không.

Để trả lời cho câu hỏi này ta sẽ xem lại phương thức truy nhập của func() là private và phương thức truy nhập của a là public. Từ đó tự rút ra một nhận xét ban đầu trước khi đi sâu tìm hiểu phần nội dung tiếp theo.

Một lớp kế thừa từ một lớp cha, sẽ được thừa hưởng tất cả các thành viên/phương thức “non-private “ của lớp cha. Lớp cha được gọi là base class (lớp cơ sở) và lớp kế thừa được gọi là devire class (lớp dẫn xuất)

image

Hãy nhìn vào phần mà chúng ta thực sự có được sau khi kế thừa, nó gồm những phần chúng ta định nghĩa cho B và phần non-private của lớp cơ sở, đó chính là lí do mà ta không thể sử dụng được func() trong ví dụ trên.

Ta không chỉ có phương thức truy nhập privatepublic. Ta có thêm protected.image

vậy protected khác public điểm nào?

image

image

Cú pháp

Trong C++, cú pháp về kế thừa khá đa dạng và nhiều cơ chế khá hay. Tuy nhiên căn bản nhất vẫn là cú pháp sau:

class Devired : Base{};. Dấu “:” thể hiện cho kế thừa.. Các bạn có thể thấy một số quy cách về cú pháp như sau, tùy vào mục đích sử dụng

class Devired : private/public/protected Base{};

Bài viết này mình không đi sâu vào từng ngôn ngữ nào chỉ sử dụng C++ để demo và ví dụ. Các bạn có thể tìm các tài liệu OOP chuyên sâu cụ thể ngôn ngữ C++ để tìm hiểu tốt hơn.

Is – a và Has a

Khi đọc tài liệu về OOP, người ta thường so sánh Mối quan hệ kế thừa là Is-a (inheriatance) và liên kết là has-a (Composition)
image

image

Khi nào thì sử dụng Composition, inheritance?

Nếu các bạn sử dụng composition, khi bạn chỉ cần sử dụng những chức năng mà lớp đó cung cấp mà không hề muốn tác động về mặt code để làm thay đổi lớp đó. Vì thế bạn chỉ thực hiện tác động với lớp đó thông qua một đối tượng cụ thể của lớp đó.

Nếu bạn sử dụng Inheritance, khi các bạn muốn tiếp cận và thao tác với các thành viên của lớp cha mà không cần thông qua một đối tượng (của lớp cha) cụ thể nào. Ngoài ra kế thừa là bước đầu tiên để ta thực hiện đa hình một tính chất mạnh mẽ và tiện dụng trong OOP.

Hãy nhìn qua hình ảnh để tổng hợp lại hai cơ chế.

image

image

This and Base

image

Ta có lớp Person có một String chứa tên, bạn muốn đặt tên thông qua phương thức setName nhưng tình huống xảy ra là đối số truyền vào có tên trùng với thành viên của lớp. Ta đơn giản giải quyết vấn đề bằng cách thay đổi đối số trong phương thức setName() khác đi.

Liệu vấn đề có thực sự được giải quyết, ta cùng nhìn thêm một tình huống như sau:

image

image

Trong C++, ta có thể sử dụng từ khóa This để thông qua đó truy cập đến các thàh viên ở lớp hiện tại (nếu vấn đề trùng tên xảy ra). Sử dụng Scope “: :” để thông qua đó truy cập đến các thành viên trong lớp cơ sở. (Trong Java sẽ khác, các bạn có thể đọc tại mục Java)

Vậy this là gì? Nhìn về mặt cú pháp các bạn có thể thấy this là một đối tượng con trỏ, trỏ đến một kiểu của lớp hiện tại. Nếu tìm hiểu sâu hơn nữa, con trỏ this có thể trỏ đến một kiểu cơ sở. (sẽ tìm hiểu ở “đa hình”)

One thought on “Kế thừa (Inheritance)

Leave a comment