0

Design Pattern - Composite

Xây dựng phần mềm hướng đối tượng là một quá trình thu thập các object đơn giản (các object này chỉ gồm các thành phần đơn giản như integer và string), rồi kết hợp thành những object phức tạp hơn như các hồ sơ nhân sự hay danh sách các bài hát.

Tuy nhiên đôi khi chúng ta lại muốn xây dựng một object phức tạp nhưng object này lại giống hệt những thành phần mà chúng ta sử dụng để xây dựng nó. Nó giống với quy trình làm bánh (Menufacture cake) trong hình sau: Menufacture cake.png

Quy trình Manufacture Cake bao gồm Make CakePackage Cake. Make CakePackage Cake gồm nhiều quy trình nhỏ hơn. Những quy trình này chính là các object phức tạp mà chúng ta muốn xây dựng vì những quy trình này đều giống nhau, quy trình mức cao hơn thì chứa các quy trình mức thấp hơn. Ta có thể dễ dàng implement các quy trình trên 1 interface với tên quy trình (name), thời gian hoàn thành (time) và chứa các quy trình con (subtasks).

Xây dựng Composite

GoF gọi design pattern "các hoạt động tổng thể theo các thành phần con" là Composite pattern.

Tức là, khi cần xây dựng 1 hệ thống hoặc một cây các đối tượng mà không muốn khi code phải lo lắng xem liệu nó cần phải giải quyết một đối tượng đơn lẻ hay tổng thể cả một cây đối tượng thì ta cần sử dụng tới Composite pattern

Để xây dựng Composite pattern, ta cần 3 thành phần:

Composite pattern.png

  • Đầu tiên, cần một infterface chung hoặc base class cho toàn bộ các đối tượng. (nó gọi là component). Khi xây dựng component hay nghĩ tới các điểm chung giữa các đối tượng cơ bản và các đối tượng ở mức cao hơn, Giống như việc làm bánh, các task đơn giản như ước lượng bột và task phức tạp như làm bột đều tiêu tốn thời gian.

  • Thứ hai, ta cần một hoặc nhiều leaf class - là các bước không thể phân chia được nữa (là bước ước lượng bột và thêm trứng). Các leaf class nên được implement Component interface.

  • Thứ ba, ta cần ít nhất một class ở mức cao hơn, gọi là composite class. Composite chính là một component, nhưng nó là đối tượng ở mức cao hơn được build từ các subcomponent. Trong ví dụ làm bánh thì composite là những task phức tạo như làm bột hoặc quy trình làm cả chiếc bánh.

#component base class
class Task
  attr_reader :name
  def initialize(name)
    @name = name
  end
  def get_time_required
    0.0
  end
end

2 leaf class

class AddDryIngredientsTask < Task
  def initialize
    super('Add dry ingredients')
  end
  def get_time_required
    1.0
    # 1 minute to add flour and sugar
  end
end

class MixTask < Task
  def initialize
    super('Mix that batter up!')
  end
  def get_time_required
    3.0
    # Mix for 3 minutes
  end
end

Task ở mức cao hơn (composite):

class MakeBatterTask < Task
  def initialize
    super('Make batter')
    @sub_tasks = []
    add_sub_task( AddDryIngredientsTask.new )
    add_sub_task( AddLiquidsTask.new )
    add_sub_task( MixTask.new )
  end
  def add_sub_task(task)
    @sub_tasks << task
  end
  def remove_sub_task(task)
    @sub_tasks.delete(task)
  end
  def get_time_required
    time=0.0
    @sub_tasks.each {|task| time +=task.get_time_required}
    time
  end
end

class MakeBatterTask chính là 1 task composite, ngoài ra chúng ta còn cần xây dựng các task composite khác như packaging the cakemanufacturing the cake. Các task này cũng giống với MakeBatterTask nhưng nó bao gồm các subclass khác nhưng xử lý trong class là tương đương. Vì thế chúng ta có thể tạo 1 base class cho những task composites

class CompositeTask < Task
  def initialize(name)
    super(name)
    @sub_tasks = []
  end
  def add_sub_task(task)
    @sub_tasks << task
  end
  def remove_sub_task(task)
    @sub_tasks.delete(task)
  end
  def get_time_required
    time=0.0
    @sub_tasks.each {|task| time += task.get_time_required}
    time
  end
end

class MakeBatterTask < CompositeTask
  def initialize
    super('Make batter')
    add_sub_task( AddDryIngredientsTask.new )
    add_sub_task( AddLiquidsTask.new )
    add_sub_task( MixTask.new )
  end
end

class MakeCakeTask < CompositeTask
  def initialize
    super('Make cake')
    add_sub_task( MakeBatterTask.new )
    add_sub_task( FillPanTask.new )
    add_sub_task( BakeTask.new )
    add_sub_task( FrostTask.new )
    add_sub_task( LickSpoonTask.new )
  end
end

Tham khảo

Github (updating):https://github.com/ducnhat1989/design-patterns-in-ruby

Sách: “DESIGN PATTERNS IN RUBY” của tác giả Russ Olsen

Bài viết liên quan:


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí