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

มัณฑนากร vs. คลาสย่อย

ในบทความล่าสุดของฉัน ฉันได้กล่าวถึงคุณสมบัติใหม่ที่ยอดเยี่ยมใน Rails 5.1 delegate_missing_to . ด้วย delegate_missing_to วิธีการใด ๆ ที่คุณไม่พบในวัตถุหนึ่งจะถูกเรียกบนวัตถุอื่นแทน :

class Player
  delegate_missing_to :@user

  def initalize(user)
    @user = user
  end

  def points
    Game.points_for_user(user.id)
  end
end

Player.new(user).name # calls user.name

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

ต้องมีเหตุผล delegate_missing_to ถูกเพิ่มแม้ว่า และสำหรับคุณสมบัติของ Rails คำขอดึงเป็นวิธีที่ยอดเยี่ยมในการค้นหาเหตุผลเหล่านั้น ในคำขอดึงนี้ DHH กล่าวถึงสาเหตุที่เขาแนะนำคุณลักษณะนี้:

นี่เป็นรูปแบบทั่วไปหากคุณต้องการสร้างมัณฑนากร:

ดูเหมือนจะเป็นจุดเริ่มต้นที่ดีทีเดียว

ทำไมต้องเป็นมัณฑนากร

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

class Player
  delegate_missing_to :@user

  def initalize(user)
    @user = user
  end

  def points
    Game.points_for_user(user.id)
  end
end

คุณจะพูดว่า “ผู้เล่น ตกแต่ง ผู้ใช้” เนื่องจากผู้เล่น เกือบ ทำหน้าที่เหมือน User แต่มีวิธีการพิเศษ points . และทำเช่นนี้โดยไม่มีการสืบทอด

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

คุณจะใช้มัณฑนากรเมื่อใด

มัณฑนากรอาจเป็นเพียงวิธีที่ซับซ้อนกว่าในการทำมรดก ฉันหมายถึง โค้ดสองบรรทัดนี้อันไหนดีกว่ากัน

player = Player.new(User.new(name: "Justin")) # Player decorates User
player = Player.new(name: "Justin")           # Player subclasses User

เห็นได้ชัดว่าที่สองใช่มั้ย? ที่นี่ การสร้าง Player เป็นมัณฑนากรแทนคลาสย่อยเป็นเพียงการเสียรหัส

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

user = User.find(1)

... some time later ...

player = Player.new(user)

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

วิธีนี้ช่วยให้คุณแยกพฤติกรรมออกเป็นคลาสต่างๆ ได้ แต่ละชั้นเรียนสามารถมุ่งเน้นไปที่วิธีการใช้วัตถุผู้ใช้ในสถานการณ์เฉพาะ – ผู้เล่น พนักงาน นักพัฒนา ด้วยมรดก สิ่งเหล่านี้จึงเป็นเรื่องง่ายสำหรับทุกสิ่งที่จะสับสน

คุณคริสกล่าวถึงประโยชน์อีกประการหนึ่งสำหรับนักตกแต่งในความคิดเห็น:

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

นักตกแต่งจะมีประโยชน์อย่างยิ่งเมื่อคุณแยกชั้นเรียนขนาดใหญ่ เมื่อใช้มัณฑนากร การปฏิบัติตามหลักการความรับผิดชอบเดียวจะง่ายกว่า – นักตกแต่งแต่ละคนสามารถทำสิ่งเดียวและทำได้ดี และคุณสามารถรวมนักตกแต่งเข้าด้วยกันเพื่อให้ได้พฤติกรรมที่ซับซ้อนมากขึ้น

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