วันนี้เขียน ๆ Ruby อยู่ ก็เจอเรื่องพิศดารอีกเรื่องหนึ่ง เกี่ยวกับเรื่องของตัวแปรคลาส (class variable) กับการสืบทอดคุณสมบัติ (inheritance)
ถ้าอ่าน ๆ ตามหนังสือทั่วไป จะบอกว่าถ้าต้องการตัวแปรสำหรับคลาส ให้ขึ้นหน้าชื่อตัวแปรด้วย @@ ตัวแปรลักษณะนี้ดูเผิน ๆ เหมือน static ใน C++ หรือใน Java แต่ทำไปทำมามันไม่ใช่ และทำให้เกิดความงงงวยอย่างยิ่ง
พิจารณาโปรแกรมด้านล่างนะครับ
class Parent @@a = 0 def set_a(b) @@a = b end def p_a puts @@a end end class Child < Parent def do_something @@a = 300 end end
ในโปรแกรมเรามีคลาสอยู่สองคลาส แล้วก็มีตัวแปรคลาส @@a อยู่
ลองดูตัวอย่างการทำงานนี้นะครับ
irb(main):019:0* p = Parent.new # irb(main):020:0> p.p_a # output: 0 irb(main):021:0> p.set_a(20) irb(main):022:0> p.p_a # output: 20 irb(main):023:0> c = Child.new irb(main):024:0> c.p_a # output: 20 (!) irb(main):025:0> c.do_something irb(main):026:0> c.p_a # output: 300 irb(main):027:0> p.p_a # output: 300 (!!!!)
จะเห็นได้ว่าตัวแปรคลาส @@a ที่ประกาศที่คลาส Parent มันใช้ร่วมกันกับ @@a ที่คลาส Child
ทีนี้พอเราจะใช้ตัวแปรดังกล่าวเก็บค่าที่ใช้เฉพาะในคลาส มันเลยจะไปปนกันเละเทะไปหมด
แล้วทางออกล่ะ?
ไปค้น ๆ มีเขียนถึงเรื่องนี้ไว้หลายที่ เช่น
จะเรียกว่าภาษานี้แข็งแกร่งหรือว่าภาษานี้ประหลาดหรือว่าภาษานี้พิศดารก็เลือกเอาเองแล้วกันนะครับ ขอไปแก้โปรแกรมต่อก่อน ;)
กระทู้เก่าๆ จะย้ายตามไปในภายหลัง ตอนนี้ปิดการโพสต์กระทู้ไว้ เหลือไว้เฉพาะอ้างอิงเท่านั้น
ใน rails ก็มีใช้เยอะเหมือนกัน โดย rails open class Class แล้วเพิ่ม class method ที่ชื่อ cattr_reader, cattr_writer, cattr_accessor หน้าตาประมาณนี้
ตัวอย่างที่นำไปใช้ ก็เช่น ActiveRecord
โอ้ว ไม่นึกว่าจะมีใช้เยอะขนาดนี้…