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


All Rights Reserved