Processor Model Design Pattern

Bình thường, khi 1 dự án phát triển đến 1 mức độ nào đó, các model sẽ có xu hướng trở nên phức tạp. Lúc này chúng ta cần xem xét 1 vài "chiến lược" để kiểm soát tình hình và đặt mọi thứ trong tầm kiểm soát.

Background

Với rails, chúng ta có ActiveRecord, một class trộn lẫn tính logic và bền vững. Điều này dặc biệt thuận lợi khi bắt đầu một ứng dụng, nhưng đáng tiếc nó lại vi phạm nghiêm trọng nguyên tắc đơn nhiệm "Single Responsibility Principle."

Việc biến 1 dự án lớn thành các phần riêng biệt với các vai trò rõ ràng là 1 ý tưởng tốt.

Creating a Processor Object

1 processor object là 1 đối tượng chỉ liên quan tới việc thao tác dữ liệu của các object khác.

Cách viết rất đơn giản:

class MyProcessor
  def initialize(thing, stuff)
    @thing = thing
    @stuff = stuff
  end
end

Where Does It Live?

Bạn có thể lưu trữ các processor object của mình vào app/models, nhưng nếu bạn muốn phân tách chúng rõ ràng hơn thì có thể tạo app/lib và đặt chúng ở đó. Bất kỳ thư mục nào bổ sung trong app/ sẽ được thêm vào đường dẫn tải tự động khi server bắt đầu chạy, do đó cứ tạo ra các thư mục bất cứ khi nào chúng có ý nghĩa cho việc tổ chức các project của chúng ta.

Practical Techniques

Một processor object chủ yếu sử dụng các hàm của ruby, tuy nhiên cũng có một số phương pháp khiến việc sử dụng chúng dễ dàng hơn

attr_reader

attr_reader, viết tắt của "Attribute Reader", sẽ tạo ra 1 instance variable và phương thức truy xuất cho bạn:

class MyClass
  attr_reader :my_attribute

  # That's the same as doing this...

  def my_attribute
    @my_attribute
  end
end

Như vậy, khi bạn tạo thuộc tính đó từ bên ngoài, nó chỉ có thể được đọc. Nếu muốn thay đổi nó, bạn cần dùng attr_accessor, mặc dù nó vi phạm tính đóng gói của object con.

Bạn cũng có thể kết hợp nhiều thuộc tính trong attr_reader như sau:

attr_reader :first_attribute, :second_attribute

delegate

Theo luật Demeter, chúng ta có thể nói chuyện với 1 object nhưng không nên nói chuyện trực tiếp với con của nó.

Hãy tưởng tượng, chúng ta có một instance của class Plane: @plane. Khi chúng ta muốn engines bắt đầu, có thể chúng ta sẽ viết thế này:

@plane.engines.each{|e| e.start}

Nhưng kiến thức về @plane có liên quan gì đến các engine của nó? Chúng ta đang phá vỡ tính đóng gói của class Plane

Thay vào đó, chúng ta sẽ nói cho máy bay biết phải làm gì:

@plane.start_engines

Việc này có nghĩa chúng ta nói cho @plane biết về việc khởi động engines.

Tại sao điều này liên quan processor objects? Khi bạn tạo ra 1 đối tượng, bạn thường muốn làm việc trên các attributes và method của các đối tượng con của nó. ĐỪNG làm điều này:

@my_object.child.the_method

Hãy thay thế bằng:

@my_object.the_method

Vậy làm thế nào để nó làm việc?

class MyObject
  attr_reader :child

  def the_method
    child.the_method
  end
end

Nếu có nhiều object con với nhiều method, hãy sử dụng delegate:

class MyObject
  attr_reader :child
  delegate :the_method, to: child
end

Bạn có thể delegate nhiều method 1 lúc:

class MyObject
  attr_reader :child
  delegate :the_method, :second_method, :third_method, to: child
end

Bài viết dịch từ http://tutorials.jumpstartlab.com/topics/models/processor_models.html


All Rights Reserved