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

ปรับปรุงสถาปัตยกรรม Rails ด้วย Presenter & Service Objects: Design Patterns ที่พิสูจน์แล้ว

ทำไมต้องมี Design Patterns?

ปัญหาคือสถาปัตยกรรม Rails (Model-View-Controller) ให้โครงสร้างพื้นฐานวางโค้ด

แต่มันไม่พอ

Views ใหญ่โตและเต็มไปด้วย Logic ในขณะที่เป้าหมายคือ "นำเสนอข้อมูล"

Controllers ถือรายละเอียดมากกว่าที่จำเป็นสำหรับงานหลักของ Controller

โซลูชันคืออะไร?

เรามี 2 แนวทางแก้ปัญหาในรูปแบบ Design Patterns:

  • Presenter Pattern
  • Service Object Pattern

ไม่ทุกคนเห็นพ้องวิธี Implement แต่อย่างที่ทำงานได้กับผม — มาสำรวจกัน!

วิธีใช้ Presenters ใน Rails

Views ใช้สำหรับ Presentation — คือ HTML, CSS, และ ERB (Embedded Ruby)

ไม่ควรมี ActiveRecord queries ใน Views

และ ส่วนใหญ่ของ Logic ควรเอาออก ถ้าอยากให้ Views สะอาดและทำงานง่าย

"Logic" ที่นี่หมายถึง การตัดสินใจด้วย if statements และ ternary operators

คำถามคือ... ทำยังไง?

เครื่องมือแรกคือ Helpers

Helpers เหมาะสำหรับ Global formatting method ที่ใช้ในหลาย Views

เช่น: Render Markdown, แสดงวันที่ในรูปแบบเฉพาะ, ลบคำเฉพาะจากข้อความ ฯลฯ

ตัวอย่าง:

 module DateHelper
  def date_as_month_and_year(date)
    date.strftime("%B %Y")
  end
end 

บันทึกโค้ดไว้ที่ app/helpers/date_helper.rb

เคล็ดลับ: ส่ง Input เข้า Helper methods ผ่าน Arguments เสมอ อย่าพึ่ง Instance Variables — จะหลุดพ้นปัญหาเยอะ

Helper methods มีข้อจำกัด โดยเฉพาะถ้าใช้สำหรับทุกความต้องการด้าน Formatting ใน Views — มักสะสมและขาด Organization

มีโซลูชัน!

แทนที่ Conditionals และ Formatting Methods ซับซ้อนด้วย Presenter Object

สมมติ View แบบนี้:

 <p>
  Post title: <%= post.title.gsub("forbidden word", "") %>
  <%= link_to "Read post", post, class: "w-75 p-3 text-#{post.draft? ? "orange" : "green"} border-#{post.draft? ? "orange" : "green"}" %>
</p> 

View สั้น แต่รู้สึกซับซ้อนกับ Ternary operators และโค้ดซ้ำ — ไม่ดี!

มาสร้าง Presenter Class กัน

ทำแบบนี้:

 class PostPresenter
  def initialize(post)
    @post = post
  end

  def title_without_forbidden_words
    @post.title.gsub("forbidden word", "")
  end

  def css_color
    @post.draft? ? "orange" : "green"
  end
end 

บันทึกที่ app/presenters/post_presenter.rb สร้างโฟลเดอร์ presenters ถ้ายังไม่มี

ตอนนี้แก้ View ได้แล้ว

แบบนี้:

 <% presenter = PostPresenter.new(post) %>
<p>
  Post title: <%= presenter.title_without_forbidden_words %>
  <%= link_to "Read post", post, class: "w-75 p-3 text-#{presenter.css_color} border-#{presenter.css_color}" %>
</p> 

เสร็จแล้ว!

  • เอา Logic ทั้งหมดออกจาก View
  • ตั้งชื่อที่มีความหมายให้ Operations การ Format และตัดสินใจ
  • Reuse Class นี้ใน Views อื่นได้โดยไม่ซ้ำโค้ด

ที่คือวิธีใช้ Presenters ใน Rails 🙂

วิธีใช้ Service Objects

Controllers ควรแค่ "บอกให้คนอื่นทำ" — ไม่ควรมีความรู้เรื่องการส่ง Tweet, Charge ลูกค้า หรือ Generate PDF

Operation เหล่านี้ ควรมอบหมาย (delegate) ให้ Service Object

Service Object ตามนิยามของผม คือ Ruby Module ที่ห่อหุ้ม Logic สำหรับทำ Action ให้เสร็จ

ตัวอย่าง:

 module TwitterService
  def self.send_welcome_message(twitter_handle)
    client.update("@#{twitter_handle} welcome to 'Oranges & Apples', we hope you enjoy our juicy fruit!")
  end

  def self.client
    @client ||= Twitter::REST::Client.new do |config|
      config.consumer_key = "..."
      config.consumer_secret = "..."
      config.access_token = "..."
      config.access_token_secret = "..."
    end
  end
end 

Convention คือบันทึกที่ app/services/twitter_service.rb

ใช้ยังไง?

Rails autoload ทุกอย่างจาก app/ โค้ดนี้พร้อมใช้ใน Controllers

ตัวอย่าง:

 class UsersController
  def create
    # ...
    TwitterService.send_welcome_message(user.twitter_handle)
  end
end 

นี่คือ Service Object Pattern ในการทำงาน

Summary (สรุป)

คุณเรียนรู้ 2 Rails Patterns ที่ช่วยปรับปรุง Code Quality ของโปรเจกต์เมื่อใช้อย่างฉลาด!

ตอนถึงคุณแล้ว — ไป Apply กันเถอะ 🙂

ขอบคุณที่อ่านครับ