1. Phi Mã

Chúng ta hãy xem xét sâu hơn về nguyên tắc thứ ba của OOP : tính kế thừa . Đây là một chủ đề rất thú vị mà bạn sẽ sử dụng thường xuyên. Đối với những người không quen biết, lập trình không thể phân biệt được với ma thuật. Vì vậy, hãy bắt đầu với một phép loại suy thú vị...;

Giả sử bạn là một phù thủy muốn tạo ra một con ngựa biết bay. Một mặt, bạn có thể thử tạo ra một chú thiên mã. Nhưng vì pegasus không tồn tại trong tự nhiên nên điều này sẽ rất khó khăn. Bạn sẽ phải tự mình làm rất nhiều. Bắt một con ngựa và gọi đôi cánh của nó dễ dàng hơn nhiều.

Trong lập trình, quá trình này được gọi là "kế thừa". Giả sử bạn cần viết một lớp rất phức tạp. Phải mất nhiều thời gian để viết mã từ đầu và sau đó kiểm tra mọi thứ trong một thời gian dài để tìm lỗi. Tại sao phải đi con đường khó khăn? Tốt hơn là xem liệu một lớp như vậy đã tồn tại chưa.

Giả sử bạn tìm thấy một lớp có các phương thức thực hiện 80% chức năng mà bạn cần. Bạn sẽ làm gì với nó tiếp theo? Bạn chỉ có thể sao chép mã của nó vào lớp học của mình. Nhưng giải pháp này có một số nhược điểm:

  1. Lớp bạn tìm thấy có thể đã được biên dịch thành mã byte và bạn có thể không có quyền truy cập vào mã nguồn của lớp đó.
  2. Mã nguồn của lớp có sẵn, nhưng bạn làm việc cho một công ty có thể bị kiện vài tỷ vì sử dụng dù chỉ 6 dòng mã của người khác. Và sau đó chủ nhân của bạn sẽ kiện bạn.
  3. Sao chép một lượng lớn mã không cần thiết. Ngoài ra, nếu tác giả của một lớp bên ngoài tìm thấy một lỗi trong đó và sửa lỗi đó, thì bạn vẫn sẽ gặp lỗi đó.

Có một giải pháp tinh tế hơn và nó không yêu cầu quyền truy cập hợp pháp vào mã của lớp ban đầu. Trong Java, bạn chỉ cần khai báo lớp đó là lớp cha của lớp bạn. Điều đó sẽ tương đương với việc thêm mã cho lớp đó vào mã của riêng bạn. Lớp của bạn sẽ thấy tất cả dữ liệu và tất cả các phương thức của lớp cha. Ví dụ: bạn có thể làm thế này: chúng ta kế thừa "ngựa" rồi thêm "đôi cánh" để được "pegasus"


2. Lớp cơ sở chung

Kế thừa cũng có thể được sử dụng cho các mục đích khác. Giả sử bạn có mười lớp rất giống nhau. Họ có cùng dữ liệu và phương pháp. Bạn có thể tạo một lớp cơ sở đặc biệt, di chuyển dữ liệu (và các phương thức liên quan) vào lớp cơ sở này và khai báo mười lớp đó là lớp con. Nói cách khác, trong mỗi lớp chỉ ra rằng lớp cha của nó chính là lớp cơ sở này.

Giống như những lợi thế của sự trừu tượng hóa chỉ được bộc lộ dọc theo sự đóng gói bên cạnh, thì những lợi thế của tính kế thừa cũng được tăng cường nhiều khi sử dụng tính đa hình. Nhưng bạn sẽ tìm hiểu về điều đó một lát sau. Hôm nay chúng ta sẽ xem xét một số ví dụ về việc sử dụng thừa kế.

Quân cờ

Giả sử chúng ta đang viết một chương trình chơi cờ vua với một người dùng là con người. Theo đó, chúng ta cần các lớp để đại diện cho các phần. Họ sẽ là những lớp học nào?

Nếu bạn đã từng chơi cờ vua, câu trả lời rõ ràng là Vua, Hậu, Tượng, Mã, Xe và Tốt.

Nhưng bản thân các lớp vẫn cần lưu trữ thông tin về từng phần. Ví dụ: tọa độ x và y và giá trị của mảnh ghép. Rốt cuộc, một số mảnh có giá trị hơn những mảnh khác.

Ngoài ra, các mảnh di chuyển khác nhau, có nghĩa là các lớp sẽ thực hiện các hành vi khác nhau. Đây là cách bạn có thể định nghĩa chúng là các lớp:

class King
{
   int x;
   int y;
   int worth;

   void kingMove()
   {
     // Code that decides
     // how to move
     // the king
   }
}
class Queen
{
   int x;
   int y;
   int worth;

   void queenMove()
   {
     // Code that decides
     // how to move
     // the queen
   }
}
class Rook
{
   int x;
   int y;
   int worth;

   void rookMove()
   {
     // Code that decides
     // how to move
     // the rook
   }
}
class Knight
{
   int x;
   int y;
   int worth;

   void knightMove()
   {
     // Code that decides
     // how to move
     // the knight
   }
}
class Bishop
{
   int x;
   int y;
   int worth;

   void bishopMove()
   {
     // Code that decides
     // how to move
     // the bishop
   }
}
class Pawn
{
   int x;
   int y;
   int worth;

   void pawnMove()
   {
     // Code that decides
     // how to move
     // the pawn
   }
}

Đây là một mô tả rất sơ khai về các quân cờ.

Lớp cơ sở chung

Và đây là cách bạn có thể sử dụng tính kế thừa để giảm số lượng mã. Chúng ta có thể đưa các phương thức và dữ liệu chung vào một lớp chung. Chúng tôi sẽ gọi nó ChessItem. Không có điểm nào trong việc tạo các đối tượng của lớp ChessItem class, vì lớp không tương ứng với bất kỳ quân cờ nào . Điều đó nói rằng, lớp học sẽ tỏ ra rất hữu ích:

class King extends ChessItem
{
   void kingMove()
   {
     // Code that decides
     // how to move the king
   }
}
class Queen extends ChessItem
{
   void queenMove()
   {
     // Code that decides
     // how to move the queen
   }
}
class Rook extends ChessItem
{
   void rookMove()
   {
     // Code that decides
     // how to move the rook
   }
}
class ChessItem
{
   int x;
   int y;
   int worth;
}
class Knight extends ChessItem
{
   void knightMove()
   {
     // Code that decides
     // how to move the knight
   }
}
class Bishop extends ChessItem
{
   void bishopMove()
   {
     // Code that decides
     // how to move the bishop
   }
}
class Pawn extends ChessItem
{
   void pawnMove()
   {
     // Code that decides
     // how to move the pawn
   }
}

Đây là một cách tuyệt vời để đơn giản hóa mã cho các đối tượng tương tự. Những lợi ích đặc biệt đáng chú ý khi có hàng nghìn đối tượng khác nhau và hàng trăm lớp trong dự án. Vì vậy, các lớp cha (cơ sở) được chọn đúng cách cho phép bạn không chỉ đơn giản hóa đáng kể logic mà còn giảm mã gấp mười lần.


3. Kế thừa lớp —extends

Vì vậy, những gì nó cần để kế thừa một lớp? Để một lớp có thể kế thừa một lớp khác, bạn cần viết extendstừ khóa sau phần khai báo lớp con rồi viết tên của lớp cha. Nó thường trông giống như thế này:

class Descendant extends Parent

Đây là những gì bạn cần viết khi khai báo lớp Descendant. Nhân tiện, một lớp chỉ có thể kế thừa một lớp.

Trong hình, chúng ta thấy con bò thừa kế con lợn, con gà thừa kế con gà, con này thừa hưởng quả trứng. Chỉ có một phụ huynh! Sự kế thừa như vậy không phải lúc nào cũng hợp lý. Nhưng khi tất cả những gì bạn có là một con lợn và bạn thực sự cần một con bò, các lập trình viên thường không thể cưỡng lại ý muốn tạo ra một con bò từ một con lợn.

Java không có đa kế thừa: một lớp không thể kế thừa hai lớp. Mỗi lớp chỉ có thể có một lớp cha. Nếu không có lớp cha nào được chỉ định, thì lớp cha là Object.

Điều đó nói rằng, Java có nhiều giao diện kế thừa. Điều này giảm nhẹ vấn đề một chút. Chúng ta sẽ nói về giao diện sau một chút, nhưng bây giờ hãy tiếp tục khám phá tính kế thừa.