12/08/2018, 17:12

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             </div>
            
            <div class=

0