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

วิธีใช้มิกซ์อินและโมดูลในแอปพลิเคชัน Ruby on Rails ของคุณ

ไม่ต้องสงสัยเลยว่าโมดูลและมิกซ์อินเป็นแหล่งข้อมูลที่ยอดเยี่ยมที่ทำให้ 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 ของเราและไม่พลาดแม้แต่โพสต์เดียว!