7 Design Patterns to Refactor MVC Components in Rails
Làm thế nào để các thành phần MVC trở nên đơn giản Để khiến cho Models, Views, Controllers trở nên đơn giản và chuẩn hóa, chúng ta phải liên tục refactor hay tái cấu trúc trên code đã được viết. Trong quá trình tái cấu trúc sẽ không được thay đổi bất kỳ các phản hồi tương tác với bất kỳ hành động ...
Làm thế nào để các thành phần MVC trở nên đơn giản
Để khiến cho Models, Views, Controllers trở nên đơn giản và chuẩn hóa, chúng ta phải liên tục refactor hay tái cấu trúc trên code đã được viết. Trong quá trình tái cấu trúc sẽ không được thay đổi bất kỳ các phản hồi tương tác với bất kỳ hành động nào của người dùng cuối, việc làm đó sẽ giữ cho code dễ đọc, dễ bảo trì, dễ dàng để kiểm thử đem lại nhiều lợi ích cho lập trình viên Tái cấu trúc lại code được thực hiện dựa trên nhiều những quy ước, chúng ta có thể sử dụng một vài mẫu thiết kế hay design patterns bao gồm
- Service Objects (and Interactor Objects)
- Value Objects
- Form Objects
- Query Objects
- View Objects (Serializer/Presenter)
- Policy Objects
- Decorators Chúng ta sẽ đi vào từng mẫu thiết kế trên để có thể tái cấu trúc code được tốt hơn.
Service Objects (and Interactor Objects)
Service Objects được tạo khi một hoạt động:
- phức tạp (giống như tính toán lương của nhân viên)
- kết nối APIs của các dịch vụ bên ngoài
- không thuộc về một model (ví dụ như việc xóa dữ liệu thừa)
- sử dụng nhiều models (ví dụ như nhập dữ liệu từ một tệp vào nhiều model)
Value Objects
Mẫu thiết kế Value Object khuyến khích hướng tới đối tượng object đơn giản (thường thì chỉ chứa giá trị gửi đi) và bạn có thể so sánh chúng dựa trên một logic hay một thuộc tính đặc biệt đơn giản (không phải dựa trên định danh). Ví dụ của values objestc là đối tượng đại diện cho một giá tiền theo đơn vị nào đó. Chúng ta có thể so sánh chúng bằng quy đổi trên một đơn vị chung.
Form Objects
Form Objects là một mẫu thiết kế về sự đóng gói logic liên quan tới xác nhận và đảm bảo dữ liệu. Ví dụ chúng ta có User và Admin, nếu model chứa toàn bộ validation logic, chúng ta sẽ không có thể sử dụng lại chúng cho Admin, khi đó FormObject sẽ được tạo chứa toàn bộ logic validation và model sẽ không còn có trách nhiệm validate dữ liệu nữa. Ban đầu chúng ta có controller và model user như sau
class UsersController < ApplicationController def create @user = User.new(user_params) if @user.save render json: @user else render json: @user.error, status: :unprocessable_entity end end private def user_params params .require(:user) .permit(:email, :full_name, :password, :password_confirmation) end end class User < ActiveRecord::Base EMAIL_REGEX = /@/ # Some fancy email regex validates :full_name, presence: true validates :email, presence: true, format: EMAIL_REGEX validates :password, presence: true, confirmation: true end
Một giải pháp là đưa validation logic vào riêng một lớp có tên là UserForm
class UserForm EMAIL_REGEX = // # Some fancy email regex include ActiveModel::Model include Virtus.model attribute :id, Integer attribute :full_name, String attribute :email, String attribute :password, String attribute :password_confirmation, String validates :full_name, presence: true validates :email, presence: true, format: EMAIL_REGEX validates :password, presence: true, confirmation: true attr_reader :record def persist @record = id ? User.find(id) : User.new if valid? @record.attributes = attributes.except(:password_confirmation, :id) @record.save! true else false end end end
Sau đó chúng ta có thể sử dụng UserForm cho controller như sau
class UsersController < ApplicationController def create @form = UserForm.new(user_params) if @form.persist render json: @form.record else render json: @form.errors, status: :unpocessably_entity end end private def user_params params.require(:user) .permit(:email, :full_name, :password, :password_confirmation) end end
Và User model không còn trách nhiệm validate dữ liệu nữa
class User < ActiveRecord::Base end
Như vậy việc này sẽ giúp cho model trở nên đơn giản hơn, validate logic có thể sử dụng lại được.
Query Objects
Query Objects là một mẫu thiết kế cho phép chúng ta trích xuất query logic từ controller và model thành lớp có thể dùng lại được.
View Objects (Serializer/Presenter)
Một View Objest cho phép chúng ta lấy dữ liệu và tính toán những gì cần cho việc hiển thị một đối tượng của Model lên View, ví dụ như dữ liệu hiển thị lên một trang HTML hay một JSON response từ một API endpoint, việc này không phải của Controller hay Model
Policy Objects
Mẫu thiết kế Policy Objects gần giống với Service Object, nhưng nó chịu trách nhiệm để đọc trong khi Service Object có trách nhiệm để ghi. Policy Objects chứa những quy tắc phức tạp và có thể dễ dàng thay thế bởi Policy Objects với quy tắc khác. Ví dụ chúng ta có thể kiểm tra nếu một user là khách thì có thể lấy về một phần tài nguyên sử dụng bằng một Policy Object cho khách riêng. Nếu user là một admin, chúng ta có thể dễ dàng thay thay thế policy objects này bằng policy object khác chứa quy luật của admin.
Decorators
Mẫu Decorator cho phép chúng ta thêm vào bất kỳ các hành vi tới riêng một đối tượng mà không ảnh hưởng tới đối tượng khác cùng lớp với nó. Mẫu thiết kế này được sử dụng rất rộng rãi để chia các chức năng trên lớp khác nhau, và là một thay thế cho sự phân lớp để đảm bảo theo quy tắc Single Responsibility Principle.
Tổng kết
Như vậy, chúng ta có thể hiểu qua được các khái niệm để giúp chúng ta có thể tái cấu trúc lại code. Các ví dụ khi sử dụng từng mẫu thiết kế trên bạn có thể tham khảo ở liên kết bên dưới. Có nhiều cách để chúng ta có thể làm điều nay nhưng hãy cẩn trọng khi bạn mới bắt đầu với lập trình.
Refs
7 Design Patterns to Refactor MVC Components in Rails