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

Rails Concerns:กังวลหรือไม่กังวล

หากคุณเคยใช้ Ruby on Rails มาก่อน คุณอาจเคยเจอข้อกังวลใจมาก่อน เมื่อใดก็ตามที่คุณเริ่มต้นโปรเจ็กต์ Rails ใหม่ คุณจะได้รับ app/controllers/concerns และ app/models/concerns . แต่สิ่งที่เป็นกังวล? และทำไมบางครั้งผู้คนจากชุมชน Rails ถึงพูดถึงพวกเขาในทางที่ไม่ดี

ภาพรวมโดยย่อ

Rails Concern คือโมดูลใดๆ ที่ขยาย ActiveSupport::Concern โมดูล. คุณอาจถามว่าข้อกังวลต่างจากโมดูลอย่างไร ข้อแตกต่างที่สำคัญคือข้อกังวลของ Rails ช่วยให้คุณใช้เวทมนตร์ได้ เช่น:

# app/models/concerns/trashable.rb
 
module Trashable
  extend ActiveSupport::Concern
 
  included do
    scope :existing, -> { where(trashed: false) }
    scope :trashed, -> { where(trashed: true) }
  end
 
  def trash
    update_attribute :trashed, true
  end
end

คุณเห็นคำนั้นรวมอยู่ด้วย เป็นคาร์โบไฮเดรตเล็กน้อยของ Rails ที่โรยบนโมดูล Ruby ActiveSupport::Concernอะไร ทำเพื่อคุณคืออนุญาตให้คุณใส่โค้ดที่คุณต้องการประเมินภายในบล็อกที่รวมไว้ ตัวอย่างเช่น คุณต้องการแยกตรรกะการทิ้งขยะออกจากโมเดลของคุณ included ให้คุณทำในสิ่งที่เราทำและต่อมารวมถึงความกังวลของโมเดลของคุณดังนี้:

class Song < ApplicationRecord
  include Trashable
 
  has_many :authors
 
  # ...
end

ค่อนข้างสะดวกและไร้เดียงสา ณ จุดนี้ใช่ไหม โมเดลลดน้ำหนักได้เล็กน้อยและตอนนี้สามารถนำขยะกลับมาใช้ซ้ำกับรุ่นอื่นๆ ได้ ไม่ใช่แค่โมเดล Song ของเรา สิ่งต่างๆ อาจซับซ้อนขึ้นได้ เข้าไปค้นหากันเลย

ตัวอย่างคลาสสิกของมิกซ์ซิน

ก่อนที่เราจะลงมือในเชิงลึกของข้อกังวล เรามาเพิ่มคำอธิบายอื่นก่อน เมื่อคุณเห็น include SomeModule หรือ extend AnotherModule สิ่งเหล่านี้เรียกว่า mixins มิกซ์อินคือชุดของโค้ดที่สามารถเพิ่มไปยังคลาสอื่นได้ และอย่างที่เราทุกคนทราบจากเอกสารของ Ruby โมดูลคือชุดของเมธอดและค่าคงที่ สิ่งที่เรากำลังทำอยู่นี้คือการรวมโมดูลที่มีเมธอดและค่าคงที่ในคลาสต่างๆ เพื่อให้สามารถใช้งานได้

นั่นคือสิ่งที่เราทำกับ Trashable กังวล. เราแยกตรรกะทั่วไปเกี่ยวกับการทำลายวัตถุแบบจำลองลงในโมดูล โมดูลนี้สามารถรวมไว้ในที่อื่นได้ในภายหลัง ดังนั้น มิกซ์อิน เป็นรูปแบบการออกแบบที่ใช้ไม่เฉพาะใน Ruby และ Rails เท่านั้น แต่ไม่ว่าจะใช้ที่ไหน ผู้คนจะชอบและคิดว่ามันดี หรือเกลียดชังและคิดว่ามันควบคุมไม่ได้

เพื่อให้เข้าใจสิ่งนี้มากขึ้น เราจะพูดถึงข้อดีและข้อเสียของการใช้สิ่งเหล่านี้ หวังว่าด้วยการทำเช่นนี้ เราจะได้รับความเข้าใจว่าเมื่อใดหรือจะใช้ข้อกังวลหรือไม่

ฉันมีทุกอย่าง

เมื่อคุณตัดสินใจที่จะดึงข้อมูลที่เป็นข้อกังวล เช่น Trashable กังวล ตอนนี้คุณสามารถเข้าถึงฟังก์ชันการทำงานทั้งหมดได้จากทุกที่ Trashable รวมอยู่ด้วย สิ่งนี้นำมาซึ่งพลังอันยิ่งใหญ่ แต่อย่างที่ Richard Schneeman กล่าวในบล็อกของเขาที่โพสต์ในหัวข้อว่า "พลังอันยิ่งใหญ่มาพร้อมความสามารถที่ยอดเยี่ยมในการสร้างโค้ดที่ซับซ้อน" เขาหมายถึงโค้ดที่ซับซ้อนซึ่งคุณอาจวางใจได้ ซึ่งควรจะเป็น อยู่ในความกังวลของคุณ

ถ้าเราดูที่ Trashable อีกครั้ง:

module Trashable
  extend ActiveSupport::Concern
 
  included do
    scope :existing, -> { where(trashed: false) }
    scope :trashed, -> { where(trashed: true) }
  end
 
  def trash
    update_attribute :trashed, true
  end
end

ตรรกะของข้อกังวลนั้นขึ้นอยู่กับความจริงที่ว่า trashed ฟิลด์มีอยู่ทุกที่ที่มีข้อกังวล ใช่ไหม ไม่เป็นไร นี่คือสิ่งที่เราต้องการ แต่สิ่งที่ผมเห็นคือผู้คนถูกล่อลวงให้ดึงสิ่งอื่นจากตัวแบบมาสู่ความกังวล เพื่อวาดภาพว่าสิ่งนี้จะเกิดขึ้นได้อย่างไร ลองนึกภาพว่า Song model มีวิธีอื่น featured_authors :

class Song < ApplicationRecord
  include Trashable
 
  has_many :authors
 
  def featured_authors
    authors.where(featured: true)
  end
 
  # ...
end
 
class Album < ApplicationRecord
  include Trashable
 
  has_many :authors
 
  def featured_authors
    authors.where(featured: true)
  end
 
  # ...
end

เพื่อให้เห็นภาพมากขึ้น ฉันได้เพิ่ม Album โมเดลที่มี Trashable สมมติว่าเราต้องการแจ้งผู้แต่งเพลงและอัลบั้มที่เป็นจุดเด่นเมื่อพวกเขาถูกทิ้งลงในถังขยะ ผู้คนจะถูกล่อลวงให้ใส่ตรรกะนี้ไว้ในข้อกังวลดังนี้:

module Trashable
  extend ActiveSupport::Concern
 
  included do
    scope :existing, -> { where(trashed: false) }
    scope :trashed, -> { where(trashed: true) }
  end
 
  def trash
    update_attribute :trashed, true
 
    notify(featured_authors)
  end
 
  def notify(authors)
    # ...
  end
end

ที่นี่สิ่งต่าง ๆ เริ่มซับซ้อนขึ้นเล็กน้อย เนื่องจากเรามีตรรกะในถังขยะอยู่นอกโมเดลเพลงของเรา เราจึงอาจต้องการแจ้งใน Trashable กังวล. ในนั้นมีสิ่ง "ผิด" เกิดขึ้น featured_authors นำมาจาก Song แบบอย่าง. ตกลง สมมติว่าคำขอตรวจสอบ passpull และการตรวจสอบ CI

จากนั้น อีกสองสามเดือนข้างหน้า ข้อกำหนดใหม่ถูกกำหนดขึ้นโดยที่นักพัฒนาจำเป็นต้องเปลี่ยนวิธีที่เรานำเสนอ featured_authors สำหรับเพลง Forexample ข้อกำหนดใหม่ต้องการแสดงเฉพาะผู้เขียนที่โดดเด่นจากยุโรป โดยธรรมชาติแล้ว นักพัฒนาจะพบว่ามีการกำหนดและแก้ไขผู้เขียนที่โดดเด่นในที่ใดบ้าง

class Song < ApplicationRecord
  include Trashable
 
  has_many :authors
 
  def featured_authors
    authors.where(featured: true).where(region: 'Europe')
  end
 
  # ...
end
 
class Album < ApplicationRecord
  include Trashable
 
  has_many :authors
 
  # ...
end

วิธีนี้ใช้ได้ผลดีในทุกที่ที่เราแสดงให้ผู้เขียนเห็น แต่หลังจากที่เราปรับใช้ในการผลิต ผู้คนจากส่วนอื่นๆ ของโลกจะไม่ได้รับการแจ้งเตือนอีกต่อไปเกี่ยวกับเพลงของพวกเขา ข้อผิดพลาดเช่นนี้เกิดขึ้นได้ง่ายเมื่อใช้ข้อกังวล ตัวอย่างข้างต้นนั้นเรียบง่ายและประดิษฐ์ขึ้น แต่ตัวอย่างที่ "อยู่ในป่า" อาจเป็นเรื่องยากมาก

สิ่งที่มีความเสี่ยงในที่นี้คือความกังวล (มิกซ์อิน) รู้มากเกี่ยวกับโมเดลที่มันรวมอยู่ในนั้น นี่คือสิ่งที่เรียกว่าการพึ่งพาแบบวงกลม . Song และ Album ขึ้นอยู่กับ Trashable สำหรับการทิ้ง Trashable ขึ้นอยู่กับทั้งคู่สำหรับfeatured_authors คำนิยาม. อาจกล่าวได้เช่นเดียวกันว่า Trashable fieldneeds จะต้องมีอยู่ในทั้งสองรุ่นเพื่อให้มี Trashable กังวลเรื่องการทำงาน

นี่คือสาเหตุที่สโมสรไม่ฝักใฝ่ฝ่ายใดอาจต่อต้าน และสโมสรมืออาชีพมีไว้สำหรับ ผมว่า ก่อน เวอร์ชันของ Trashable เป็นสิ่งที่ฉันจะไปกับ codebase ของฉัน มาดูกันว่าเราจะทำให้เวอร์ชันที่สองดีขึ้นได้อย่างไร

คุณมาจากไหน

มองย้อนกลับไปที่ Trashable . ของเรา ด้วยการแจ้ง เราต้องทำอะไรสักอย่างกับมัน อีกสิ่งหนึ่งที่เกิดขึ้นเมื่อใช้ความกังวลคือเรามักจะทำสิ่งที่แห้งเกินไป เรามาลองทำสิ่งนั้นเพื่อจุดประสงค์ในการสาธิตกับโมเดลที่มีอยู่ของเราโดยสร้างข้อกังวลอื่น (อดทนกับฉันใน อันนี้):

module Authorable
  has_many :authors
 
  def featured_authors
    authors.where(featured: true)
  end
end

จากนั้น Song . ของเรา และ Album จะมีลักษณะดังนี้:

class Song < ApplicationRecord
  include Trashable
  include Authorable
 
  # ...
end
 
class Album < ApplicationRecord
  include Trashable
  include Authorable
 
  # ...
end

เราทำทุกอย่างให้แห้ง แต่ตอนนี้ข้อกำหนดสำหรับผู้แต่งจากยุโรปยังไม่บรรลุผล ที่แย่ไปกว่านั้นตอนนี้คือ Trashable ข้อกังวลและรุ่นขึ้นอยู่กับ Authorable . อะไรนรก? ตรงคำถามของฉันเมื่อฉันจัดการกับข้อกังวลบางเวลาที่ผ่านมา เป็นการยากที่จะติดตามว่าวิธีการมาจากไหน

วิธีแก้ปัญหาทั้งหมดของฉันคือเก็บ featured_authors ให้ใกล้เคียงกับโมเดลมากที่สุด notify วิธีการควร ไม่ ร่วมเป็นส่วนหนึ่งของ Trashable กังวลเลย แต่ละรุ่นควรดูแลด้วยตัวเอง โดยเฉพาะอย่างยิ่งหากพวกเขามีแนวโน้มที่จะแจ้งกลุ่มย่อยที่แตกต่างกัน มาดูวิธีทำไม่เจ็บตัวกัน:

 
# Concerns
module Trashable
  extend ActiveSupport::Concern
 
  included do
    scope :existing, -> { where(trashed: false) }
    scope :trashed, -> { where(trashed: true) }
  end
 
  def trash
    update_attribute :trashed, true
  end
end
 
module Authorable
  has_many :authors
 
  # Other useful methods that relate to authors across models.
  # If there are none, ditch the concern.
end
 
# Models
class Song < ApplicationRecord
  include Trashable
  include Authorable
 
  def featured_authors
    authors.where(featured: true).where(region: 'Europe')
  end
 
  # ...
end
 
class Album < ApplicationRecord
  include Trashable
  include Authorable
 
  def featured_authors
    authors.where(featured: true)
  end
 
  # ...
end

ความกังวลเช่นนี้สามารถจัดการได้และไม่ซับซ้อนเกินไป ฉันข้าม notify ฟังก์ชันที่ฉันอธิบายไว้ก่อนหน้านี้เนื่องจากอาจเป็นหัวข้อสำหรับวันอื่น

บอสตัวสุดท้าย

สำหรับ Basecamp ผู้สร้าง Rails ความกังวลเกี่ยวกับการอ้างอิงข้อกังวลอื่น ๆ ดูเหมือนจะสมบูรณ์แบบตามที่ DHH แสดงไว้ในทวีตเมื่อก่อน:

การดูภาพหน้าจอของรหัสแสดงว่าคุณกำลังอ้าปากค้างหรือตกใจ ฉันรู้สึกว่าไม่มีระหว่างนี้ ถ้าฉันมีโอกาสแก้ไขโค้ดนี้ ฉันคิดว่ามันเป็น "การต่อสู้ขั้นสุดท้ายกับบอส" แต่เรื่องตลกที่น่าสนใจของที่นี่คือมีความคิดเห็นที่บอกว่าข้อกังวลใดขึ้นอยู่กับข้อใด ลองดูที่:

  # ...
 
  include Subscribable # Depends on Readable
  include Eventable    # Depends on Recordables
 
  # ...

การแสดงความคิดเห็นเช่นนี้อาจมีประโยชน์ แต่ก็ยังมีการตั้งค่าไว้สำหรับทำบางสิ่งที่ไม่สมบูรณ์ โดยเฉพาะอย่างยิ่งหากคุณเพิ่งเริ่มใช้ codebase การเป็นคนใหม่และการไม่รับรู้ถึง "gotchas" ทั้งหมดที่โค้ดมี สามารถส่งความกังวลลงไปด้านล่างได้อย่างแน่นอน

สิ่งนี้คือสิ่งที่ DHH แบ่งปันในความคิดเห็นในการสนทนา ทวีตตอบกลับภายในถามว่าคนที่ทำงานกับ codebase นี้ควรโต้ตอบกับข้อกังวลเช่นนี้อย่างไร DHH ตอบว่าพวกเขาไม่มีเอกสารที่เป็นลายลักษณ์อักษรมากนัก พวกเขาไม่ค่อยจ้างดังนั้นทีมของพวกเขาจึงคุ้นเคยกับเอกสารเหล่านี้เป็นอย่างดี

แต่การมีทีมงานมากประสบการณ์ที่รู้จัก codebase และข้อโต้แย้งในการใช้นั้นเป็นเรื่องแปลกและไม่แข็งแกร่ง ฉันเดาว่ามันเป็นความรู้สึกมากกว่าที่จะใช้พวกเขาหรือไม่ คุณพอใจกับการสืบทอดหลาย ๆ แบบที่ modulesprovide หรือคุณชอบองค์ประกอบหรือไม่? โทร.

บทสรุป

ดังที่เราได้เห็น ความกังวลไม่ได้มีอะไรมากไปกว่าโมดูลที่มีไวยากรณ์ที่เป็นประโยชน์ในการดึงข้อมูลและทำให้โค้ดของคุณแห้ง หากคุณมีเครื่องมือที่มีประโยชน์มากกว่าที่คาดไว้ คุณอาจไม่ควรติดต่อเพื่อแจ้งข้อกังวลทันที พฤติกรรมที่คล้ายกับการจัดการไฟล์แนบและตรรกะในการทำลายล้างที่เราแสดงให้เห็นในตัวอย่างอาจเป็นตัวเลือกที่ดีในการแยกออกเป็นโมดูล (ข้อกังวล)

หวังว่าคุณจะได้เห็นสิ่งที่ดีและไม่ดีที่เป็นไปได้เมื่อจัดการกับข้อกังวลและโมดูลโดยทั่วไป จำไว้ว่าไม่มีรหัสใดที่สมบูรณ์แบบ และสุดท้าย คุณจะเรียนรู้ได้อย่างไรว่าอะไรดีและอะไรไม่ดีสำหรับคุณ หากคุณไม่พยายามและอาจล้มเหลวหรือประสบความสำเร็จ

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

รอตอนต่อไป เชียร์!

ป.ล. หากคุณต้องการอ่านโพสต์ Ruby Magic ทันทีที่ออกจากสื่อ สมัครรับจดหมายข่าว Ruby Magic ของเราและไม่พลาดแม้แต่โพสต์เดียว!