หน้าเว็บ

วันอังคารที่ 24 สิงหาคม พ.ศ. 2553

การส่ง message ให้กับอ็อบเจ็ก



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

เช่น ถ้าเราเรียกเมธอด 100.to_s นั่นหมายความว่าีเรากำลังส่ง message (ที่เป็นเมธอดชื่อ) "to_s" ออกไปหา receiver ซึ่งในที่นี้ก็คือตัวเลข 100

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

การมองเมธอดให้เป็น message ที่ส่งไปหา receiver จะเป็นวิธีหนึ่งที่ช่วยเราแก้ปัญหานี้ได้ โดยเมธอดในภาษา Ruby ที่เกี่ยวข้ิองในการแก้ปัญหานี้ได้แก่ respond_to? และ send
ลองดูตัวอย่างต่อไปนี้นะครับ

เราจะทำอย่างไร ถ้าเราต้องการสั่งให้อ็อบเจกต์ Men ทำการกระโดด เดิน หรือ นั่ง ด้วยการป้อนการกระทำดังกล่าวผ่านลงไปทาง keyboard โดยกำหนดให้โปรแกรมของเรารับค่าสตริงที่ป้อนเข้ามาด้วยเมธอด gets จากนั้น ตัดอักขระ new line ด้วยเมธอด chomp หนึ่งที แล้วเก็บค่าที่ได้ลงใน ตัวแปร request
เราอาจใช้วิธีตรวจสอบว่า สตริงที่รับเข้ามามีค่าตรงกับชื่อของเมธอดที่มีหรือไม่ ถ้าใช่ก็ให้แสดงผลของเมธอดนั้นออกทางหน้าจอดังนี้
class Man
  def jump
    "I'm jumping"
  end
  def walk
    "I'm walking"
  end
  def sit
    "I'm sitting"
  end
end

guy = Man.new
puts "What you want me to do ?"

case gets.chomp
  when "jump"; puts guy.jump
  when "walk"; puts guy.walk
  when "sit"; puts guy.sit
  else puts "The man not understand this command"
end

จะเห็นว่าวิธีดังกล่าวอาจทำให้โค้ดของเรายาวมาก หากเราคลาส Men มีเมธอดมากกว่านี้
ทีนี้ ถ้าเรามองการเรียกเมธอด jump, walk หรือ sit เป็นการส่ง message ที่บอกถึง "การกระทำ(method)" ไปให้กับอ็อบเจกต์ดู เราจะเขียนโค้ดใหม่ได้ดังนี้
request = gets.chomp
if guy.respond_to?(request)
  puts guy.send(request)
else
  puts "The man not understand this command"
end

จากโค้ดข้างต้น จะเห็นว่ามีเมธอดที่มาช่วยเราแก้ปัญกาอยู่ 2 เมธอด คือ เมธอด respond_to? และ send
ในบรรทัดที่ 5 เมธอด respond_to? เป็นเมธอดของคลาส Object ทำหน้าที่ตรวจสอบว่า เราสามารถเรียกใช้เมธอดที่เป็นพารามิเตอร์ของมันอยู่จากคลาสนั้นๆ ได้หรือไม่ โดยจะคืนค่าจริงถ้าสามารถเรียกเมธอดจากคลาสนั้นได้ (แสดงว่ามีเมธอดอยู่จริง)
และหัวใจของโค้ดนี้ก็อยู่ที่เมธอด send ในบรรทัดที่ 6 นี่ละครับ โดยเมธอดนี้จะทำหน้าที่เหมือนกับเป็น dot operator เลย แต่แทนที่จะมองว่ามันจะ "เรียก" เมธอดออกมาใช้ตรงๆ คราวนี้เราจะมองว่ามัน "ส่ง" message ที่มีค่าเป็นชื่อเมธอด ไป "เรียก" เมธอดตัวนั้นออกมาใช้

อย่างไรก็ตาม มีข้อควรระวังในการใช้ send เหมือนกันครับ
ถึงแม้ว่าเมธอด send จะเป็นเมธอดของคลาส Object ที่เราแทบจะสามารถเรียกใช้มันได้จากทุกอ็อบเจกต์ (เพราะอ็อบเจกต์แทบทุกตัวล้วนสืบทอดมาจากคลาส Object) แต่ชื่อของมันก็อาจจะไปซ้ำกับเมธอดอื่นที่มีชื่อว่า "send" ได้เหมือนกัน ซึ่งน่าจะมีโอกาสซ้ำสูงซะด้วย เพราะเราอาจตั้งชื่อเมธอดในคลาสของเราว่า send แต่ใช้ส่งข้อมูลอย่างอื่นก็ได้
แต่ Ruby ก็มีมี alias สำหรับเมธอด send เอาไว้กันพลาดแล้ว ซึ่งก็คือเมธอดชื่อ "__send__" นั่นเอง
ดังนั้นหากจะนำหลักการส่ง message ไปยัง receiver มาใช้ก็ให้คิดถึง เมธอด __send__ เอาไว้แล้วกันครับ