Refactor bằng việc sử dùng pattern Decorator
Bài trước mình đã đề cập đến Service object để giảm tải cho controller , model và tránh DRY code. Trong bài này mình tiếp tục giới thiệu về một design pattern nữa đó là Decorator để tối ưu code trong project. Decorator Pattern Decorator cho phép chúng ta thêm các phần xử lý nhỏ cho mỗi ...
Bài trước mình đã đề cập đến Service object để giảm tải cho controller , model và tránh DRY code. Trong bài này mình tiếp tục giới thiệu về một design pattern nữa đó là Decorator để tối ưu code trong project.
Decorator Pattern
Decorator cho phép chúng ta thêm các phần xử lý nhỏ cho mỗi instance trước khi show ra view mà không làm ảnh hưởng các xửa lý của toàn bộ các instance khác trong class. VD trong ta có model users có một field name. Giả sử chúng ta hiển thị ra view(profile cá nhân chẳng hạn) hiển thị name hoa toàn bộ. Cách thông thường thì chúng ta có thể thêm một hàm process_name vào trong model. thế nhưng khi thêm vào đây thì toàn bộ các user khác cũng sẽ có hàm này nhưng nếu vấn đề process_name chỉ dùng ở view thôi thì nó sẽ gây ra lãng phí và làm fat model. Để giải quyết vấn đề này thì chúng ta sẽ đặt hàm này nó vào trong class decorator. và chỉ dùng khi cần thôi không ảnh hưởng đến toàn bộ các instance của các model.
Gem Draper sẽ giúp chúng ta implement và sử dụng rất dễ dàng
Implement
Trước hết bạn add gem draper vào gem file của project gem 'draper' sau đó chạy bundler install default decorator class sẽ là
class UserDecorator < Draper::Decorator delegate_all end
Đặt giả sử không dùng decorate thì đoạn code của chúng ta sẽ như sau
<%= @user.name.underscore if @user.name %>
hoặc nếu bạn đặt phần xử lý đó vào trong model
class User < ActiveRecord::Base def process_name name.undersocre if name end end
Và trên view <%= @user.process_name %> Nó thật sự không cần thiết nếu chỉ phục vụ ở trên view thì lãng phí với những đoạn code logic chỉnh sửa string như thế này mà lại đặt trong model. Bạn tưởng tượng trên view rất nhiều chỗ mà mình muốn convert string hay định dạng ngày giờ mà cái nào cũng nhồi vào model thì thạt là tởm. Giờ sẽ là cách tối ưu cho issue này
- create một folder app/decorators thư mục này sẽ chứa nhiều class tương ứng với mỗi model
- create một file app/decorators/user_decorator.rb ở đây chúng ta sẽ định nghĩ các hàm xử lý để phục vụ hiển thị view
class UserDecorator < Draper::Decorator delegate_all def process_name name.underscore if name end end
để sử dụng bạn chỉ việc: user.decorate.process_name
Khi bạn định nghĩa delegate_all thì toàn bộ các method cũng như attributes trong model User đều có thể được sử dụng trực tiếp. Nói cách khác bạn có thể sử dụng user_decorator như một user bình thường ngoài ra còn sử dụng được thêm các method được định nghĩa trong decorator nữa
hoặc trong controller
class UsersController def show @user = User.find(params[:id]).decorate end end
khi đó trên view bạn chỉ việc
<%= @user.process_name %>
Conclusion
trên đây minh đã giới thiệu về decorate cung như cách implement qua cac ví dụ. Đơn giản chỉ là dùng như một đối tượng của model nhưng lợi ích của Decorator giúp chúng ta dễ dàng quản lý và maintain