Tìm hiểu Decorator trong Rails
Bài đăng này đã không được cập nhật trong 8 năm
1. Decorator là gì?
Như chúng ta đã biết, RoR được xây dựng trên mô hình MVC. Trong đó Model đóng vai trò xử lý dữ liệu, Views có nhiện vụ hiển thị dữ liệu cho người dùng.
Giả sử bạn đang xây dựng một hệ thống tuyển dụng nhân sự, trong đó cần lưu rất nhiều thông tin của các ứng viên (Candidate). Để phục vụ cho việc filter, hệ thống cần lưu tên của từng ứng viên dưới dạng first_name
và last_name
. Nhưng trên views bạn cần hiện thị tên ứng viên đó dưới dạng full_name = first_name + last_name.
Đối với trường hợp, cách nhanh nhất chúng ta nghĩ đến là sẽ xây dựng một hàm full_name
trong model Candidate
.
Tuy nhiên đối với những hệ thống lớn, số lượng phương thức cần xử lý tương tự như vậy sẽ rất lớn. Chính vì vậy khi cần nâng cấp, bảo trì hệ thống sẽ rất khó khăn. Decorator được sinh ra để giải quyết vấn đề này. Nó là cầu nối giữa Model và View.
Decorator như một lớp con, cho phép chúng ta tạo thêm những hành động cho đối tượng mà không gây ảnh hưởng tới những đối tượng khác của cùng một lớp. Trong trường hợp trên thì phương thức full_name chỉ tác động lên một đối tượng @candidate thuộc lớp Candidate.
2. Ví dụ về Decorator
Có nhiều cách để sử dụng decorator trong một project. Bạn có thể tự cài đặt thủ công hoặc dùng các gem hỗ trợ như ActiveDecorator hoặc Draper.
Chúng ta sẽ bắt đầu với gem Draper
Trước tiên chúng ta thêm gem vào Gemfile
gem "draper"
Sau đó cài đặt và khởi tạo một lớp decorator cho model Candidate
$ bundle install
$ rails generate decorator Candidate
Tất cả code decorator của mọi model sẽ được lưu trữ trong folder app/decorators/
. Lệnh khởi tạo bên trên sẽ sinh ra một file app/decorators/candidate_decorator.rb
có nội dung như sau:
class CandidateDecorator < Draper::Decorator
delegate_all
end
Giờ chúng ta chỉ cần viết thêm các phương thức trong đó.
class CandidateDecorator < Draper::Decorator
delegate_all
def full_name
if first_name.blank? && last_name.blank?
"No name is provided"
else
"#{first_name} #{last_name}"
end
end
def link_to_candidate
link_to candidate.full_name, candidate
end
end
Sau khi đã hoàn thiện phương thức trong decorate bạn chỉ cần gọi .decorate
trong bất kỳ đối tượng ActiveRecord nào của dự án.
class CandidatesController < ApplicationController
def index
@candidates = Candidate.all.decorate
end
def show
@candidate = Candidate.find(params[:id]).decorate
end
end
Trên views chúng ta gọi ra như sau:
<p id="candidate-<% @candidate.id %>"> Candidate information </p>
<p>
<strong>Full name:</strong>
<%= @candidate.link_to_candidate %>
</p>
Draper khá dễ hiểu và sử dụng. Các bạn có thể tham khảo thêm tại: Draper
Tiếp theo chúng ta sẽ cùng thực hiện cài đặt decorator theo cách hoàn toàn thủ công. Đầu tiên chúng ta tạo thư mục decorators trong thư mục dự án:
$ mkdir app/decorators
Tạo file decorator của model Candidate tại app/decorators/candidate_decorator.rb
module CandidateDecorator def full_name "#{first_name} #{last_name}" end end
Việc còn lại là làm sao để một đối tượng @candidate
của lớp Candidate
có thể sử dụng phương thức full_name
chúng ta vừa tạo. Chúng ta chỉ cần include module CandidateDecorator vào lớp Candidate.
class Candidate < ActiveRecord::Base
include CandidateDecorator
end
Giờ thì trên views chúng ta hoàn toàn sử dụng các phương thức trong decorator Candidate như bình thường cho mọi đối tượng của Candidate.
3. Kết luận
Decorator pattern là một design pattern được thiết kế giúp việc quản lý tốt hơn các phương thức hỗ trợ đối với một đối tượng độc lập mà không ảnh hưởng tới những đối tượng tác cùng lớp. Nó cũng giúp giảm số lượng dòng code trong Model, đóng vai trò giống như model và có thể giao tiếp với views. Với những hệ thống nhỏ thì không thực sự cần thiết để dùng decorator mà chúng ta hoàn toàn có thể viết các phương thức đó ngay trong model. Tuy nhiên nó tỏ ra rất hiệu quả đối với những hệ thống lớn.
Để tìm hiểu nhiều hơn về decorator bạn có thể tham khảo các link sau:
http://johnotander.com/rails/2014/03/07/decorators-on-rails
https://github.com/amatsuda/active_decorator
https://github.com/drapergem/draper
Cám ơn các bạn rất nhiều!
All rights reserved