7
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rubyデザインパターン 4日目 : Composite

Posted at

Rubyデザインパターン学習のために、自分なりに読書の結果をまとめていくことに決めました。第4日目はCompositeです。(http://www.amazon.co.jp/gp/product/4894712857/ref=as_li_qf_sp_asin_tl?ie=UTF8&camp=247&creative=1211&creativeASIN=4894712857&linkCode=as2&tag=morizyun00-22)

スクリーンショット 2015-07-27 11.25.28.png

 4日目 Composite

4日目はCompositeパターンです。
このパターンの特徴は、ツリーや階層構造を作るときに便利なパターンになります。

階層、ツリー構造のオブジェクトをつくりたいとき、そのツリーを利用するコードが一つのオブジェクトを扱っているのか、ごちゃごちゃした枝全体を扱っているのか考えさせたくないときに便利です。

具体的なコードに移ります。ケーキ屋さんがケーキを作る工程を表現するプログラムを例にあげます。

構成要素として必要な物は以下の3点です

  1. コンポーネント(基底クラス
  2. 子クラス(小麦粉の計量、卵の投入などそれ以上機能を分離できない最も小さな要素
  3. コンポジット(子クラスをまとめあげるもの

Composite サンプルコード

class Task
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def get_time_required
    0.0
  end
end

class AddDryIngredientsTask < 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
  end
end

class MakeBatterTask < Task
  def initialize
    super('Make batter')
    @sub_tasks = []
    add_sub_task( AddDryIngredientsTask.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

batter = MakeBatterTask.new
batter.get_time_required # => 4.0

MakeBatterTaskでは、子クラスを管理するためのメソッドを定義してあります。具体的には以下のふたつです。

  • add_sub_task
  • remove_sub_task

このふたつのメソッドで@sub_tasks配列に必要な子タスクを出し入れします。
add_sub_taskで追加された要素は、オーバーライドされたget_time_requiredで各々のクラスで定義された作業時間を合計し、出力します。
新しい子クラスを作ったときは、@sub_tasksに入れて扱う事で、等価に増やすことができます。

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( FilePanTask.new )
    add_sub_task( BakeTask.new )
    add_sub_task( FrostTask.new )
    add_sub_task( LickSpoonTask.new )
  end
end

そして、最後はこういった形でコンポジットが子クラスをまとめあげることができるように、コンポジットがコンポジットをまとめあげることができます。

再帰的な実装にしてあるので、階層がどれだけ深くなっても対応できるようになっています。

def get_time_required
  time = 0.0
  @sub_tasks.each {|task| time += task.get_time_required }
  time
end

 まとめ

作業を定義する具体的なクラスも、それをまとめあげて管理するクラスも似たようなメソッドを持っており、扱う側からどちらも等価なものとして扱いたいときに有効です。
例えると、ディレクトリとファイルの関係のように、親ディレクトリを削除したときに所属するディレクトリもファイルも削除する、といったシステムにも向いているかもしれません。
継承ベースであるTemplate Methodの、再帰的なバージョンとも言えるかもしれませんね。

参照

7
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?