Encapsulation ในภาษา Ruby
ในบทนี้ เราจะพูดเกี่ยวกับ Encapsulation ในภาษา Ruby เราจะแนะนำให้คุณรู้จักว่า Encapsulation คืออะไร และมันถูกนำไปใช้อย่างไรในการเขียนโปรแกรม หลังจากนั้นเราจะพูดถึงคำสั่งที่ใช้สำหรับกำหนดการเข้าถึงของเมธอดในภาษา Ruby และการใช้งานเมธอด Setter และ Getter ซึ่งเป็นเมธอดที่ใช้สำหรับสร้างเมธอดเพื่อกำหนดค่าและรับค่าจากแอตทริบิวส์ของออบเจ็ค ก่อนที่เราจะเริ่มมาทำความรู้จักกับ Encapsulation กันก่อน
Encapsulation คืออะไร
การห่อหุ้มข้อมูล (Encapsulation) คือคุณสมบัติของการเขียนโปรแกรมเชิงวัตถุ (OOP) โดยมีแนวคิดที่ว่าข้อมูลจะผูกอยู่กับเมธอดที่จัดการข้อมูลนั้นๆ หรือเป็นการป้องกันการเข้าถึงข้อมูลหรือส่วนประกอบของออบเจ็คโดยตรงจากภายนอก ในภาษา Ruby แอตทริบิวต์ทั้งหมดนั้นจะถูกห่อหุ้มอยู่ภายในคลาส ในการเข้าถึงแอตทริบิวต์จากภายนอกคลาสนั้น จะต้องเข้าถึงผ่าน public เมธอดที่ถูกประกาศไว้ในคลาส ลองพิจารณาตัวอย่างต่อไปนี้
class Person
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def full_name
return @first_name + " " + @last_name
end
end
p = Person.new("Matteo", "Marcus")
puts p.full_name
ในตัวอย่าง เราได้สร้างคลาสที่ชื่อว่า Person
ที่มีสองแอตทริบิวส์คือ first_name
และ last_name
ที่ใช้สำหรับเก็บชื่อและนามสกุลตามลำดับ ภายในคลาสนั้นมีเมธอด initialize
ซึ่งเป็นเมธอดที่ใช้สำหรับกำหนดค่าให้กับออบเจ็คตอนที่มันถูสร้าง และเมธอด full_name
สำหรับรับชื่อแบบเต็มของออบเจ็ค
จากทั้งสองเมธอดที่เราได้ประกาศไปนั้น คุณคิดว่ามันเพียงพอแล้วหรือยัง แล้วถ้าเราต้องการเข้าถึงค่าแอตทริบิวต์เพื่อเปลี่ยนแปลงและอ่านค่าละ เราจะต้องทำอย่างไร ยกตัวอย่างเช่น
# Direct access to attributes
person.first_name = "Luciano"
puts person.first_name
จากสองคำสั่งด้านบน เราได้พยายามเข้าถึงแอตทริบิวส์ first_name
โดยตรงเพื่อกำหนดค่าและอ่านค่าให้กับแอตทริบิวส์ดังกล่าว ในภาษา Ruby เราไม่สามารถทำเช่นนี้ได้ เพื่อเข้าถึงแอตทริบิวส์ของออบจ็คจากภายนอกนั้น เราจะต้องทำมันผ่านเมธอดที่เราสร้างขึ้นมา นี่เป็นตัวอย่าง
class Person
# Setter methods
def first_name=(value)
@first_name = value
end
def last_name=(value)
@last_name = value
end
# Getter methods
def first_name
return @first_name
end
def last_name
return @last_name
end
def full_name
return @first_name + " " + @last_name
end
end
person = Person.new
person.first_name = "Matteo"
person.last_name = "Marcus"
puts "Full name: #{person.first_name} #{person.last_name}"
puts "Via full_name method: #{person.first_name} #{person.last_name}"
ในตัวอย่างต่อมา เนื่องจากเราต้องการเข้าถึงแต่ละแอตทริบิวส์เพื่อกำหนดค่าและอ่านค่า ดังนั้นเราจึงได้กำหนดเมธอดสำหรับกำหนดค่าและอ่านค่าให้กับแอตทริบิวส์ first_name
และ last_name
ในตัวอย่างนี้ เราได้นำเมธอด initialize
ออกไป เพราะเราจะใช้เมธอดใหม่ที่เราประกาศมาแทน
# Setter methods
def first_name=(value)
@first_name = value
end
def last_name=(value)
@last_name = value
end
# Getter methods
def first_name
return @first_name
end
def last_name
return @last_name
end
ภายในคลาสนั้นเราได้สร้างสี่เมธอดสำหรับกำหนดค่าและรับค่าจากแอตทริบิวส์ทั้งสอง โดยทั่วไปแล้วเมธอดเหล่านั้นเรียกว่า Setter และ Getter เมธอด และเมื่อเราประกาศเมธอดแล้ว เราสามารถใช้เมธอดเหล่านี้สำหรับกำหนดค่าและรับค่าจากแอตทริบิวส์เหมือนกับว่าเราเข้าผ่านแอตทริบิวส์โดยตรง
person = Person.new
person.first_name = "Matteo"
person.last_name = "Marcus"
puts "Full name: #{person.first_name} #{person.last_name}"
puts "Via full_name method: #{person.first_name} #{person.last_name}"
หลังจากนั้นเรานำคลาสมาสร้างออบเจ็ค และกำหนดค่าให้กับแอตทริบิวส์และอ่านค่าจากแอตทริบิวส์ด้วยเมธอด Setter และ Getter ที่เราได้สร้างขึ้นมา
Full name: Matteo Marcus
Via full_name method: Matteo Marcus
นี่เป็นผลลัพธ์การทำงานของโปรแกรม ในการใช้งานเมธอด Setter และ Getter สำหรับกำหนดค่าและอ่านข้อมูลจากแอตทริบิวส์ภายในออบเจ็คที่ละค่า และนอกจากนี้ เรายังคงใช้เมธอด full_name
สำหรับอ่านชื่อพร้อมกับนามสกุล
คำสั่งกำหนดการเข้าถึงในภาษา Ruby
ในการเขียนโปรแกรมในภาษา Ruby เราสามารถกำหนดการเข้าถึงให้กับเมธอดของคลาสได้ ซึ่งในภาษา Ruby นั้นมีคำสั่งสำหรับกำหนดการเข้าถึงอยู่สามคำสั่ง ดังที่แสดงในตารางด้านล่างนี้
คำสั่ง | คลาส | คลาสย่อย | ภายนอกคลาส |
---|---|---|---|
public | สามารถเข้าถึงได้ | สามารถเข้าถึงได้ | สามารถเข้าถึงได้ |
protected | สามารถเข้าถึงได้ | สามารถเข้าถึงได้ | ไม่สามารถเข้าถึงได้ |
private | สามารถเข้าถึงได้ | สามารถเข้าถึงได้ | ไม่สามารถเข้าถึงได้ |
จากตารางนั้นเป็นคำสั่งสำหรับกำหนดการเข้าถึงของเมธอด เราสามารถใช้คำสั่งเหล่านี้เพื่อกำหนดว่าเมธอดจะสามารถเข้าถึงได้อย่างไร ยกตัวอย่างเช่น public เมธอดจะสามารถเข้าถึงได้ทุกที่ ในขณะที่ private เมธอดจะสามารถเข้าถึงได้จากคลาสที่มันถูกประกาศเท่านั้น ต่อไปมาดูตัวอย่างการใช้งานในแต่ละคำสั่ง
คำสั่ง public และ private
คำสั่ง public และ private ใช้สำหรับกำหนดการเข้าถึงของเมธอดเช่นเดียวกัน แต่สิ่งที่แตกต่างกันคือ เมธอดที่สร้างจากคำสั่ง public จะสามารถเข้าถึงได้จากภายนอกของคลาส ในขณะที่เมธอดที่สร้างจากคำสั่ง private จะสามารถเข้าถึงได้จากคลาสที่ประกาศมันเท่านั้น โดยปกติแล้วเมธอดที่ประกาศในคลาสจะเป็น public ถ้าหากคุณไม่ได้ระบุคำสั่งใดเลย นอกจากเราจะกำหนดให้มันเป็นอย่างอื่น
ต่อไปมาดูตัวอย่างของคลาสของรถยนต์ ซึ่งคลาสนี้มีแอตทริบิวส์และเมธอดที่เป็นทั้งแบบ public และ private นี่เป็นโค้ดของโปรแกรม
class Car
def initialize(model, color, fuel, speed)
@model = model
@color = color
@fuel = fuel
@speed = speed
end
public
def console
puts "Model: #{@model}"
puts "Color: #{@color}"
puts "Fuel level: #{@fuel}"
puts "Current speed: #{@speed}"
end
def start
puts "Car has started"
end
def stop
puts "Car has stopped"
end
def drive
consume_fuel
push_engine
spin_wheel
puts "Car is running"
end
private
def consume_fuel
puts "Consuming fuel"
end
def push_engine
puts "Pushing engine to work"
end
def spin_wheel
puts "Wheel is spinning "
end
end
c = Car.new("Porsche", "Green", 100, 240)
c.start
c.drive
c.stop
c.console
ในตัวอย่าง เราได้สร้างคลาสที่มีชื่อว่า Car
คลาสนี้มีแอตทริบิวส์สำหรับเก็บค่าโมเดล สี ระดับน้ำมันเชื้อเพลิง และความเร็วของรถยนต์ ซึ่งแอตทริบิวส์เหล่านี้จะถูกกำหนดผ่านเมธอด initialize
ซึ่งเป็นคอนสตรัคเตอร์ของคลาส
public
def console
puts "Model: #{@model}"
puts "Color: #{@color}"
puts "Fuel level: #{@fuel}"
puts "Current speed: #{@speed}"
end
def start
puts "Car has started"
end
def stop
puts "Car has stopped"
end
def drive
consume_fuel
push_engine
spin_wheel
puts "Car is running"
end
หลังจากนั้นเป็นการประกาศ public เมธอด สังเกตว่าเราได้ใช้คำสั่ง public
ก่อนบรรทัดของการประกาศเมธอด console
นั่นหมายความว่าเมธอดที่ประกาศหลังจากคำสั่งดังกล่าวจะเป็น public เมธอด
ในตัวอย่าง เราได้ประกาศสี่เมธอด เมธอด console
ใช้สำหรับแสดงข้อมูลเกี่ยวกับรถยนต์ผ่านทางคอนโซลรถ เมธอด drive
ใช้สำหรับขับเพื่อให้รถยนต์เคลื่อนที่ไป ส่วนเมธอด start
และ stop
ใช้สำหรับสตาร์ทและดับเครื่องยนต์
private
def consume_fuel
puts "Consuming fuel"
end
def push_engine
puts "Pushing engine to work"
end
def spin_wheel
puts "Wheel is spinning "
end
ต่อมาเป็นการใช้คำสั่ง private
สำหรับประกาศเมธอดให้เป็น private เมธอดที่ถูกประกาศหลังจากคำสั่งนี้จะเป็น private เมธอด ซึ่งเป็นเมธอดที่สามารถเข้าถึงได้แค่ภายในคลาสที่ได้ประกาศมันขึ้นมา
def drive
consume_fuel
push_engine
spin_wheel
puts "Car is running"
end
เราได้เรียกใช้งาน private เมธอดจากเมธอด drive
ซึ่งอยู่ภายในคลาสเดียวกัน ในตัวอย่างแสดงให้เห็นว่า ในการที่รถยนต์เคลื่อนที่ เราจะต้องสั่งการโดยเรียกจากเมธอด drive
หลังจากนั้นการนำมันเชื้อเพลิงมาใช้ การทำใช้เครื่องยนต์ทำงาน และการทำให้ล้อหมุนจะถูกทำงานอัตโนมัติด้วย private เมธอด
c = Car.new("Porsche", "Green", 100, 240)
c.start
c.drive
c.stop
c.console
หลังจากนั้นเราได้นำคลาสมาสร้างออบเจ็ค และเรียกใช้งานเมธอดต่างๆ เพื่อให้รถยนต์นั้นทำงาน สังเกตว่าเมธอดทั้งที่เราเรียกนั้นเป็น public เมธอดทั้งหมด และเราไม่สามารถเรียกใช้งาน private เมธอดจากภายนอกของคลาสได้
Car has started
Consuming fuel
Pushing engine to work
Wheel is spinning
Car is running
Car has stopped
Model: Porsche
Color: Green
Fuel level: 100
Current speed: 240
นี่เป็นผลลัพธ์การทำงานของโปรแกรม ในตัวอย่างได้แสดงให้คุณเห็นถึงวิธีการใช้งานคำสั่ง public
และ private
สำหรับกำหนดการเข้าถึงของเมธอด
ตัวอย่างนี้แสดงให้เห็นว่า เมื่อคุณขับรถยนต์ คุณไม่จำเป็นต้องรู้ว่าเบื้องหลังมันทำงานอย่างไร ในการที่จะนำเชื้อเพลงมาใช้ การสั่งให้เครื่องยนต์ทำงาน หรือการสั่งให้ล้อหมุน ซึ่งสิ่งเหล่านี้เป็นสิ่งที่เป็นนามธรรมกับผู้ใช้งาน ทั้งหมดที่คุณต้องทำก็คือเหยียบคันเร่ง และการทำงานเหล่านี้จะเกิดขึ้นอัติโนมัติ ซึ่งเราเรียกว่าการปกปิดหรือห่อหุ้มการทำงานเอาไว้จากภายนอกนั่นเอง
คำสั่ง protected
คำสั่ง protected เป็นสั่งที่ใช้สำหรับกำหนดให้เมธอดสามารถเข้าถึงได้จากภายในคลาสของมันเอง และภายในคลาสที่สืบทอดไปเท่านั้น เราไม่สามารถเรียกเมธอดชนิดนี้ได้จากภายนอกของคลาส ต่อไปมาดูตัวอย่างการใช้งานคำสั่ง protected ในภาษา Ruby
class Rectangle
def initialize(width, height)
@width = width
@height = height
end
protected
def area
return @width * @height
end
end
class Cube < Rectangle
def initialize(width, height, dept)
super(width, height)
@dept = dept
end
def volume
return area * @dept
end
end
c = Cube.new(3, 4, 5)
puts c.volume
ในตัวอย่าง เราได้สร้างคลาส Rectangle
ซึ่งเป็นคลาสของพื้นที่สี่เหลี่ยม และคลาส Cube
ซึ่งเป็นคลาสของกล่องลูกบาศก์ เนื่องจากออบเจ็คทั้งสองประเภทนี้มีคุณสมบัติบางอย่างที่เหมือนกัน นั่นก็คือความยาวและความสูง ดังนั้นเราจึงได้สืบทอดคลาส Cube
มาจากคลาส Rectangle
เพื่อที่จะรับเอาคุณสมบัติดังกล่าวจากคลาสหลักของมัน
protected
def area
return @width * @height
end
ในคลาส Rectangle
เราได้ประกาศเมธอด area
ซึ่งเป็น protected เมธอด นั่นทำให้เมธอดนี้สามารถเข้าถึงได้ภายในคลาสที่ประกาศมัน และคลาสที่ได้สืบทอดจากคลาส Rectangle
ไป
def volume
return area * @dept
end
ในเมธอด volume
ของคลาส Cube
เราได้เรียกใช้เมธอด area
เพื่อคำนวณหาพื้นที่ในระนาบสองมิติซึ่งเป็นสิ่งที่กล่องลูกบาศก์ต้องมีอยู่แล้ว และสิ่งที่เพิ่มเติมเข้ามาก็คือความลึกของลูกบาศก์นั่นเอง
c = Cube.new(3, 4, 5)
puts c.volume
หลังจากนั้นเราได้นำคลาส Cube
มาสร้างออบเจ็คลูกบาศก์โดยการกำหนดความกว้าง ความสูง และความลึก และแสดงปริมาตรของลูกบาศก์ออกมาทางหน้าจอผ่าน public เมธอด volume
60
นี่เป็นผลลัพธ์การทำงานของโปรแกรมจากการใช้งานคำสั่ง protected
สำหรับกำหนดการเข้าถึงให้กับเมธอดในภาษา Ruby
และอย่างที่เราได้บอกไป protected เมธอดนั้นจะไม่สามารถเข้าถึงจากภายนอกคลาสได้ นั่นทำให้คำสั่งต่อไปนี้ไม่ทำงาน
r = Rectangle.new(2, 3)
puts r.area
c = Cube.new(3, 4, 5)
puts c.area
ในตัวอย่างจะเกิดข้อผิดพลาดขึ้น เนื่องจากเราได้เรียกใช้งานเมธอด area
ซึ่งเป็น protected เมธอดจากภายนอกของคลาส ถึงแม้ว่ามันเข้าท่าที่จะเรียกใช้งานเมธอด area
บนออบเจ็ค Rectangle
แต่เนื่องจากเมธอดดังกล่าวได้ถูกประกาศเป็น protected นั่นจึงทำให้เราไม่สามารถเข้าถึงได้ และเพื่อที่จะทำให้มันสามารถเข้าถึงได้ คุณต้องประกาศมันให้เป็น public เมธอดแทน
เมธอด Setter และ Getter ในภาษา Ruby
สำหรับเนื้อหาในส่วนสุดท้ายของบทนี้เราจะแนะนำให้คุณรู้จักกับเมธอด Setter และ Getter ในภาษา Ruby ซึ่งเมธอดเหล่าใช้เพื่อสร้างเมธอดสำหรับกำหนดค่าและรับค่าจากแอตทริบิวต์ของออบเจ็ค ซึ่งในภาษา Ruby นั้นมีสามเมธอดให้เราได้ใช้งาน
attr_accessor(:attr_name, ...)
attr_writer(:attr_name, ...)
attr_reader(:attr_name, ...)
ในตัวอย่างเป็นรูปแบบการใช้งานเมธอดเพื่อสร้างเมธอด Setter และ Getter ในภาษา Ruby และทั้งสามเมธอดนั้นรับค่าอาร์กิวเมนต์ตั้งแต่หนึ่งถึงหลายอาร์กิวเมนต์ ซึ่งเป็น Symbol ของแอตทริบิวต์ที่เราต้องการสร้างเมธอด Setter และ Getter ให้กับมัน
เมธอด attr_accessor
นั้นใช้สำหรับสร้างเมธอด Setter และ Getter ให้กับแอตทริบิวต์ และเมธอด attr_writer
นั้นใช้สำหรับสร้างเมธอด Setter เพียงอย่างเดียว ในขณะที่เมธอด attr_reader
นั้นใช้สำหรับสร้างเมธอด Getter เพียงอย่างเดียว ต่อไปมาดูตัวอย่างการใช้งานของเมธอดเหล่านี้
class Fruit
attr_accessor(:name)
attr_writer(:color)
attr_reader(:weight)
def initialize(name, color, weight)
@name = name
@color = color
@weight = weight
end
def info
puts "Name: #{@name}"
puts "Color: #{@color}"
puts "Weight: #{@weight} grams"
end
end
f1 = Fruit.new("Apple", "Red", 100)
f1.info
f1.name = "Orange"
f1.color = "Orange"
puts "New name: #{f1.name}"
f1.info
ในตัวอย่าง เราได้สร้างคลาส Fruit
ซึ่งเป็นคลาสของผลไม้ และในคอนสตรัคเตอร์ของคลาสนั้นมีการรับแอตทริบิวส์สามอย่างคือ ชื่อ สี และน้ำหนักของผลไม้
attr_accessor(:name)
attr_writer(:color)
attr_reader(:weight)
ภายในคลาส เราได้สร้างเมธอดสำหรับกำหนดค่าและรับค่าให้กับแอตทริบิวส์ โดยที่แอตทริบิวส์ name
จะมีเมธอดสำหรับกำหนดค่าและรับค่า แอตทริบิวส์ color
จะมีเมธอดสำหรับกำหนดค่าเพียงอย่างเดียว และสุดท้ายแอตทริบิวส์ weight
จะมีเมธอดสำหรับรับค่าเพียงอย่างเดียว
จากในตัวอย่างก่อนหน้านั้น ให้ผลลัพธ์การทำงานที่เหมือนกับการประกาศเมธอดในคลาสในตัวอย่างต่อไปนี้
class Fruit
def initialize(name, color, weight)
@name = name
@color = color
@weight = weight
end
# Equivalent to attr_accessor
def name=(value)
@name = value
end
def name
return @name
end
# Equivalent to attr_writer
def color=(value)
@color = value
end
# Equivalent to attr_reader
def weight
return @weight
end
end
และอย่างที่คุณเห็นทั้งเมธอด attr_accessor
, เมธอด attr_writer
และเมธอด attr_reader
นั้นเป็นเมธอดสำหรับอำนวยความสะดวกเพื่อให้เราสามารถสร้างเมธอด Setter และ Getter ได้อย่างรวดเร็วและสะดวกมากขึ้นโดยที่ไม่ต้องประกาศขึ้นมาเอง คุณแค่เรียกใช้เมธอดเหล่านี้ตามด้วยชื่อ Symbol ของแอตทริบิวส์ที่ต้องการ และ Ruby จะสร้างให้คุณในตอนที่โปรแกรมทำงาน
ในบทนี้ คุณได้เรียนรู้เกียวกับ Encapsulation ในภาษา Ruby ซึ่งเป็นคุณสมบัติการห่อหุ่มในการเขียนโปรแกรมเชิงวัตถุที่ให้เราสามารถกำหนดการเข้าถึงเมธอดในรูปแบบต่างๆ ได้ นี่เป็นแนวในการปกปิดข้อมูลและปกปิดการทำงานจากภายนอกของคลาสหรือออบเจ็ค