หน้าเว็บ

วันพุธที่ 15 กันยายน พ.ศ. 2553

Singleton เมธอด

Singleton mehtod คือเมธอดที่เราสร้างขึ้นมาสำหรับ "อ็อบเจก" ตัวใดตัวหนึ่ง (individual object) เท่านั้น
หมายความว่า นอกจากอ็อบเจกจะมีความสามารถทำโน้นทำนี่ตามเมธอดต่างๆ ที่กำหนดเอาไว้ในคลาสแล้ว เราสามารถกำหนดแบบเจาะจง ให้อ็อบเจกตัวใดตัวหนึ่งมีเมธอดเพิ่มขึ้นมาจากเมธอดเดิมที่มีอยู่แล้วในคลาสได้ด้วย เมธอดที่เราเจาะจงให้กับอ็อบเจกต์นั้นแหละที่เรียกว่า singleton method



การกำหนด singleton method ให้กับอ็อบเจกต์ คล้ายกับการกำหนด instance method ภายในคลาส
จะต่างกันตรงที่ singleton method จะถูกกำนหนดอยู่ภายนอกคลาสของอ็อบเจกต์นั้นๆ โดยให้เราระบุชื่อของอ็อบเจกต์ไว้หน้าชื่อของ singleton method ดังตัวอย่างต่อไปนี้
class Farang
   def say
      "Hello"
   end
end

nicolas = Farang.new
puts "Nicolas say: " + nicolas.say

louis = Farang.new
def louis.say_thai
   "Sawatdee krub"
end

puts "Louis say: " + louis.say
puts "Louis say: " + louis.say_thai

ผลลัพธ์จากการรันโค้ดจะได้

Nicolas say: Hello
Louis say: Hello
Louis say: Sawatdee krub


จากโค้ดจะเห็นว่า singleton method ของเราก็คือเมธอด say_thai
อ็อบเจกต์ louis เท่านั้นสามารถจะใช้เมธอดนี้ได้ สังเกตุนิดนึงนะครับว่าผมเขียนเมธอดนี้เอาไว้ภายนอกคลาส Farang
ถ้าลองให้อ็อบเจกต์ nicolas เรียกใช้เมธอด say_thai ดูบ้างก็จะพบว่าเกิด error ขึ้น เพราะ say_thai เป็น singleton method (เอาไว้ใช้กับอ็อบเจกต์ที่กำหนดให้เท่านั้น) ไม่ใช้ instance เมธอดที่อยู่ภายในคลาส Farang
puts nicolas.say_thai  
# => undefined method `say_thai' for # (NoMethodError)

ถ้าดูให้ดี คุณอาจจะสงสัยเหมือนผมว่า เอ ถ้าเราจะสร้าง singleton method เนี่ย จู่ๆ ก็สามารถเขียน def แล้วตามด้วย "ชื่อของอ็อบเจกต์.ชื่อเมธอด" ไว้ในส่วนของ top level ได้เลยหรือ
แล้ว singleton method มีคลาสให้สังกัดหรือไม่ ถ้ามีแล้วคลาสนั้นอยู่ไหน เรียกใช้ได้หรือไม่

คำตอบคือ
ทุกๆ อ็อบเจกต์ของ Ruby นั้นจะมีคลาสที่มันสังกัดอยู่ 2 คลาสนะครับ (อ้าว ไม่ใช้คลาสแม่ของมันคลาสเดียวหรือ !?) นั่นคือ
1) คลาสแม่แท้ๆ ซึ่งตัวมันเป็น instance ของคลาสนั้น
2) คลาส singleton ของตัวมันเอง

อ่าฮ่า ทุกๆ อ็อบเจกตฺต่างก็มีคลาส singleton ของตัวมันนี่เอง
คลาส singleton ของอ็อบเจกต์ใดๆ จะถูกสร้างขึ้นโดยอัตโนมัติ และมันไม่มีชื่อคลาสของตัวเอง แต่ว่าเราสามารถกำหนดเมธอด หรือค่าคงที่ ลงไปใน singleton class ได้ ซึ่งลักษณะของการกำหนดนั้นทำได้เหมือนกับการสร้างคลาสทั่วๆไปได้ โดยใช้คีย์เวิร์ด class แล้วตามด้วยเครื่องหมาย "<< ชื่อของอ็อบเจกต์"

จากโค้ดตัวอย่างที่ผ่านมา
หากกำหนด singleton method ในลักษณะของการเขียนแบบ singleton class แล้ว โค้ดของเราจะมีหน้าตาแบบนี้ครับ
louis = Farang.new 
# => or you can use louis = Object.new
class << louis
  def say_thai
    "Sawatdee krub"
  end
end 

จะเห็นว่าการใช้คีย์เวิร์ด class แล้วตามด้วย "<< ชื่ออ็อบเจกต์" นั้นหมายถึงการเปิดคลาส singleton ของอ็อบเจกต์ตัวนั้นขึ้นมา ซึ่งทำให้เราสามารถกำหนดเมธอดภายใน singleton class นั้นได้ ซึ่งแน่นอนว่า เมธอดที่กำหนดขึ้นมานั้นจะกลายเป็น singleton method และมีไว้ใช้สำหรับอ็อบเจกต์ตัวนั้นตัวเดียว

ลองมาดูกันต่ออีกหน่อยดีมั้ยครับ

ถ้าเกิดอ็อบเจกต์ของคลาส Farang (ผมหมายถึง 'ฝรั่ง' น่ะครับ) มีหลายคนที่ดันพูดไทยได้ การกำหนด singleton method อย่างที่ผ่านมาอาจจะไม่เหมาะ
แต่จะดูดีกว่าถ้าเราใช้ module เข้ามาช่วย
ดังนั้นโค้ดใหม่ของเราจะเป็นดังนี้ครับ
louis = Farang.new
christien = Farang.new

module ThaiSpeakable
  def say_thai
    "Sawatdee krub"
  end
end

class << louis
  include ThaiSpeakable
end

class << christien
  include ThaiSpeakable
end

puts "Louis say: " + louis.say_thai
puts "Christien say: " + christien.say_thai

ผลลัพธ์จากการรันโค้ดจะได้
Louis say: Sawatdee krub
Christien say: Sawatdee krub

เราสามารถเช็คว่าอ็อบเจกต์ตัวหนึ่งมี singleton method ใดบ้าง ได้จากเมธอด "singleton_methods" ของคลาส Kernel
เมื่อลองเช็คอ็อบเจกต์ ของฝรั่งทั้งสามคนนี้ดูก็จะได้ผลลัพธ์ดังนี้
louis.singleton_method      # => say_thai
christien.singleton_method  # => say_thai
nicolas.singleton_method    # => nil
ความสัมพันธ์ลับๆ ที่ไม่ลับ ของ class method กับ singleton method
เนื่องจากคลาสทุกๆ คลาสใน Ruby เป็น instance ของคลาส Class (ไม่เชื่อลอง String.class ดู จะได้ผลลัพธ์เป็น "Class") และ singleton เมธอด ก็คือเมธอดที่ใช้ได้เฉพาะอ็อบเจกต์ตัวใดตัวหนึ่ง ดังนั้นถ้าลองมองในแง่ของ singleton เมธอดแล้วย้อนถามว่า singleton เมธอดของอ็อบเจกต์ของคลาส Class คืออะไร มีหน้าแบบไหน

ตอบตาม concept ของ singleton ก็ต้องบอกว่า
singleton เมธอด ของ อ็อบเจกต์ของคลาส Class (ซึ่งมันคือคลาสใดๆ) ก็คือเมธอดที่สามารถเรียกได้จากอ็อบเจกต์ของคลาส Class เท่านั้น ซึ่งมันก็คือ class method ดีๆ นี่เอง !!!

ทีนี้ ถ้าเรามองคลาส Farang ในแง่ที่ว่าตัวคลาส Farang เองเป็นอ็อบเจกต์ของคลาส Class ดังนั้นตามหลักการแล้ว เราจึงสามารถกำหนดเมธอด singleton ให้กับคลาสอ็อบเจกต์ Farang ได้เช่นกัน
อย่างงั้น ผมขอลองสร้างเมธอด singleton ให้กับคลาสอ็อบเจกต์ Farang โดยจะใช้หลักการกำหนด singleton method แบบเดียวกับโค้ดแรกที่เคยแสดงให้ดู นั่นคือ ใส่ชื่อของเมธอด singleton ที่ต้องการตามหลังชื่อของอ็อบเจกต์ (ในที่นี้คือชื่อคลาส Farang ... ย้ำ ... ตอนนี้ให้มองว่า คลาส Farang เป็นอ็อบเจกต์ของคลาส Class)

ผมจะเขียนโค้ดได้ดังนี้
# Singleton method as Class method
def Farang.from_country
  %w(England UnitedState France)
end
puts Farang.from_country

ผลลัพธ์ของการรันโค้ดข้างต้น
England
United State
France

ใครเห็นการกำหนดเมธอด from_contry จากโค้ดข้างต้นก็จะบอกได้ทันทีเลยว่า นี่มันเป็นการกำหนด "Class method" ที่เราคุ้นเคยกันดี

ครับ ผมกำลังจะบอกว่า Class method ที่เรารู้จักนั้น อีกนัยหนึ่งมันก็คือ singleton method ของคลาสใดๆ ที่ถูกมองอยู่ในรูปของอ็อบเจกต์ของคลาส Class นั่นเอง

3 ความคิดเห็น:

  1. อยากรู้จักคุณเข้าของบล็อกมากๆครับ
    คือผมสนใจเขียนภาษา ruby มากๆครับ
    แอด msn ผมหน่อยนะ
    i-panat@hotmail.com

    ตอบลบ
  2. แอดให้แล้วนะครับ

    แต่ช่วงหลังผมไม่ค่อยได้ออนเอ็มน่ะ ยังไงถ้ามีคำถามหรือคำแนะนำก็เมลหาผมได้เลยครับ worrawutp@gmail.com

    ตอบลบ