Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

ทำความเข้าใจ Freeze Method ของ Ruby: Object Mutability Explained

Object เป็น mutable หมายถึงอะไร? อย่าให้คำฟูทำสับสน "Mutability" แค่หมายถึง internal state ของ object เปลี่ยนได้ นี่คือ default ของทุก objects ยกเว้นที่ frozen หรือ special objects บางตัว

คือไม่ใช่ทุก objects ใน Ruby ที่ mutable!

เช่น: ไม่มีความหมายให้ numbers, symbols, หรือแม้ true false (ซึ่งก็เป็น objects) เปลี่ยน เลข 1 จะเป็น 1 เสมอ แต่ objects อื่นๆ โดยเฉพาะที่เก็บข้อมูล เช่น Array หรือ Hash ควรเปลี่ยนได้เพื่อ performance

ทางเลือกคืออะไร? สร้าง copy ใหม่ ของ object พร้อมการเปลี่ยนแปลง return object ใหม่นี้ ทิ้ง object เดิมไว้ ถ้า arrays เป็น immutable และอยากเปลี่ยนแค่ element หนึ่ง ต้อง copy ทั้งหมดรวม elements ที่ไม่เปลี่ยน bayangkan copy array ล้าน element ทุกครั้งที่เปลี่ยนอะไรเล็กน้อย! ไม่คุ้มค่า...

ไปดูลึก mutability ใน Ruby

Mutability และ Variables as Pointers

มี category ของ programming errors เกิดจาก 2 สิ่ง:

  • Mutable objects
  • Variables ไม่เก็บข้อมูลโดยตรง แต่เก็บ reference ไปที่เก็บข้อมูล

หนึ่งใน error patterns: พยายาม 'alias' variable

ตัวอย่าง:

name = "Peter"
other_name = name
puts other_name # "Peter"

ทั้ง name และ other_name เก็บ reference ไป string object เดียวกัน ใช้ตัวไหนก็ได้ display หรือ modify string นี้

ทำความเข้าใจ Freeze Method ของ Ruby: Object Mutability Explained

ปัญหาเกิดถ้า treat other_name เหมือน copy ของ string:

other_name[0] = 'T'
name # "Teter"
other_name # "Teter"

เพราะทั้งคู่ point ไป string เดียวกัน เราเปลี่ยน "Peter" เป็น "Teter" ไปแล้ว ปัญหาเพราะคง "Peter" ไว้

Cloning Objects

วิธีจัดการ: ใช้ dup method บอก Ruby ให้ copy object มี clone method อีกตัว ซึ่งนอกจาก copy object ยัง copy frozen status และ singleton methods ที่ define บน object

ตัวอย่าง:

numbers = [1, 2, 3]
more_numbers = numbers.dup
more_numbers << 4
numbers # [1, 2, 3]
more_numbers # [1, 2, 3, 4]

เห็นว่า numbers array เดิม unchanged ลองลบ dup บรรทัดที่ 3 ดูสิ 🙂

The Ruby Freeze Method

อีกวิธีป้องกัน object จากการเปลี่ยนแปลง: 'freeze' มัน ทุก Ruby object freeze ได้ด้วย freeze method ตอน object frozen พยายามเปลี่ยนจะได้ RuntimeError exception

Note: ใช้ frozen? method เช็กว่า object frozen หรือไม่

ตัวอย่าง:

animals = %w( cat dog tiger )
animals.freeze
animals << 'monkey' # RuntimeError: can't modify frozen Array

จุดสำคัญ: freeze แค่ object นั้น ในตัวอย่างคือ array ป้องกัน add/remove items แต่ strings ภายใน array ไม่ frozen ยังเปลี่ยนได้!

animals[1][0] = 't' # => ["cat", "tog", "tiger"]

อยาก freeze strings เรียก freeze บนพวกมัน: animals.each(&:freeze)

Frozen Strings

Mutable objects ส่งผล performance โดยเฉพาะ strings เพราะโอกาสสูงที่ string เดียวกันถูกใช้หลายครั้งในโปรแกรมใหญ่ Ruby สร้าง object ใหม่ทุก string แม้สอง string ดูเหมือนกัน (same 'content') ใช้ object_id ใน irb ดูได้

ตัวอย่าง:

a = 'test'
b = 'test'
a.object_id # 76325640
b.object_id # 76317550

นี่ปัญหา เพราะ objects เหล่านี้กิน memory และ CPU cycles เพิ่ม ตั้งแต่ Ruby 2.1 ใช้ frozen strings Ruby จะใช้ string object เดียวกัน หลีกเลี่ยงสร้าง copies ซ้ำ ประหยัด memory และ performance boost เล็กน้อย

Rails ใช้ frozen strings มาก ดู PR นี้ นำไปสู่ Ruby dev team พิจารณาเปลี่ยน strings เป็น immutable by default จริง Ruby 2.3 (เพิ่ง release ไม่กี่วัน) มี 2 วิธี enable สำหรับโปรเจกต์

1. ใส่ # frozen_string_literal: true บนทุกไฟล์ที่อยากให้ strings immutable

2. ใช้ command-line argument --enable=frozen-string-literal

Immutable strings by default น่าจะมาใน Ruby 3.0

อย่าไป freeze strings ทุกตัวใน app แค่ strings ที่ใช้ร้อยๆ ครั้งจึงเห็น benefit นี่คือ tool หา potential strings ที่ freeze ได้

Know Your Methods

ไม่ใช่ทุกเมธอดใน mutable object จะเปลี่ยน object เช่น gsub return string ใหม่ ทิ้ง original บางเมธอดมี alternative version เปลี่ยน object in-place ซึ่งมัก efficient มาก เมธอดเหล่านี้มักลงท้าย ! แสดง effect

ตัวอย่าง 'bang' methods: gsub! และ map!

สังเกต: เมธอดลงท้าย ! ไม่ได้หมายถึง 'เมธอดที่เปลี่ยน object' เสมอไป ในทั่วไป ! ใช้ denote 'danger' ตัวอย่าง: exit! ออกจากโปรแกรมทันที ignore exit handlers มีเมธอดที่เปลี่ยน object แต่ไม่ลงท้าย ! เช่น delete, clear, push, concat และอีกมากมาย

ปิดท้าย

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

หวังว่าจะมีประโยชน์ โปรด แชร์ ให้เพื่อนๆ เพลิดเพลิน กับมัน และสมัครรับ newsletter ด้านล่างเพื่อไม่พลาด content แบบนี้!