การเขียนโปรแกรม Metaprogramming ดูเหมือนเป็นคำที่แปลกมาก แต่มันดีไหม
อาจมีประโยชน์ แต่หลายคนไม่ทราบว่าการใช้โปรแกรมเมตามีต้นทุนบางอย่าง
เพียงเพื่อให้เราอยู่ในหน้าเดียวกัน…
metaprogrammingคืออะไร ว่า?
ฉันกำหนด metaprogramming ว่าใช้วิธีใด ๆ ที่:
- เปลี่ยนโครงสร้างของโค้ดของคุณ (เช่น
define_method
) - เรียกใช้สตริงราวกับว่าเป็นส่วนหนึ่งของโค้ด Ruby จริงของคุณ (เช่น
instance_eval
) - ทำบางสิ่งเพื่อตอบสนองต่อเหตุการณ์บางอย่าง (เช่น
method_missing
)
ดังนั้นค่าใช้จ่ายของ metaprogramming คืออะไร? ฉันแบ่งพวกเขาออกเป็น 3 กลุ่ม:
- ความเร็ว
- ความสามารถในการอ่าน
- ความสามารถในการค้นหา
หมายเหตุ :คุณสามารถพูดได้ว่ามีกลุ่มที่สี่:ความปลอดภัย . เหตุผลก็คือ
eval
ซึ่งไม่ได้ทำการตรวจสอบความปลอดภัยใดๆ กับสิ่งที่ถูกส่งผ่าน คุณต้องดำเนินการเอง
มาสำรวจกันอย่างละเอียดกันเลย!
ความเร็ว
ต้นทุนแรกคือความเร็วเนื่องจากวิธีการเขียนโปรแกรมเมตาส่วนใหญ่ช้ากว่าวิธีปกติ
นี่คือโค้ดการเปรียบเทียบบางส่วน:
require 'benchmark/ips' class Thing def method_missing(name, *args) end def normal_method end define_method(:speak) {} end t = Thing.new Benchmark.ips do |x| x.report("normal method") { t.normal_method } x.report("missing method") { t.abc } x.report("defined method") { t.speak } x.compare! end
ผลลัพธ์ (ทับทิม 2.2.4) :
normal method: 7344529.4 i/s defined method: 5766584.9 i/s - 1.34x slower missing method: 4777911.7 i/s - 1.54x slower
อย่างที่คุณเห็นทั้งสองวิธี metaprogramming (define_method
&method_missing
) ค่อนข้างช้ากว่าวิธีปกติเล็กน้อย
นี่คือสิ่งที่น่าสนใจที่ฉันค้นพบ…
ผลลัพธ์ข้างต้นมาจาก Ruby 2.2.4
แต่ถ้าคุณเรียกใช้การวัดประสิทธิภาพเหล่านี้ใน Ruby 2.3
หรือ Ruby 2.4
ดูเหมือนว่าวิธีการเหล่านี้จะช้าลง!
ผลการเปรียบเทียบ Ruby 2.4 :
normal method: 8252851.6 i/s defined method: 6153202.9 i/s - 1.39x slower missing method: 4557376.3 i/s - 1.87x slower
ฉันใช้เกณฑ์มาตรฐานนี้หลายครั้งเพื่อให้แน่ใจว่าไม่ใช่ความบังเอิญ
แต่ถ้าคุณใส่ใจ &ดูการวนซ้ำต่อวินาที (i/s
) ดูเหมือนว่าวิธีการปกติจะเร็วขึ้นตั้งแต่ Ruby 2.3 นั่นคือเหตุผลที่ method_missing
ดูช้าลงมาก 🙂
ความสามารถในการอ่าน
ข้อความแสดงข้อผิดพลาดอาจมีประโยชน์น้อยกว่าเมื่อใช้ instance_eval
/ class_eval
วิธีการ
ดูโค้ดต่อไปนี้:
class Thing class_eval("def self.foo; raise 'something went wrong'; end") end Thing.foo
ซึ่งจะส่งผลให้เกิดข้อผิดพลาดดังต่อไปนี้:
(eval):1:in 'foo': 'something went wrong...' (RuntimeError)
สังเกตว่าเราไม่มีชื่อไฟล์ (มันเขียนว่า eval
แทน) &หมายเลขบรรทัดที่ถูกต้อง ข่าวดีก็คือมีการแก้ไขสำหรับสิ่งนี้ eval
. เหล่านี้ เมธอดใช้พารามิเตอร์พิเศษสองตัว:
- ชื่อไฟล์
- หมายเลขบรรทัด
การใช้ค่าคงที่ในตัว __FILE__
&__LINE__
เป็นพารามิเตอร์สำหรับ class_eval
คุณจะได้รับข้อมูลที่ถูกต้องในข้อความแสดงข้อผิดพลาด
ตัวอย่าง :
class Thing class_eval( "def foo; raise 'something went right'; end", __FILE__, __LINE__ ) end
เหตุใดจึงไม่เป็นค่าเริ่มต้น
ฉันไม่รู้ แต่หากคุณจะใช้วิธีการเหล่านี้ โปรดจำไว้เสมอ 🙂
ความสามารถในการค้นหา
วิธีการเขียนโปรแกรมเมตาทำให้โค้ดของคุณค้นหาได้น้อยลง เข้าถึงได้น้อยลง (ผ่านเอกสารที่แย่กว่านั้น) และแก้ปัญหาได้ยากขึ้น
หากคุณกำลังมองหาคำจำกัดความของเมธอด คุณจะไม่สามารถทำ CTRL+F (หรือช็อตคัทที่คุณใช้) เพื่อค้นหาเมธอดที่กำหนดผ่านเมตาโปรแกรมมิงได้ โดยเฉพาะอย่างยิ่งถ้าชื่อเมธอดถูกสร้างขึ้นในขณะใช้งานจริง
ตัวอย่างต่อไปนี้กำหนด 3 วิธี การใช้โปรแกรมเมตาดาต้า:
class RubyBlog def create_post_tags types = ['computer_science', 'tools', 'advanced_ruby'] types.each do |type| define_singleton_method(type + "_tag") { puts "This post is about #{type}" } end end end rb = RubyBlog.new rb.create_post_tags rb.computer_science_tag
เครื่องมือที่สร้างเอกสาร (เช่น Yard
หรือ RDoc
) ไม่พบวิธีการเหล่านี้และแสดงรายการ
เครื่องมือเหล่านี้ใช้เทคนิคที่เรียกว่า "การวิเคราะห์แบบคงที่" เพื่อค้นหาคลาสและเมธอด เทคนิคนี้สามารถค้นหาวิธีการที่กำหนดไว้โดยตรงเท่านั้น (โดยใช้ def
ไวยากรณ์)
ลองเรียกใช้ yard doc
จากตัวอย่างสุดท้าย คุณจะเห็นว่าวิธีเดียวที่พบคือ create_post_tags
.
หน้าตาเป็นแบบนี้ :
มีวิธีบอก yard
เพื่อบันทึกวิธีการพิเศษโดยใช้ @method
แต่นั่นก็ใช้ไม่ได้ผลเสมอไป
ตัวอย่าง :
class Thing # @method build_report define_method(:build_report) end
นอกจากนี้ หากคุณกำลังจะใช้เครื่องมือเช่น grep
, ack
หรือโปรแกรมแก้ไขของคุณเพื่อค้นหาคำจำกัดความของเมธอด จะหาเมธอด metaprogramming ได้ยากขึ้น กว่าวิธีปกติ
“ฉันไม่คิดว่า Sidekiq ใช้เมตาโปรแกรมมิง เลยเพราะฉันพบว่ามันบดบังรหัสมากกว่าที่จะช่วย 95% ของเวลาทั้งหมด” – Mike Perham ผู้สร้าง Sidekiq
บทสรุป
ไม่ใช่ทุกอย่างที่ไม่ดีเกี่ยวกับ metaprogramming อาจมีประโยชน์ในสถานการณ์ที่เหมาะสมในการทำให้โค้ดของคุณมีความยืดหยุ่นมากขึ้น
เพียงตระหนักถึงค่าใช้จ่ายเพิ่มเติมเพื่อให้คุณสามารถตัดสินใจได้ดีขึ้น
อย่าลืม แชร์โพสต์นี้ ถ้าเห็นว่ามีประโยชน์ 🙂