1. เพกาซัส

มาดูหลักการข้อที่สามของOOPกัน: การสืบทอด นี่เป็นหัวข้อที่น่าสนใจมากที่คุณจะใช้บ่อยๆ สำหรับผู้ที่ไม่ได้ฝึกหัด การเขียนโปรแกรมนั้นแยกไม่ออกจากเวทมนตร์ เรามาเริ่มด้วยการเปรียบเทียบที่น่าสนใจ...;

สมมติว่าคุณเป็นพ่อมดที่ต้องการสร้างม้าบิน ในแง่หนึ่ง คุณสามารถลองเสกเพกาซัสได้ แต่เนื่องจากไม่มีเพกาซีในธรรมชาติ จึงเป็นเรื่องยากมาก คุณจะต้องทำอะไรมากมายด้วยตัวเอง มันง่ายกว่ามากที่จะจับม้าและเสกปีกของมัน

ในการเขียนโปรแกรม กระบวนการนี้เรียกว่า "การสืบทอด" สมมติว่าคุณต้องเขียนคลาสที่ซับซ้อนมาก ใช้เวลานานในการเขียนโค้ดตั้งแต่ต้น จากนั้นทดสอบทุกอย่างเป็นเวลานานเพื่อค้นหาข้อผิดพลาด ทำไมต้องไปทางยาก? เป็นการดีกว่าที่จะดูว่ามีคลาสดังกล่าวอยู่แล้วหรือไม่

สมมติว่าคุณพบคลาสที่มีเมธอดที่ใช้ 80% ของฟังก์ชันที่คุณต้องการ คุณจะทำอย่างไรกับมันต่อไป? คุณเพียงแค่คัดลอกรหัสไปยังชั้นเรียนของคุณ แต่วิธีนี้มีข้อเสียหลายประการ:

  1. คลาสที่คุณพบอาจถูกคอมไพล์เป็น bytecode แล้ว และคุณอาจไม่มีสิทธิ์เข้าถึงซอร์สโค้ด
  2. มีซอร์สโค้ดของชั้นเรียน แต่คุณทำงานให้กับบริษัทที่อาจถูกฟ้องเรียกค่าเสียหายสองสามพันล้านจากการใช้โค้ดของคนอื่นถึง 6 บรรทัด แล้วนายจ้างจะฟ้องคุณ
  3. การทำซ้ำรหัสจำนวนมากโดยไม่จำเป็น นอกจากนี้ หากผู้เขียนคลาสภายนอกพบจุดบกพร่องในนั้นและแก้ไข คุณจะยังคงมีจุดบกพร่องนั้นอยู่

มีวิธีแก้ปัญหาที่สวยงามกว่า และไม่จำเป็นต้องเข้าถึงโค้ดของคลาสดั้งเดิมอย่างถูกกฎหมาย ใน Java คุณสามารถประกาศคลาสนั้นเป็นพาเรนต์ของคลาสของคุณ ซึ่งจะเทียบเท่ากับการเพิ่มรหัสสำหรับคลาสนั้นในรหัสของคุณเอง ชั้นเรียนของคุณจะเห็นข้อมูลทั้งหมดและวิธีการทั้งหมดของชั้นเรียนหลัก ตัวอย่างเช่น คุณสามารถทำสิ่งนี้: เราสืบทอด "ม้า" แล้วเพิ่ม "ปีก" เพื่อรับ "เพกาซัส"


2. คลาสพื้นฐานทั่วไป

มรดกสามารถใช้เพื่อวัตถุประสงค์อื่นได้เช่นกัน สมมติว่าคุณมีสิบคลาสที่คล้ายกันมาก พวกเขามีข้อมูลและวิธีการเดียวกัน คุณสามารถสร้างคลาสพื้นฐานพิเศษ ย้ายข้อมูล (และเมธอดที่เกี่ยวข้อง) ไปยังคลาสพื้นฐานนี้ และประกาศคลาสทั้งสิบเหล่านั้นให้เป็นรุ่นลูกหลาน กล่าวอีกนัยหนึ่ง ในแต่ละคลาสระบุว่าคลาสพาเรนต์คือคลาสพื้นฐานนี้

เช่นเดียวกับที่ข้อดีของสิ่งที่เป็นนามธรรมถูกเปิดเผยด้วยการห่อหุ้มด้านข้างเท่านั้น ข้อดีของการถ่ายทอดทางพันธุกรรมก็ได้รับการปรับปรุงอย่างมากเช่นกันเมื่อใช้ความหลากหลาย แต่คุณจะได้เรียนรู้เกี่ยวกับสิ่งนั้นในภายหลัง วันนี้เราจะมาดูตัวอย่างของการใช้การสืบทอด

ตัวหมากรุก

สมมติว่าเรากำลังเขียนโปรแกรมเล่นหมากรุกกับผู้ใช้ที่เป็นมนุษย์ ดังนั้นเราต้องการคลาสเพื่อเป็นตัวแทนของชิ้นส่วน พวกเขาจะเรียนวิชาอะไร?

หากคุณเคยเล่นหมากรุก คำตอบที่ชัดเจนคือKing, Queen, Bishop, Knight, Rook และ Pawn

แต่ชั้นเรียนเองก็ยังจำเป็นต้องเก็บข้อมูลเกี่ยวกับแต่ละชิ้น ตัวอย่างเช่น พิกัด x และ y และมูลค่าของชิ้นส่วน ท้ายที่สุดแล้วของบางชิ้นก็มีค่ามากกว่าชิ้นอื่นๆ

นอกจากนี้ ชิ้นส่วนจะเคลื่อนที่แตกต่างกัน ซึ่งหมายความว่าคลาสจะใช้พฤติกรรมที่แตกต่างกัน นี่คือวิธีที่คุณสามารถกำหนดให้เป็นคลาส:

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
   }
}

นี่เป็นคำอธิบายดั้งเดิมของตัวหมากรุก

คลาสพื้นฐานทั่วไป

และนี่คือวิธีที่คุณสามารถใช้การสืบทอดเพื่อลดจำนวนโค้ด เราสามารถนำเมธอดและข้อมูลทั่วไปมาไว้ในคลาสทั่วไปได้ เราจะเรียกมันChessItemว่า ไม่มีประโยชน์ในการสร้างวัตถุของChessItem classเนื่องจากคลาสไม่สอดคล้องกับตัวหมากรุกใดๆ ที่กล่าวว่าชั้นเรียนจะพิสูจน์ได้ว่ามีประโยชน์มาก:

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
   }
}

นี่เป็นวิธีที่ยอดเยี่ยมในการลดความซับซ้อนของโค้ดสำหรับวัตถุที่คล้ายกัน ประโยชน์จะสังเกตเห็นได้ชัดเจนเป็นพิเศษเมื่อมีอ็อบเจกต์ต่างๆ นับพันรายการและคลาสหลายร้อยคลาสในโปรเจ็กต์ ดังนั้น คลาสพาเรนต์ (ฐาน) ที่เลือกอย่างเหมาะสมจึงไม่เพียงแต่ช่วยให้คุณลดความซับซ้อนของลอจิกได้มากเท่านั้น แต่ยังลดโค้ดเป็นสิบเท่าอีกด้วย


3. การสืบทอดคลาส —extends

ดังนั้นสิ่งที่ต้องทำเพื่อสืบทอดคลาส? สำหรับคลาสหนึ่งที่จะสืบทอดอีกคลาส คุณต้องเขียนextendsคีย์เวิร์ดหลังการประกาศคลาสลูก จากนั้นจึงเขียนชื่อคลาสพาเรนต์ มันมักจะมีลักษณะดังนี้:

class Descendant extends Parent

นี่คือสิ่งที่คุณต้องเขียนเมื่อประกาศคลาส Descendant อย่างไรก็ตาม คลาสสามารถสืบทอดได้เพียงคลาสเดียวเท่านั้น

ในภาพ เราเห็นว่าวัวสืบทอดหมู ไก่สืบทอดไข่ ผู้ปกครองเพียงคนเดียว! มรดกดังกล่าวไม่สมเหตุสมผลเสมอไป แต่เมื่อสิ่งที่คุณมีคือหมูและคุณต้องการวัวจริงๆ โปรแกรมเมอร์มักจะไม่สามารถต้านทานความต้องการที่จะสร้างวัวจากหมูได้

Java ไม่มีการสืบทอดหลายรายการ: คลาสไม่สามารถสืบทอดสองคลาสได้ แต่ละคลาสสามารถมีคลาสพาเรนต์ได้เพียงหนึ่งคลาสเท่านั้น หากไม่ได้ระบุคลาสพาเรนต์ คลาสพาเรนต์จะObjectเป็น

ที่กล่าวว่า Java มีการสืบทอดอินเทอร์เฟซหลายรายการ ซึ่งจะช่วยลดปัญหาได้เล็กน้อย เราจะพูดถึงอินเทอร์เฟซในภายหลัง แต่สำหรับตอนนี้ เรามาสำรวจการสืบทอดกันต่อไป