ออบเจ็ค ในภาษา Ruby

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

  • Ruby กับออบเจ็ค
  • การสร้างออบเจ็ค
  • Ruby object hierarchy
  • Ruby top-level

Ruby กับออบเจ็ค

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

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

p 1
p 5.4
p "Ruby"
p true
p [1, 2, 3]

ในตัวอย่าง เป็นการแสดงข้อมูลประเภทต่างๆ ในภาษา Ruby ซึ่งถ้าหากคุณมาจากภาษา PHP หรือ Perl ข้อมูลเหล่านี้จะเป็นเพียง Literal ของตัวเลข สตริง และ boolean เท่านั้น แต่สำหรับในภาษา Ruby แล้ว Literal เหล่านี้เป็นออบเจ็ค

p 1.class
p 5.4.class
p "Ruby".class
p true.class
p [1, 2, 3].class

เนื่องจากมันเป็นออบเจ็ค ทำให้เราสามารถเรียกใช้งานเมธอดของมันได้ จากตัวอย่าง ทั้ง 1 หรือ Ruby นั้นต่างก็เป็นออบเจ็ค instance ซึ่งโดยทั่วไปแล้ว ทุกออบเจ็คจะถูกสร้างมาจากคลาสเสมอ เราเรียกใช้งานเมธอด class จากออกเจ็คโดยการใส่เครื่องหมาย .(จุด) ระหว่างออบเจ็คและชื่อของเมธอด ด้วยรูปแบบ: object.method

Integer
Float
String
TrueClass
Array

และนี่เป็นผลลัพธ์การทำงานของโปรแกรม เราเรียกใช้เมธอด class จากออบเจ็คประเภทต่างๆ เพื่อดูประเภทหรือคลาสของแต่ละออบเจ็ค

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

str = "Ruby"
p str.length
p str.downcase
p str.upcase
p str.reverse

num = 3
p num.to_f
p num.to_s
p num.even?
p num.odd?

ในตัวอย่าง เรามีตัวแปร str ซึ่งเป็นออบเจ็คจากคลาส String และเรียกใช้งานเมธอด length เพื่อหาความยาวของข้อความ เมธอด downcase เพื่อแปลงข้อความให้เป็นตัวพิมพ์เล็ก เมธอด upcase เพื่อแปลงข้อความให้เป็นตัวพิมพ์ใหญ่ และเมธอด reverse เพื่อแปลงข้อความย้อนกลับ

นอกจากนี้เรามีตัวแปร num ซึ่งเป็นออบเจ็คจากคลาส Integer เราใช้เมธอด to_i เพื่อแปลงตัวเลขเป็นตัวเลขทศนิยม เมธอด to_s เพื่อแปลงตัวเลขเป็นข้อความ เมธอด even? เพื่อตรวจสอบว่าตัวเลขเป็นจำนวนคู่หรือไม่ และเมธอด odd? เพื่อตรวจสอบว่าตัวเลขเป็นจำนวนคี่หรือไม่

String
4
"ruby"
"RUBY"
"ybuR"
Integer
3
"3"
false
true

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

จากตัวอย่างด้านบน เป็นการใช้งานเมธอดเพียงบางส่วนเท่านั้น คุณสามารถเรียกใช้เมธอด methods บนออบเจ็คใดๆ เพื่อดูรายชื่อเมธอดทั้งหมดที่สามารถใช้ได้บนออบเจ็ค ดังตัวอย่างต่อไปนี้

p 1.methods

จากตัวอย่าง เป็นการแสดงรายชื่อของเมธอดทั้งหมดที่สามารถใช้ได้กับข้อมูลประเภท Integer

อีกสิ่งหนึ่งที่น่าสนใจในภาษา Ruby คือการใช้งานตัวดำเนินการที่คุณอาจจะคุ้นเคยในภาษาอื่นๆ มาดูตัวอย่างต่อไปนี้

p 1 + 2 # 3
p 4 * 5 # 20
p 8 < 15 # true

ในตัวอย่าง เราได้ทำการบวกเลขด้วยตัวดำเนินการ + และคูณตัวเลขด้วยตัวดำเนินการ * และเปรียบเทียบว่าค่าทางซ้ายน้อยกว่าค่าทางขวาหรือไม่ด้วยตัวดำเนินการ <

แต่สำหรับในภาษา Ruby แล้วนั้น คุณเพิ่งจะเรียกใช้เมธอดจากออบเจ็คเหล่านั้น

p 1.+(2) # 3
p 4.*(5) # 20
p 8.<(10) # true

จากในตัวอย่างด้านบน นั่นแสดงให้เห็นว่าจริงๆ แล้วตัวดำเนินการทั้งหมดในภาษา Ruby นั้นเป็นเพียงเมธอดของออบเจ็ค ซึ่งคำสั่ง 1 + 2 และ 1.+(2) นั้นเป็นคำสั่งเดียวกัน มันคือการเรียกใช้เมธอด + ของออบเจ็ค 1 โดยมี 2 เป็นอาร์กิวเมนต์ของเมธอด

ในภาษา Ruby นั้นคุณสามารถละเว้นวงเล็บในการเรียกใช้งานเมธอดได้ และสำหรับเมธอดที่มีชื่อเป็นสัญลักษณ์อย่างเช่น + คุณสามารถละเว้นจุดหน้าเมธอดได้ จากที่โดยทั่วไปจะต้องเรียกใช้เมธอดในรูปแบบ: object.method(value)

puts 1
puts(1)

puts "Ruby", "PHP", "Python"
puts("Ruby", "PHP", "Python")

"marcuscode".upcase
"marcuscode".upcase()

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

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

class Integer

    def +(b)
        self * b
    end

end

p 2 + 3
p 2.+(3)

ในตัวอย่างด้านบน เราได้เปลี่ยนพฤติกรรมการทำงานของเมธอด + ในคลาส Integer ใหม่ โดยเปลี่ยนจากการบวกเป็นการคูณแทน

6
6

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

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

การสร้างออบเจ็ค

ในตัวอย่างก่อนหน้า เป็นการสร้างออบเจ็คแบบโดยปริยาย (implicit) โดยการกำหนดค่าด้วย Literal ให้กับตัวแปรโดยตรง ซึ่งภาษา Ruby จะแปลงข้อมูลเหล่านั้นเป็นออบเจ็คให้อัตโนมัติเบื้องหลัง นี่เป็นตัวอย่างการสร้างออบเจ็คแบบโดยปริยายหรือกำหนดค่าด้วย Literal

num = 5
lang = "Ruby"

ในทางกลับกัน เราอาจจะสร้างออบเจ็คแบบชัดเจน (explicit) เนื่องจากเรารู้ว่าทุกอย่างในภาษา Ruby นั้นเป็นออบเจ็ค และออบเจ็คนั้นถูกสร้างมากจากคลาส ดังนั้นเราจะใช้คลาสเพื่อสร้างออบเจ็ค

name = String.new "Mateo"
number = Array.new
number.push(1, 2, 3)

puts name
puts number

puts name.class
puts number.class

ในตัวอย่าง เป็นการสร้างออบเจ็คด้วยเมธอด new ซึ่งเป็นเมธอดของคลาส String และ Array แต่สำหรับคลาสบางประเภทอย่างเช่น Integer Float หรือ Symbol คุณไม่สามารถใช้เมธอดดังกล่าวได้ สิ่งที่คุณสามารถทำได้ก็คือการสร้างออบเจ็คด้วย Literal นั่นเอง

Mateo
1
2
3
String
Array

นี่เป็นผลลัพธ์การทำงานของโปรแกรมที่แสดงการสร้างออบเจ็คด้วยคลาส เราได้แสดงค่าของออบเจ็ค และคลาสของมัน

ในตัวอย่างก่อนหน้า ทั้งคลาส String และ Array นั้นเป็นคลาสที่มากับภาษา Ruby ที่เราสามารถนำมาใช้ได้เลย อย่างไรตาม คุณสามารถสร้างคลาสขึ้นมาเอง และนำมันมาสร้างออบเจ็คได้ มาดูตัวอย่างกัน

class Being
end

b = Being.new
puts b # <Being:0x0000000004ad2588>
puts b.object_id # 40013840

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

มาดูตัวอย่างเพิ่มเติมสำหรับการสร้างคลาสในภาษา Ruby

class Book

    def initialize(name, author, year)
        @name = name
        @author = author
        @year = year
    end

    def info
        puts "#{@name} by #{@author}, published in #{@year}"
    end

end

b1 = Book.new('C Programming', 'Dennis Ritchie', 1972)
b2 = Book.new('Python Programming', 'Guido van Rossum', 1990)
b3 = Book.new('Ruby Programming', 'Yukihiro Matsumoto', 1995)

puts "List of books"
puts b1.info
puts b2.info
puts b3.info

ในตัวอย่าง เราได้สร้างคลาสที่มีชื่อว่า Book ภายในคลาสนั้นมีเมธอดสองเมธอด เมธอดแรกคือเมธอด initialize เมธอดทำงานเป็นคอนสตรัคเตอร์ (construct) ที่จะถูกเรียกใช้เมื่อออบเจ็คถูกสร้าง โดยจะมีการรับพารามิเตอร์ที่ส่งเข้ามา

def initialize(name, author, year)
    @name = name
    @author = author
    @year = year
end

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

def info
    "#{@name} by #{@author}, published in #{@year}"
end

เราได้สร้างเมธอด info สำหรับแสดงรายละเอียดของหนังสือแต่ละเล่ม ซึ่งประกอบไปด้วยชื่อหนังสือ ชื่อผู้เขียน และปีที่หนังสือถูกเผยแพร่

List of books
C Programming by Dennis Ritchie, published in 1972
Python Programming by Guido van Rossum, published in 1990
Ruby Programming by Yukihiro Matsumoto, published in 1995

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

Ruby object hierarchy

ออบเจ็คทั้งหมดในภาษา Ruby นั้นมีรากฐานหรือถูกสืบทอดมาจากคลาส Object ซึ่งการสืบทอดนั้นเป็นคุณสมบัติของการเขียนโปรแกรมเชิงวัตถุ โดยที่คลาสหลัก (base class) สามารถสืบทอดคุณสมบัติต่างๆ ของมันไปยังคลาสลูก (delivered class)ได้

puts 1.inspect
puts 5.4.inspect
puts "marcuscode".inspect

puts 1.class
puts 5.4.class
puts "marcuscode".class

ในตัวอย่าง จะเห็นว่าคุณสามารถเรียกใช้งานเมธอด inspect และเมธอด class ที่เราได้สร้างขึ้นได้ ซึ่งความจริงแล้วเมธอดเหล่านี้ไม่ได้ถูกกำหนดในคลาส Integer, Float หรือ String แต่มันเป็นเมธอดจากคลาส Object ที่ได้รับการสืบทอดมานั่นเอง

1
5.4
"marcuscode"
Integer
Float
String

นี่เป็นผลลัพธ์การทำงานของโปรแกรม เราได้แสดงข้อมูลของออบเจ็คแต่ละประเภทด้วยเมธอด inspect และแสดงคลาสของออบเจ็คเหล่านี้

มาดูตัวอย่างเพิ่มเติมเกี่ยวกับการสืบทอดคลาส เพื่อแสดงให้เห็นว่าคลาสที่เราสร้างขึ้นมาเองก็ถูกสืบทอดมาจากคลาส Object อัตโนมัติด้วยเช่นกัน

class Point

    attr_accessor :x, :y

    def initialize(x, y)
        @x = x
        @y = y
    end

    def self.distance(a, b)
        Math.sqrt((b.x - a.x) ** 2 + (b.y - b.x) ** 2)
    end

end

p1 = Point.new(1, 3)
p2 = Point.new(5, 8)

puts p1.inspect
puts p2.inspect
puts Point.distance(p1, p2)

puts p1.instance_of? Point
puts p1.kind_of? Object

ในตัวอย่าง เราได้สร้างคลาส Point เพื่อเก็บพิกัดบนระนาบสองมิติ ภายในคลาสนั้นมีคลาสเมธอด distance ใช้สำหรับคำนวณหาระยะห่างระหว่างจุดสองจุด

puts p1.inspect
puts p2.inspect
puts Point2D.distance(p1, p2)

เราได้เรียกใช้เมธอด inspect บนออบเจ็ค p1 และ p2 ซึ่งเป็นเมธอดที่สืบทอดมาจากคลาส Object และแสดงระยะห่างระหว่างพิกัด 1, 3 และ 5, 8 บนระนาบสองมิติ

puts p1.instance_of? Point
puts p1.kind_of? Object

ต่อมาเป็นการเรียกใช้เมธอด instance_of? เพื่อตรวจสอบว่าออบเจ็ค p1 นั้นถูกสร้างมาจากคลาส Point หรือไม่ และเมธอด kind_of? เพื่อตรวจสอบว่าคลาสของออบเจ็คนั้นได้สืบทอดมาจากคลาส Object หรือไม่

#<Point2D:0x0000000004b6fbf8 @x=1, @y=3>
#<Point2D:0x0000000004b6fba8 @x=5, @y=8>
5.0
true
true

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

Ruby top-level

Ruby มีออบเจ็คหลักที่สามารถอ้างถึงได้คือ Ruby top-level มันเป็นส่วนพื้นที่การทำงานหลักของโปรแกรมที่อยู่นอกสุดและมีชื่อเรียกว่า main มันคือออบเจ็ค instance ที่สร้างมาจากคลาส Object ดังนั้นโค้ดที่เราเขียนขึ้นทั้งหมดไม่ว่าจะเป็นคลาส เมธอด และโมดูล จะอยู่ภายใน top-level จะอยู่ใน top-level ซึ่งเป็นสภาพแวดล้อมการทำงานหลักของ Ruby

a = 2
b = 5

puts local_variables

Kernel.puts self
puts self.class

ในตัวอย่างนั้นอธิบายการทำงานของ Ruby top-level

a = 2
b = 5

ในตอนแรก เราได้ประกาศตัวแปรสองตัวสำหรับเก็บค่าตัวเลข ซึ่งตัวแปรเหล่านี้มีขอบเขตอยู่ภายใน toplevel

puts local_variables

ต่อมาเราแสดงรายการของตัวแปร local ทั้งหมดออกมาด้วยเมธอด local_variables ซึ่งเป็นเมธอดที่อยู่ภายในโมดูล Kernel ที่สามารถใช้งานได้กับออบเจ็คทุกออบเจ็ค รวมทั้ง top-level เอง

Kernel.puts self

ตัวแปร self นั้นเป็นตัวแปรสำหรับอ้างถึงออบเจ็คปัจจุบัน ในที่นี้มันหมายถึง top-level เราแสดงค่ามันออกมาด้วยเมธอด putsจะเห็นว่า "name" นั้นเป็นชื่อของมัน

puts self.class

ต่อมาเราใช้เมธอด class เพื่อดูว่าคลาสของ top-level คืออะไร

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

a
b
main
Object

และนี่เป็นผลลัพธ์การทำงานของโปรแกรม ตัวแปร a และ b นั้นเป็นตัวแปร local ในขอบเขตของ top-level ส่วน main นั้นเป็นชื่อถูกกำหนดให้กับ top-level ในสภาพแวดล้อมการทำงานของมัน และสุดท้าย Object นั้นเป็นประเภทของ top-level

ต่อไปมาดูตัวอย่างเพิ่มเติมเกี่ยวกับ Ruby top-level

@name = "Mateo"
@height = 5.9

def info
    "#{@name} is #{@height} feets tall"
end

puts self.instance_variables
puts self.private_methods.include? :info

puts info

ในตัวอย่าง นั้นเป็นการประกาศตัวแปร instance และเมธอดที่อยู่ภายในขอบเขตของ top-level

@name = "Mateo"
@height = 5.9

เราได้ประกาศสองตัวแปร instance ซึ่งเป็นตัวแปรของ top-level และ instance เมธอดสำหรับแสดงค่าตัวแปรดังกล่าว

puts self.instance_variables

เราได้แสดงรายการของตัวแปร instance ภายใน top-level ด้วยเมธอด instance_variables

puts self.private_methods.include? :info

เมธอด private_methods ส่งค่ากลับเป็นอาเรย์ของ symbol ของ private เมธอดทั้งหมดใน top-level เนื่องจากคลาสออบเจ็คนั้นมีเมธอดเป็นจำนวนมาก เราจึงใช้เมธอด include? ของอาเรย์เพื่อตรวจสอบว่ามีเมธอด info อยู่หรือไม่

puts info

เราเรียกใช้เมธอด info เพื่อแสดงชื่อและความสูงที่ได้เก็บไว้ในตัวแปร instance

@name
@height
true
Mateo is 5.9 feets tall

นี่เป็นผลลัพธ์การทำงานของโปรแกรม เราได้แสดงตัวแปร instance ทั้งหมดออกมา และแสดงให้เห็นว่าเมธอด info นั้นเป็นเมธอดของ top-level ออบเจ็ค และสุดท้ายแสดงรายละเอียดด้วยเมธอดดังกล่าว

ในบทนี้ คุณได้ทำความเข้าในเกี่ยวกับออบเจ็คในภาษา Ruby อย่างไรก็ตาม คุณจะได้เรียนเกี่ยวกับคลาส ออบเจ็ค และคุณสมบัติอื่นๆ ของมันอีกครั้งในบทการเขียนโปรแกรมเชิงวัตถุในภาษา Ruby