ถ้าไม่คุ้นเคย term นี้ refactoring คือการปรับปรุงคุณภาพโค้ดโดยไม่เปลี่ยน behavior ทำให้โค้ดง่ายต่อการทำงานมากขึ้น
ในโพสต์นี้จะเรียนรู้ Ruby refactoring techniques พื้นฐาน มาเริ่มกัน!
Extract Method
Refactoring พบบ่อยที่สุดคือ 'extract method' ย้ายโค้ดจากเมธอดเก่าไปเมธอดใหม่ ทำให้ได้เมธอดเล็กๆ ชื่อ descriptive
ตัวอย่าง:
@sold_items = %w( onions garlic potatoes )
def print_report
puts "*** Sales Report for #{Time.new.strftime("%d/%m/%Y")} ***"
@sold_items.each { |i| puts i }
puts "*** End of Sales Report ***"
end
เริ่ม extract ส่วน醜ที่สุด: current date generation
def print_report
puts "*** Sales Report for #{current_date} ***"
@sold_items.each { |i| puts i }
puts "*** End of Sales Report ***"
end
def current_date
Time.new.strftime("%d/%m/%Y")
end
อ่านง่ายขึ้นแล้ว แต่ไปต่อได้ extract เพิ่มจนได้โค้ดนี้:
def print_report
print_header
print_items
print_footer
end
def print_header
puts "*** Sales Report for #{current_date} ***"
end
def current_date
Time.new.strftime("%d/%m/%Y")
end
def print_items
@sold_items.each { |i| puts i }
end
def print_footer
puts "*** End of Sales Report ***"
end
โค้ดยาวขึ้น แต่ไม่อ่านง่ายกว่านี้? อย่ากลัวเมธอดเล็กๆ มันดีต่อโค้ด
Refactoring Conditionals
Refactor conditionals ซับซ้อนเป็นเมธอดอ่านง่ายขึ้น
ตัวอย่าง:
def check_temperature
if temperature > 30 && (Time.now.hour >= 9 && Time.now.hour <= 17)
air_conditioner.enable!
end
end
ส่วนที่ 2 ของ if ไม่อ่านง่าย extract เป็นเมธอด:
def check_temperature
if temperature > 30 && working_hours
air_conditioner.enable!
end
end
def working_hours
Time.now.hour >= 9 && Time.now.hour <= 17
end
เราตั้งชื่อ descriptive ให้ condition ทำให้อ่านง่ายสำหรับผู้อ่านอนาคต (รวมตัวเอง!)
Replace Method with Method Object
บางครั้งมีเมธอดใหญ่ out of control ยาก refactor เพราะ big methods tend to มี local variables เยอะ วิธีหนึ่ง: 'Method Object' refactoring
"Big methods are where classes go to hide." - Uncle Bob
ตัวอย่าง:
require 'socket'
class MailSender
def initialize
@sent_messages = []
end
def send_message(msg, recipient = "rubyguides.com")
raise ArgumentError, "message too small" if msg.size < 5
formatted_msg = "[New Message] #{msg}"
TCPSocket.open(recipient, 80) do |socket|
socket.write(formatted_msg)
end
@sent_messages << [msg, recipient]
puts "Message sent."
end
end
sender = MailSender.new
sender.send_message("testing")
Refactor: สร้าง class ใหม่ promote local variables เป็น instance variables จะ refactor ต่อได้โดยไม่ต้องส่ง data ไปมาระหว่างกัน
Hey! อยากพัฒนา Ruby skills ให้ก้าวกระโดด? เช็ค Beautiful Ruby Course ของผม 🙂
MailSender class หลัง refactor:
class MailSender
def initialize
@sent_messages = []
end
def deliver_message(message)
send(message)
@sent_messages << message
puts "Message sent."
end
def send(msg)
TCPSocket.open(msg.recipient, 80) { |socket| socket.write(msg.formatted_msg) }
end
end
Class ใหม่ที่ introduce:
class Message
attr_reader :msg, :recipient
def initialize(msg, recipient = "rubyguides.com")
raise ArgumentError, "message too small" if msg.size < 5
@msg = msg
@recipient = recipient
end
def formatted_msg
"[New Message] #{msg}"
end
end
sender = MailSender.new
msg = Message.new("testing")
sender.deliver_message(msg)
สรุป
ใช้ refactoring techniques นี้ช่วยปฏิบัติตาม Single Responsibility Principle และควบคุม classes และเมธอดไว้
ถ้าชอบบทความนี้แชร์ให้เพื่อนๆ ด้วย 🙂