Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Nâng Cao Kiến Trúc Rails Với Presenter & Service Objects: Các Design Pattern Đã Chứng Minh

Tại Sao Cần Design Pattern?

Vấn đề là kiến trúc Rails, Model-View-Controller, chỉ cho bạn cấu trúc cơ bản để đặt code.

Nhưng điều này chưa đủ.

View của bạn trở nên lớn & đầy logic khi mục tiêu của chúng là trình bày thông tin. Controller giữ chi tiết vượt quá những gì cần thiết cho công việc cốt lõi.

Giải pháp là gì? Chúng ta có hai giải pháp dưới dạng design pattern:

  • Pattern Presenter
  • Pattern Service Object

Không phải ai cũng đồng ý về cách triển khai chính xác, nhưng tôi sẽ đưa ra phiên bản hiệu quả cho tôi. Hãy khám phá!

Cách Dùng Presenter Trong Rails

View dùng để trình bày, nghĩa là HTML, CSS, & ERB (Embedded Ruby). Không nên có truy vấn ActiveRecord trong view. Và phần lớn logic nên bị loại bỏ nếu bạn muốn view sạch & dễ làm việc.

Bằng "logic" tôi nghĩa là ra quyết định với câu lệnh if & toán tử ternary

Câu hỏi là... Làm sao? Công cụ đầu tiên xử lý logic trong view là helper. Helper tuyệt vời khi bạn có phương thức định dạng toàn cục dùng ở nhiều view.

Ví dụ: Render Markdown, hiển thị ngày theo định dạng cụ thể, xóa từ cụ thể khỏi văn bản, v.v.

Như sau:

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

Bạn lưu code này dưới thư mục app/helpers & file date_helper.rb.

Mẹo: Luôn truyền input vào helper qua đối số, không bao giờ dựa vào biến instance. Điều này sẽ cứu bạn khỏi nhiều rắc rối.

Helper có hạn chế, nhất là nếu bạn dùng cho mọi nhu cầu định dạng trong view. Chúng có xu hướng tích tụ & thiếu tổ chức. Giải pháp đang đến!

Thay Thế Điều Kiện Phức Tạp & Phương Thức Định Dạng Bằng Presenter Object

Giả sử bạn có view như sau:

 <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 khá ngắn, đúng không? Nhưng cảm giác rất phức tạp với các toán tử ternary & code trùng lặp. Không tốt!

Hãy tạo lớp presenter để giải quyết.

Cách làm:

 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 

Lưu dưới app/presenters/post_presenter.rb, tạo thư mục presenters nếu chưa có.

Bây giờ bạn thay đổi view.

Như sau:

 <% 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> 

Xong! Chúng ta đã: loại bỏ toàn bộ logic khỏi view, thêm tên có ý nghĩa cho các phép định dạng & ra quyết định, và có thể tái dùng class này ở view khác mà không lặp code. Đó là cách dùng presenter trong Rails 🙂

Cách Dùng Service Objects

Controller nên chỉ bảo người khác làm gì, không nên biết cách gửi Tweet, thu phí khách hàng hay tạo PDF. Những thao tác này nên được ủy thác cho service object.

Service object, theo định nghĩa của tôi, là một module Ruby đóng gói logic hoàn thành một hành động.

Ví dụ:

 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 

Quy ước lưu dưới thư mục app/services, file như twitter_service.rb.

Cách dùng? Vì Rails autoload mọi thứ từ app/, code này sẽ có sẵn trong controller.

Ví dụ:

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

Đó là pattern service object hoạt động.

Tóm Tắt

Bạn đã học hai pattern Rails hữu ích giúp cải thiện chất lượng code dự án khi dùng khéo léo! Lượt của bạn áp dụng thôi 🙂 Cảm ơn bạn đã đọc.