読者です 読者をやめる 読者になる 読者になる

MogLog

メモというか日記というか備忘録というか

『Rubyによるデザインパターン』学習ノート:Composite

Compositeパターン 全体から部分を組立てる

オブジェクト指向でソフトウェアを作ることは、シンプルなオブジェクトを組み合わせて、より複雑なオブジェクトを作るプロセスにほかならない。そうしてできたオブジェクトはより高度なオブジェクトを構築するために使うことができる。このプロセスを経ると、たいていは幾つかの非常に高機能なオブジェクトができる。そしてそれらのオブジェクトは、そのオブジェクトを組み立てるために利用したコンポーネントとは似ても似つかないものになる。
しかし、場合によっては構成するコンポーネントと同じように見え、振る舞うオブジェクトが必要になるときもある。

GoFは、「全体が部分のように振る舞う」という状況を表すパターンをCompositeパターンと呼んでいる。
このパターンは、以下2つのケースで利用できる。
・階層構造やツリー構造のオブジェクトを作りたい時
・ツリーを利用するコードが単純なオブジェクトを扱っているのか、それとも複雑なオブジェクトを扱っているのかを考えさせたくない時

Compositeパターンを作るには3つの部品を使う。
1,全てのオブジェクトの共通のインタフェースまたは基底クラス =>コンポーネント
2,葉クラス
3,コンポジットクラス

# ケーキを作るクラス
# コンポジットクラスがたくさんあることが想定されるので、
# 小タスク管理の詳細は、Compositeという基底クラスを作って管理する

# コンポーネントクラス
class Task
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def get_time_required
  end
end


# 小タスクの管理をするコンポジットクラス
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 AddDryIngrediendsTask < Task
  def initialize
    super('Add dry ingredients')
  end

  def get_time_required
    1.0 # 所要時間1分
  end
end


# 葉クラス
# 混ぜるタスク
class MixTask < Task

  def initialize
    super('Mix that batter up')
  end
 
  def get_time_required
    3.0 #所要時間3分
  end
end


# 葉クラス
# 水を追加するタスク
class AddLiquidsTask < Task
  def initialize
    super('Add liquid')
  end

  def get_time_required
    2.0
  end
end


# コンポジットクラス
# バターを作る
class MakeBatterTask < CompositeTask
  def initialize
    super('Make batter')
    add_sub_task(AddDryIngrediendsTask.new)
    add_sub_task(AddLiquidsTask.new)
    add_sub_task(MixTask.new)
  end
end

#super: superは現在のメソッドがオーバーライドしているメソッドを呼び出す


adit = AddDryIngrediendsTask.new
puts adit.get_time_required # => 1.0

mt = MixTask.new
puts mt.get_time_required # => 3.0

alt = AddLiquidsTask.new
puts alt.get_time_required # => 2.0

mbt = MakeBatterTask.new
puts mbt.get_time_required # => 6.0

コンポーネントクラス: Task
・葉クラス: AddDryIngrediendsTask, MixTask, AddLiquidsTask
・コンポジットクラス: Composite, MakeBatterTask

出力結果を見ると、コンポジットクラスであるMakeBatterTaskクラスは、他の単純なタスクと同じように見えるけれど、実際には3つのタスクから構成されている。これがCompositeパターンの一例。