การสืบทอดคลาสเป็นคุณสมบัติ OOP พื้นฐาน (Object-Oriented Programming) ที่ช่วยให้คุณสร้างรุ่นที่เฉพาะเจาะจงและเฉพาะทางมากขึ้นของคลาสใดก็ได้
นี่คือตัวอย่าง :
Food -> Fruit -> Orange
มีความสัมพันธ์ระหว่างชั้นเรียนเหล่านี้!
เราสามารถพูดได้ว่าส้มเป็นผลไม้ แต่ผลไม้ก็เป็นอาหารเช่นกัน
คลาสหลัก (เรียกอีกอย่างว่า ซูเปอร์คลาส หรือ คลาสพื้นฐาน ) เป็นแบบทั่วไปมากกว่าคลาสย่อยเสมอ
Fruit
(ทั่วไปมากขึ้น) เป็นคลาสหลักของ Orange
(เจาะจงมากขึ้น)
ใน Ruby หน้าตาเป็นแบบนี้ :
class Food end class Fruit < Food end class Orange < Fruit end
ความหมายอย่างหนึ่งของมรดก ใน Ruby คือทุกวิธี &ทุกค่าคงที่ที่กำหนดไว้ใน Food
จะสามารถใช้ได้ใน Fruit
และบน Orange
.
เมธอดจะถูกส่งต่อจากคลาสพื้นฐาน แต่ไม่ใช่วิธีอื่น
จุดประสงค์ของการสืบทอดคืออะไร
คุณอาจคิดว่าการสร้างลำดับชั้นของออบเจ็กต์ให้องค์กร "รู้สึกดี" บางอย่างสำหรับโค้ดของคุณ แต่ยังมีอีกมากสำหรับโค้ดนี้
การสืบทอดถูกใช้เพื่อสร้างเวอร์ชันอื่นของคลาสพาเรนต์ซึ่งเหมาะสำหรับวัตถุประสงค์เฉพาะ อาจต้องใช้คุณลักษณะหรือวิธีการพิเศษบางอย่างที่ไม่สมเหตุสมผลในคลาสหลัก
นี่คือตัวอย่าง :
ผลไม้ทุกชนิดมีสี น้ำหนัก และชื่อ
ผลไม้บางชนิดอาจมีลักษณะพิเศษที่ไม่ใช้ร่วมกับผลไม้ชนิดอื่น ดังนั้น คุณจึงสร้างคลาสใหม่ที่สืบทอดคุณลักษณะของผลไม้ทั้งหมด (สี น้ำหนัก ฯลฯ) แล้วจึงเพิ่มคุณสมบัติพิเศษเข้าไป
นั่นคือสิ่งที่ฉันหมายถึงความเชี่ยวชาญ
ตัวอย่างอื่น :
คุณมีคลาสที่เขียนไปยังฐานข้อมูล แต่คุณต้องการเวอร์ชันอื่นของคลาส (อาจเป็นเพื่อจุดประสงค์ในการดีบัก) ซึ่งจะบันทึกการดำเนินการของฐานข้อมูลทั้งหมด
ในกรณีนี้คุณต้องการลวดลายมัณฑนากร
คุณสามารถอ่านคำอธิบายโดยละเอียดเกี่ยวกับบทความนี้ได้ แต่แนวคิดพื้นฐานคือคุณใช้การสืบทอดเพื่อรวมคลาสอื่น แล้วเพิ่มสิ่งใหม่ๆ เข้าไป
โดยไม่ต้องเปลี่ยนต้นฉบับ!
มรดกในโลกแห่งความจริง
ได้เลย
เราได้เรียนรู้เกี่ยวกับการสืบทอด แต่คุณรู้หรือไม่ว่าคุณกำลังใช้มันทุกวันในฐานะนักพัฒนา Ruby
ทับทิมเองก็ใช้มรดก เพื่อเปิดใช้งานวิธีการเช่น:
puts
class
super
เนื่องจากวัตถุ Ruby ทั้งหมดสืบทอดมาจาก Object
คลาสตามค่าเริ่มต้น
ดังนั้น ถ้าคุณสร้างคลาสแบบนี้ :
class Apple end
คลาสหลักคือ Object
:
Apple.superclass # Object
นั่นเป็นเหตุผลที่คุณสามารถใช้วิธีการต่างๆ ได้ดังที่กล่าวไว้ข้างต้น
ตัวอย่างเช่น เมื่อคุณเรียก puts
Ruby มองหาวิธีนี้ในชั้นเรียนของคุณ
แล้ว :
- ค้นหาเมธอดในคลาสพาเรนต์ตัวใดตัวหนึ่ง
- หากไม่พบ มันจะเริ่มต้นจากวัตถุอีกครั้ง &จะพยายามค้นหา
method_missing
- หากไม่พบ มันจะขึ้น
NoMethodError
, หรือNameError
ถ้าวิธีการถูกเรียกโดยไม่มีวัตถุที่ชัดเจน (a.size
vssize
โดยที่a
เป็นวัตถุที่ชัดเจน)
ดูตัวอย่างมรดกเพิ่มเติมได้ใน Rails
ตรงนี้ :
class ApplicationController < ActionController::Base end
ตัวควบคุม :
class SessionsController < ApplicationController end
รุ่น :
class Comment < ApplicationRecord belongs_to :article end
การสืบทอดมีอยู่ทั่วไปใน Ruby แต่บางครั้งก็ไม่ใช่วิธีแก้ปัญหาที่ถูกต้อง
องค์ประกอบ:ทางเลือกในการสืบทอด
การสืบทอดมีข้อ จำกัด บางประการ
ตัวอย่างเช่น :
คุณต้องการสร้างคอมพิวเตอร์จากชิ้นส่วนต่างๆ
เราว่าคอมพิวเตอร์มีส่วนประกอบ แต่แต่ละส่วนไม่ใช่คอมพิวเตอร์เอง
หากคุณแยกพวกมันออกจากกัน พวกมันจะทำหน้าที่ของมันไม่ได้
เราต้องการอย่างอื่น…
เราต้องการองค์ประกอบ!
องค์ประกอบสร้างคลาสที่ส่วนต่างๆ มารวมกันเพื่อทำหน้าที่
เหมือนกับคอมพิวเตอร์
นี่คือตัวอย่างการวางองค์ประกอบภาพจริง :
class Computer def initialize(memory, disk, cpu) @memory = memory @disk = disk @cpu = cpu end end
คอมพิวเตอร์ได้รับชิ้นส่วนที่จำเป็นในการทำงาน
นี่คือการเรียบเรียง
หลักการทดแทน Liskov
การสืบทอดจะมีประสิทธิภาพเมื่อใช้ในสถานการณ์ที่เหมาะสม
แต่ก็เหมือนกับเครื่องมืออื่นๆ ที่สามารถนำไปใช้ในทางที่ผิดได้!
อันที่จริง มีคำพูดยอดนิยมจากหนังสือ Design Patterns ดั้งเดิมที่มีลักษณะดังนี้:
“ชอบการจัดองค์ประกอบมากกว่าการสืบทอด”
รูปแบบการออกแบบ:องค์ประกอบของซอฟต์แวร์เชิงวัตถุที่นำกลับมาใช้ใหม่ได้
เพื่อให้แน่ใจว่าคุณกำลังใช้การสืบทอดอย่างถูกต้อง มีหลักการอยู่ข้อหนึ่งคือ L
จาก SOLID
ย่อมาจาก “หลักการทดแทน Liskov”
สิ่งนี้บอกว่าคลาสย่อยของคุณจะต้องสามารถใช้แทนคลาสพื้นฐานของคุณได้
กล่าวอีกนัยหนึ่ง :
หากคุณได้รับมรดกจาก Fruit
&color
เป็นสตริง คุณไม่ต้องการเปลี่ยน color
เพื่อส่งคืนสัญลักษณ์ในคลาสย่อย
ตัวอย่าง :
ผู้ใช้ Fruit
ขึ้นอยู่กับ color
ส่งคืนสตริง
class Fruit def color "orange" end end
สิ่งนี้ทำให้ LSP แตก :
class Orange < Fruit def color :orange end end
หากคุณเปลี่ยน color
เพื่อส่งคืนสัญลักษณ์ คุณจะไม่สามารถแทนที่ Fruit
วัตถุที่มี Orange
.
ทำไม?
เพราะถ้าเรียก method เช่น split
บนสัญลักษณ์ คุณจะได้รับข้อผิดพลาด
เป็นวิธีที่ไม่มีสัญลักษณ์
แต่เครื่องสายใช่
แฟล็กสีแดงอีกอันหนึ่งคือเมื่อคลาสย่อยของคุณไม่ใช่ความเชี่ยวชาญเฉพาะทางที่แท้จริงของคลาสพาเรนต์ และคุณกำลังใช้คลาสพาเรนต์เพื่อแชร์เมธอดยูทิลิตี้
สรุป
คุณได้เรียนรู้เกี่ยวกับมรดกและองค์ประกอบใน Ruby แล้ว!
คุณสามารถใช้มรดก เพื่อสร้างเวอร์ชันเฉพาะของคลาสพาเรนต์ และ องค์ประกอบ เพื่อรวมส่วนประกอบเข้าด้วยกัน อย่าลืมปฏิบัติตาม LSP
หลักการถ้าไม่อยากยุ่ง
ตอนนี้คุณสามารถเขียนโค้ดเชิงวัตถุได้ดีขึ้น 🙂
ขอบคุณสำหรับการอ่าน