ไม่ต้องสงสัยเลยว่าโมดูลและมิกซ์อินเป็นแหล่งข้อมูลที่ยอดเยี่ยมที่ทำให้ Ruby น่าสนใจ ทำให้แอปพลิเคชันสามารถแบ่งปันรหัสที่สามารถใช้งานได้ง่ายในที่อื่นๆ นอกจากนี้ยังช่วยเราจัดระเบียบโค้ดโดยจัดกลุ่มฟังก์ชันและข้อกังวล ซึ่งช่วยปรับปรุงความสามารถในการอ่านและบำรุงรักษาโค้ดของเรา
ในบทความนี้ เราจะพูดถึงแนวคิดเบื้องหลังโมดูลและมิกซ์อิน เราจะเรียนรู้วิธีสร้างและผสมโมดูลในชั้นเรียนอื่นๆ และพูดคุยเกี่ยวกับประโยชน์ของการใช้โมดูลเหล่านี้ในแอปพลิเคชัน Ruby on Rails
ฉันหวังว่าคุณจะสนุกกับการเดินทาง!
โมดูลคืออะไร
โมดูลเป็นหนึ่งในทรัพยากรที่ยอดเยี่ยมที่สุดของ Ruby เนื่องจากมีข้อดีสองประการ:เราสามารถสร้างเนมสเปซเพื่อป้องกันการชนกันของชื่อและเราสามารถใช้เป็นมิกซ์อินเพื่อแชร์โค้ดในแอปพลิเคชัน
ในแง่โครงสร้าง โมดูลค่อนข้างคล้ายกับคลาส Ruby สำหรับ Ruby แล้ว Class
เป็น Module
ดังที่เราเห็นด้านล่างใน irb
คอนโซล:
> Class.is_a?(Module)
=> true
คล้ายกับคลาส ด้วยโมดูล เรายังจัดกลุ่มเมธอดและค่าคงที่ และแชร์โค้ด อย่างไรก็ตาม มีความแตกต่างเล็กน้อยระหว่างโมดูลและคลาส Ruby ธรรมดา:
- เราเริ่มนิยามด้วย
Module
คีย์เวิร์ดแทนclass
; - เราไม่สามารถสร้างอินสแตนซ์ของโมดูลได้ ดังนั้นจึงไม่สามารถสร้างวัตถุจากโมดูลได้
- เราไม่สามารถสืบทอดจากโมดูลได้ ดังนั้นเราจึงใช้เป็นมิกซ์อินแทน
- โมดูลเป็นโค้ดแบบสแตนด์อโลน ดังนั้นจึงไม่มีลำดับชั้นการสืบทอดของโมดูล
โมดูลเป็นสถานที่ที่ดีในการให้บริการ ข้อกังวล ค่าคงที่ และรหัสอื่นใดที่ควรจะอยู่ด้วยกันโดยมีความรับผิดชอบเดียวกัน
นี่คือลักษณะของโมดูล:
# lib/modules/invoice_creator.rb
module InvoiceCreator
TAX_FEE = 0.5
def self.generate
puts "Don't worry! I'll generate the invoice for you at #{TAX_FEE}%"
end
def invoice_total
puts "I'll return the invoice total"
1000
end
end
ในตัวอย่างนี้ เราสามารถสังเกตได้ว่าโมดูลสามารถจัดเตรียมเมธอดได้ 2 ประเภท ได้แก่ เมธอดของโมดูลและเมธอดของอินสแตนซ์
self.generate
เป็นวิธีการของโมดูล ซึ่งหมายความว่าเราสามารถใช้งานได้โดยไม่ต้องรวม (หรือขยาย) โมดูลในวัตถุอื่น ซึ่งเป็นเรื่องปกติมากเมื่อเราสร้างออบเจ็กต์บริการ เป็นต้น เราสามารถเรียกวิธีการแบบโมดูลของเราดังนี้:
2.5.3 :006 > InvoiceCreator.generate
Don't worry! I'll generate the invoice for you
=> nil
invoice_total
เป็นวิธีการอินสแตนซ์และเพื่อให้สามารถใช้งานได้ เราจำเป็นต้องรวมโมดูลเข้ากับคลาส เช่นนี้:
# app/models/invoice.rb
class Invoice < ApplicationRecord
include InvoiceCreator # This includes our module in the Invoice class
def calculate_tax
total = invoice_total # included method from our module
tax = total * InvoiceCreator::TAX_FEE
puts "This is the invoice tax: #{tax}"
end
end
วิธีการอินสแตนซ์ทั้งหมดจาก InvoiceCreator
สามารถใช้ได้กับ Invoice
อินสแตนซ์ เพื่อให้เราสามารถเรียก calculate_tax
วิธีง่ายๆ :
2.5.3 :008 > Invoice.new.calculate_tax
"I'll return the invoice total"
"This is the invoice tax: 500.0"
=> nil
2.5.3 :009 >
นอกจากนี้ ภายใน calculate_tax
คุณสังเกตเห็นว่าเรากำลังใช้ค่าคงที่ที่กำหนดไว้ในโมดูลของเรา ดังที่ได้กล่าวไว้ก่อนหน้านี้ โมดูลต่างๆ เป็นตัวเก็บค่าคงที่ที่ยอดเยี่ยม!
ลองนึกภาพสถานการณ์ที่เราจำเป็นต้องมี InvoiceCreator
. สองประเภท เพื่อสร้างใบแจ้งหนี้ที่แตกต่างกันโดยสิ้นเชิงสำหรับซัพพลายเออร์และลูกค้า เราจะจบลงด้วยการจัดประเภทชื่อ และเพื่อหลีกเลี่ยงปัญหานี้ เราจึงใช้ประโยชน์จากโมดูลอื่นๆ:เนมสเปซ มาดูกันในหัวข้อถัดไป
เนมสเปซทุกที่
เนมสเปซสามารถกำหนดเป็นวิธีจัดระเบียบโค้ดของเราเมื่อเราต้องการสร้างบริบทในเครื่องสำหรับฟังก์ชันที่กำหนด ซึ่งเป็นสิ่งที่เราต้องการสำหรับสถานการณ์ที่ฉันเพิ่งอธิบายไป:บริบทที่แตกต่างกันสำหรับผู้สร้างใบแจ้งหนี้ของลูกค้า และอีกบริบทสำหรับซัพพลายเออร์ ผู้สร้างใบแจ้งหนี้
กลับไปที่รหัสของเรา เราจะต้องสร้างสองโมดูลที่แตกต่างกัน:
# lib/modules/customer/invoice_creator.rb
module Customer
module InvoiceCreator
def self.generate
puts "Don't worry! I'll generate the customer invoice for you"
end
end
end
# lib/modules/supplier/invoice_creator.rb
module Supplier
module InvoiceCreator
def self.generate
puts "Don't worry! I'll generate the supplier invoice for you"
end
end
end
จากนั้นเราก็สามารถใช้ Customer::InvoiceCreator
หรือ Supplier::InvoiceCreator
ที่เราต้องการ:
2.5.3 :014 > Customer::InvoiceCreator.generate
Don't worry! I'll generate the customer invoice for you
=> nil
2.5.3 :015 > Supplier::InvoiceCreator.generate
Don't worry! I'll generate the supplier invoice for you
=> nil
2.5.3 :016 >
ด้วยวิธีนี้ แต่ละรหัสจะถูกรวมไว้ในโมดูลของตัวเอง โดยคำนึงถึงการแยกข้อกังวลหลัก นอกจากนั้น การกำหนดเนมสเปซในทุกๆ ที่ยังเป็นวิธีที่ยอดเยี่ยมในการจัดระเบียบโค้ดของเราอย่างดี
มาดูกันว่าเราจะใช้ประโยชน์อื่นๆ ของโมดูล Ruby ได้อย่างไร:มิกซ์อิน
ความมหัศจรรย์ของการใช้มิกซ์อิน
อย่างที่คุณอาจทราบแล้ว คุณลักษณะหนึ่งของ Ruby คือมันใช้กลไกการสืบทอดเดี่ยว ซึ่งหมายความว่าคลาสสามารถสืบทอดจากคลาสอื่นเท่านั้น เราอาจจำเป็นต้องสืบทอดจากชั้นเรียนมากขึ้น ใน Ruby เราครอบคลุมความต้องการนั้นได้โดยใช้องค์ประกอบแทนรูปแบบการสืบทอด
สามารถทำได้โดยใช้มิกซ์อิน เมื่อเราผสมโค้ดในคลาส Ruby อื่น เรากำลังเพิ่มพฤติกรรมให้กับคลาสนี้มากขึ้นโดยไม่ต้องใช้การสืบทอด และสิ่งนี้น่าทึ่งมาก ดังนั้นใน Ruby มิกซ์อินจึงเป็นโมดูลที่เรารวมไว้ในคลาสที่จำเป็น จากการที่เราได้รับจากการรักษารหัสของเราให้สะอาดและแยกความรับผิดชอบตามที่ควรจะเป็น
ตัวอย่างเช่น สมมติว่า InvoiceCreator
. ของเรา โมดูลเป็นบริการที่ต้องใช้ฟังก์ชันที่จัดเตรียมโดยโมดูลอื่นๆ เช่น InvoiceCalculator
, InvoiceRenderer
และ InvoiceSender
จำเป็นต้องดำเนินการตามขั้นตอนการสร้างใบแจ้งหนี้ให้สำเร็จ
เราสามารถบรรลุสิ่งนี้ได้โดยการรวมกลุ่มของโมดูลเป็นมิกซ์อินในโค้ดของเรา ดังนั้นเราจึงสามารถใช้เมธอดได้โดยตรง ดังตัวอย่างด้านล่าง:
# lib/modules/invoice_calculator.rb
module InvoiceCalculator
def calculate_items_total
puts "imagine some math here"
end
end
# lib/modules/invoice_renderer.rb
module InvoiceRenderer
def generate_invoice_pdf
puts "imagine that we are using some PDF generation magic here"
end
end
# lib/modules/invoice_sender.rb
module InvoiceSender
def send_invoice
puts "imagine your favorite mail service being used here"
end
end
เนื่องจากเราไม่สามารถสร้าง InvoiceCreator
สืบทอดจากอีกสามโมดูลที่เหลือ เราจะรวมพวกมันเข้าไปแทน วิธีนี้ InvoiceCreator
รวมวิธีการทั้งหมดจากโมดูลใบแจ้งหนี้อื่น ๆ และเราสามารถเรียกสิ่งเหล่านี้ในคลาส/โมดูลใดก็ได้ InvoiceCreator
โมดูลถูกรวมอยู่ด้วย ระวังด้วย! หากโมดูลใดมีเมธอดที่มีชื่อเหมือนกัน โมดูลจะเขียนทับกัน
# lib/modules/customer/invoice_creator.rb
module Customer
module InvoiceCreator
include InvoiceCalculator
include InvoiceRenderer
include InvoiceSender
def generate_invoice
calculate_items_total # from InvoiceCalculator
generate_invoice_pdf # from InvoiceRenderer
send_invoice # from InvoiceSender
end
end
end
ตอนนี้เราสามารถเรียกวิธีการบริการของเราได้ทุกที่ที่เรารวมไว้โดยทำสิ่งนี้:
# app/models/invoice.rb
class Invoice < ApplicationRecord
include Customer::InvoiceCreator
def send_invoice_to_customer
puts "Don't worry! I'll generate the customer invoice for you"
generate_invoice
end
end
นี่จะเป็นผลลัพธ์ เรียกวิธีการจากโมดูลใบแจ้งหนี้ที่รวมผ่าน InvoiceCreator
โมดูล:
2.5.3 :051 > Invoice.new.generate_invoice
Don't worry! I'll generate the supplier invoice for you
imagine some math here
imagine that we are using some PDF generation magic here
imagine your favorite mail service being used here
=> nil
สังเกตว่านี่คือวิธีที่เราใช้องค์ประกอบเหนือหลักการสืบทอด หลักการนี้ชี้ว่าคุณควรใช้องค์ประกอบภาพทุกครั้งที่ทำได้ ในการจัดองค์ประกอบ เราสร้างชั้นเรียนที่มีหน้าที่จัดหาฟังก์ชันเฉพาะให้กับผู้อื่น ซึ่งเป็นสิ่งที่เรากำลังทำอยู่
นั่นคือทั้งหมด!
ในฐานะนักพัฒนา Ruby เราชอบที่จะใช้สิ่งอำนวยความสะดวกและไวยากรณ์น้ำตาลในการวางแผน เขียน บำรุงรักษา และปรับโครงสร้างโค้ดของเราใหม่ ฉันหวังว่าในบทความนี้ คุณจะเข้าใจได้ว่าทำไมโมดูลจึงเป็นแหล่งข้อมูลที่ยอดเยี่ยมเพื่อช่วยปรับปรุงความสามารถในการอ่านโค้ดของเรา เพื่อรักษาสิ่งต่าง ๆ ไว้ด้วยความรับผิดชอบเพียงอย่างเดียว และรักษา Codebase ของเราให้สะอาดและง่ายต่อการบำรุงรักษา และเราสามารถใช้มิกซ์อินเมจิกเพื่อรวมโมดูลในโค้ดของเราในกรณีที่เราจำเป็นต้องสืบทอดจากหลายแหล่ง
เราจะพูดถึงทรัพยากรที่ยอดเยี่ยมที่สุดของ Ruby and Rails ที่นี่ โปรดคอยติดตาม!
ป.ล. หากคุณต้องการอ่านโพสต์ Ruby Magic ทันทีที่ออกจากสื่อ สมัครรับจดหมายข่าว Ruby Magic ของเราและไม่พลาดแม้แต่โพสต์เดียว!