Basic design patterns in Ruby on Rails
Không chỉ trong công việc lập trình, trong cuộc sống hàng ngày, chúng ta đều gặp một số vấn đề. Mọi người đều có những khó khăn gần tương tự nhau, và xã hội chúng ta đã tìm ra những cách thức, khuôn mẫu phổ quát để giải quyết những vấn đề đó. Và xét trên khía cạnh lập trình những phương pháp, khuôn ...
Không chỉ trong công việc lập trình, trong cuộc sống hàng ngày, chúng ta đều gặp một số vấn đề. Mọi người đều có những khó khăn gần tương tự nhau, và xã hội chúng ta đã tìm ra những cách thức, khuôn mẫu phổ quát để giải quyết những vấn đề đó. Và xét trên khía cạnh lập trình những phương pháp, khuôn mẫu phổ quát đó được gọi là pattern. Vậy design pattern là gì? theo wikipedia thì:
Software design pattern là một giải pháp có thể tái sử dụng, dùng để giải quyết một vấn đề chung thường xảy ra trong một hoàn cảnh nhất định trong software design. Design Pattern không phải là một thiết kế hoàn chỉnh để có thể chuyển đổi trực tiếp thành source code hay machine code. Mà nó là một mô tả hoặc template cho cách giải quyết vấn đề có thế được sử dụng trong nhiều tình huống khác nhau. Các design pattern được hình thức hoá tốt nhất để lập trình viên có thể sử dụng để giải quyết các vấn đề phổ biến khi thiết kế một ứng dụng hoặc hệ thống
Nói một cách đơn giản, design pattern là các mẫu thiết kế có sẵn, dùng để giải quyết một vấn đề. Áp dụng mẫu thiết kế này sẽ làm code dễ bảo trì, dễ mở rộng hơn
Ngày nay có khá nhiều design pattern được sử dụng và áp dụng rộng dãi nhưng chúng ta có thể chia thành 3 nhóm chính:
- Creational Design Pattern: Liên quan đến việc khởi tạo object. VD: Factory, Object Pool, Abstract Factory, Builder.
- Structure Design Pattern: Liên quan đến kết cấu, liên hệ giữa các object. VD: Adapter, Bridge, Decorator, Proxy, Composite, Facede.
- Behavioral Design Pattern: Liên quan tới hành vi của các object. VD: Iterator, Mementor, Strategy, Template Method, Visitor.
Tôi đã chọn hai design pattern tôi thấy phổ biến nhất trong Rails là service và decorators. Lý do chính khiến chúng phổ biến là chúng giúp giữ cho model nhẹ và DRY bằng cách di chuyển một phần lớn logic vào các phần khác của application. Và tất nhiên các model nhẹ là các model tốt, vậy nên chúng ta sẽ cùng tìm hiểu rõ hơn hai design pattern!
Service
Service là design pattern được sử dụng nhiều nhất trong các Rails Application. Ý tưởng của déign pattern này rất đơn giản - Nếu một phần logic nào đó không thực phù hợp để viết trong model hay controller thì tốt nhất bạn nên đưa nó vào service. Bạn có thể sử dụng service của mình ở nhiều nơi như models, controllers, jobs,... và điều đó sẽ giúp ứng dụng của bạn clean & DRY
app/ controllers/ ... services/ base_service.rb single_service.rb some_group_of_services/ first_service.rb second_service.rb
Bây giờ chúng ta sẽ toạ một base_service để có thể chắc chắn rằng tất cả service của chúng ta sử dụng cùng một interface để giao tiếp với phần còn lại của application.
class BaseService private_class_method :new def self.call(*args) new(*args).call end end
class YourService < BaseService attr_accessor :first_variable, second_variable def initialize(first_variable, second_variable) self.first_variable = first_variable self.second_variable = second_variable end def perform # method body end private end
Method initialize là bắt buộc và cần thiết cho mỗi service, và method perfrom là nơi thực hiện những logic của service. Nếu phần logic trong method perform quá dài và phức tạp, bạn nên tách ra thành nhiều method private. Bởi vì service chỉ nên có một method available bên ngoài service là method perform.
Decorator
Trong OOP, decorator cho chúng ta khả năng mở rộng hành vi của một object cụ thể bằng cách cung cấp cho nó một số các method mở rộng. Trong Ruby on Rails, chúng ta thường sử dụng design pattern này với gem draper.
Draper rất hữu dụng khi chúng ta có những method trong models, mà chúng chỉ được sử dụng ở phần view. Sử dụng decorator design pattern nghĩa là đặt toàn bộ các logic chỉ sử dụng trên view vào decorator. Vậy, nếu trong model của chúng ta có một method full_name như sau:
def full_name "#{first_name} #{last_name}" end
Thì chúng ta nên chuyển chúng vào UserDecorator.
class UserDecorator < Draper::Decorator delegate_all def full_name "#{object.first_name} #{object.last_name}" end end
Và điều tuyệt nhất là không phải mọi User Object đều có method này, chúng ta phải "decorate" object này trước khi chuyển xuống view bằng cách rất đơn giản: user.decorate. Vậy nên trong controller chúng ta cần phải dùng:
def show @user = User.find(params[:id]).decorate end And then, we can use this code in our views: <%= @user.full_name %>
Trên đây là 2 design pattern mình muốn giới thiệu trong bài viết này. Tất nhiên vẫn còn rất rất nhiều design pattern khác và không phải tất cả chúng đều được áp dụng trong Rails Application. Nhưng vẫn rất tốt nếu nếu biết một số design pattern hoặc chỉ là ý tưởng chính của design pattern đó, bởi vì có thể một ngày nào đó bạn gặp phải một vấn đề và nó có thể giải quyết bằng một design pattern mà bạn biết. Hoặc thậm chí phát triển một design pattern của riêng bạn