Object Oriented ใน ruby

อัดรูปดิจิตอล ทำสมุดภาพของคุณเอง
รอรับได้. ท่องเที่ยว แต่งงาน ฯลฯ

www.tanabutr.co.th/photobook


รู้สึกว่าจะหลีกเลี่ยงที่จะไม่พูดถึง object oriented ไม่ได้ (ต้องพูด) เพราะใช้ ruby ไปแล้วมันต้องไปเกี่ยวข้องกับเรื่องนี้, ก็ต้องมีความรู้ปูพื้นเรื่องนี้ไว้ด้วย.

ทุกอย่างใน ruby เป็นอ็อบเ็จค (วัตถุ) หมด. อ็อบเจ็คหรืออินสแตนซ์ (instance) เกิดมาจากคลาส (class). ให้คิดว่าคลาสเป็นแม่พิมพ์ต้นแบบหรือเป็นประเภท (type) สำหรับแยกแยะวัตถุ (อ็อบเจค) ต่างๆ. คลาสหรือตัวแม่พิมพ์จะมีการกำหนดไว้ล่วงหน้าแล้วว่าอ็อบเจคที่จัดอยู่ในคลาสนั้นๆจะมีคุณสมบัติอะไร, ทำอะไรได้บ้าง. สมมติว่าเราตั้งคลาสที่ชื่อว่า Car. รถยนต์ก็ต้องมีล้อ, มีพวงมาลัย. นอกจากพวกส่วนประกอบแล้วก็ยังต้องมีกริยาหรือการกระทำ. เช่นรถยนต์ก็ต้องวิ่งได้. เวลาเขียนโปรแกรมพวกคุณสมบัติก็มักจะเก็บอยู่ในตัวแปร, ส่วนกริยาก็เป็นฟังก์ชัน. แต่ฟังก์ชันของคลาสเรามักจะเรียกว่าเมตธอต (method). การควบคุมอ็อบเจคนั้นมักจะเกิดจากการส่ง message ให้อ็อบเจคซึ่งก็คือการเรียกใช้เมตธอตนั่นเอง. เช่นมีตัวแปรชื่อ var เก็บอ็อบเจคของคลาส String. เราก็สามารถสั่งอ็อบเจคนั้นให้ทำงานต่างๆได้ถ้าเรารู้ว่าอ็อบเจคนั้นทำอะไรได้บ้าง. สมมติว่าเราต้องการสั่งให้ var มันแสดงจำนวนอักขระที่มีอยู่ในตัวมันก็ต้องรู้ว่าต้องใช้เมตธอต length. แล้วสั่ง

irb(main):001:0> var = "Hello world"
=> "Hello world"
irb(main):002:0> puts var.length
11
=> nil

การสั่งให้อ็อบเจคทำงานในภาษา ruby จะใช้เครื่องหมายจุด . แล้วตามด้วยชื่อเมตธอต.

ถ้าอยากรู้ว่าอ็อบเจคนั้นๆจัดอยู่ในคลาสอะไรก็ใช้เมตธอต class.

irb(main):007:0> var.class
=> String

ในแนวคิดเรื่อง object oriented จะมีแนวคิดเรื่องการสืบทอดหรือที่ภาษาอังกฤษเรียกว่า inheritant อยู่ด้วยคือคลาสต่างๆสามารถสืบทอดคุณสมบัติและการกระทำมาจากคลาสอื่นๆได้ด้วย. และีคลาสต้นตระกูลร่วมของคลาสทุกตัวได้แก่คลาสที่มีชื่อว่า Object. ในตัวอย่างข้างบน var เป็นตัวแปรที่เก็บอ็อบเจคของคลาส String ก็จริงแต่เมตธอต class ที่เรียกใช้นั้นสืบทอดมาจากคลาส Object ไม่ใช่เมตธอตที่กำหนดโดยคลาส String. เมตธอตของคลาส Object ที่น่าสนใจอีกตัวคือ to_s ย่อมาจาก to string ใช้แสดงข้อมูลของอ็อบเจคนั้นในรูปของสายอักขระ.

irb(main):017:0> 4
=> 4
irb(main):018:0> 4.class
=> Fixnum
irb(main):019:0> 4.to_s
=> "4"
irb(main):020:0> 4.to_s.class
=> String

จากตัวอย่างข้างบนจะเห็นว่าถ้ามีตัวเลขเช่น 4 ในโค้ด, ruby จะถือว่า 4 นี่คืออ็อบเจคซึ่งจัดอยู่ในคลาส Fixnum. ถ้าจะแปลงให้อยู่ในรูปของสายอักขระก็ใช้เมตธอต to_s. "4.to_s" นี่ถือว่าเป็นอ็อบเจคของคลาส String แล้วดังนั้นจะส่ง message ต่อไปอีกก็ได้เช่น "4.to_s.class" ก็จะได้ผลลัพธ์เป็น String.

ทีนี้เรามาดูเรื่องการสร้างอ็อบเจค. อ็อบเจคสร้างยังไง? มันขึ้นอยู่กับคลาส, แต่ก็จะมีรูปแบบเป็นเขียนชื่อคลาสแล้วตามด้วยอาร์กิวเมนต์. ตัวอย่างเช่นจะสร้างอินสแตนซ์ของคลาส String ก็ต้องกำหนดข้อมูลของอ็อบเจคนั้นๆ.

irb(main):023:0> String("Hello")
=> "Hello"
irb(main):024:0> String "Hello"
=> "Hello"
irb(main):025:0> "Hello"
=> "Hello"

อย่าลืมว่ามันละไม่เขียนวงเล็บก็ได้. และคลาส String นี่มันพิเศษ, เวลาสร้างอินสแตนซ์ไม่ต้องเขียนชื่อคลาสก็ได้. เขียนสายอักขระในเครื่องหมายคำพูดได้เลย, ถือว่าเป็นการสร้างอ็อบเจคของคลาส String.

ในตัวอย่างข้างบนสร้างอินสแตนซ์ของคลาส String แล้วแต่ไม่มีตัวแปรเก็บอินสแตนซ์นั้นไว้. อินสแตนซ์ที่สร้างมามันก็หายไป. มันอาจจะยังไม่หายไปทันทีหรือหายไปทันที, อันนี้ไม่รู้. แต่ที่แน่ๆคือมีสิ่งที่เรียกว่า garbage collector (GC) ตรวจดูอยู่ว่าถ้ามีอินสแตนซ์ที่ไม่ได้ใช้มันก็จะลบออกไปจากหน่วยความจำ. ตรงนี้แหละทำให้เราืทำงานได้สะดวกขึ้น, ไม่ต้องมาสนใจเรื่องการจัดการหน่วยความจำเพราะมี garbage collector จัดการให้.

ถ้าไม่ต้องการให้อินสแตนซ์ที่สร้างมาหายไปเฉยๆก็เอาไปเก็บในตัวแปรซะ. โดยตัวชื่อตัวแปรได้เลยแล้วใช้เครื่องหมายเท่ากับ = จัดเก็บอ็อบเจคนั้นๆ.

irb(main):026:0> var = "Hello"
=> "Hello"

สังเกตว่าตัวแปรเหมือนกับชื่อซึ่งตั้งให้กับอ็อบเจค. ถ้าคิดในแง่ของโลกจริงๆทุกอย่างมันมีชื่อเสมอ. ของที่ไม่มีชื่อก็คือยังไม่ได้ตั้งชื่อให้แต่มีตัวตน. ก็เหมือนกับเราสร้างอินสแตนซ์ขึ้นมาแล้วไม่เก็บใส่ตัวแปร, อินสแตนซ์นั้นมีตัวตนจริงแต่ไม่มีชื่อ, เลยอ้างอิงไม่ได้ (หรืออ้างอิงลำบาก). ตัวแปรใน ruby สามารถสร้างได้ทันทีเลย, ไม่ต้องประกาศเหมือนกับตัวแปรที่ใช้ใน Java, C, C++. นอกจากจะแตกต่างตรงที่ไม่ต้องประกาศชื่อตัวแปรก่อนแล้วยังไม่ต้องระบุประเภทของตัวแปรด้วย. คือไม่ต้องบอกก่อนว่าตัวแปรชื่อ var นี่เป็นตัวแปรสำหรับอ็อบเจคประเภท (type) อะไร. ดังนั้นถ้าอยู่ๆเห็น var เฉยๆเราก็ไม่รู้หรอกว่าตัวแปรนั้นเก็บอ็อบเจคประเภทอะไรอยู่, จึงต้องใช้เมตธอต class ช่วยจะได้รู้.

ข้อสังเกตถัดไป. ถ้าเรามีตัวแปรของคลาส String อยู่เฉยๆเราจะรู้ได้อย่างไรว่าตัวแปรนั้นมีข้อมูลหรือเปล่า? ตรงนี้สามารถใช้เมตธอตของคลาส Object หรือเมตธอตของคลาส String ช่วยในเรื่องนี้.

irb(main):029:0> var.empty?
=> false
irb(main):030:0> var.nil?
=> false

เมตธอต empty? ใช้สำหรับตรวจสอบดูว่าเนื้อหาของสายอักขระนั้นมันว่างเปล่าหรือไม่. ปรกติชื่อเมตธอตที่ใช้ตรวจสอบมักจะส่งค่า true หรือ false กลับมาและชื่อเมตธอตลงท้ายด้วยเครื่องหมายคำถาม ?. ดูตอนแรกอาจจะแปลกๆ. ส่วน nil? ตรวจสอบว่ามันไม่มีตัวตนใช่หรือไม่. มาดูตัวอย่างจะเข้าใจง่ายกว่า.

irb(main):039:0> var = ""
=> ""
irb(main):040:0> var.empty?
=> true
irb(main):041:0> var.nil?
=> false

จากตัวอย่าง var มันมีตัวตน, แต่ empty ไม่ได้หมายความว่ามัน nil (ไม่มีตัวตน).

ปัญหาต่อไปคือเราจะรู้ได้อย่างไรว่ามีคลาสอะไรให้ใช้บ้างและคลาสนั้นๆมีเมตธอตอะไร? อันนี้อาจจะต้องไปอ่านเอกสาร ruby manual ก่อนรอบหนึ่งก็จะรู้ว่ามันมีคลาสมาตรฐานอะไรบ้างเช่น Object, String, Fixnum, ฯลฯ. หรือใช้คำสั่ง ri. คำสั่ง ri คล้ายๆกับ man ที่คอยจะบอกว่ามีคลาสอะไรทำอะไรได้บ้าง.
ตัวอย่างการใช้เช่น

$ ri -l # แสดงคลาสทั้งหมด
OptionParser
OptionParser::accept
OptionParser::inc
...
$ ri Object#class
----------------------------------------------------------- Object#class
     obj.class    => class
------------------------------------------------------------------------
     Returns the class of _obj_, now preferred over +Object#type+, as an
     object's type in Ruby is only loosely tied to that object's class.
     This method must always be called with an explicit receiver, as
     +class+ is also a reserved word in Ruby.

        1.class      #=> Fixnum
        self.class   #=> Object

สรุป

  • คลาสเปรียบเสมือนแม่แบบ, ประเภทของวัตถุ.
  • อ็อบเจคจะมีประเภท (คลาส) เสมอ
  • คลาสที่สามารถสืบทอดคุณสมบัติกันได้และคลาสต้นตระกูลสูงสุดคือ Object.
  • ตัวแปรคือชื่อที่ตั้งให้อ็อบเจค
  • การสร้า้งอ็อบเจคหรืออินสแตนซ์ทำได้โดยการเขียนชื่อคลาสแล้วตามด้วยอาร์กิวเมนต์
  • ใช้คำสั่ง ri ดูรายชื่อคลาส, การใช้เมตธอต.

Comments: blogger