Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

อธิบายมรดกทับทิม – เรียนรู้ OOP วันนี้!

การสืบทอดคลาสเป็นคุณสมบัติ 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 vs size โดยที่ 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 หลักการถ้าไม่อยากยุ่ง

ตอนนี้คุณสามารถเขียนโค้ดเชิงวัตถุได้ดีขึ้น 🙂

ขอบคุณสำหรับการอ่าน