Composite
Rubyによるデザインパターン生活、、4日目。
#しばらく飲み会続きでお休みしていました。
、、継続します。
- 作者: Russ Olsen,ラス・オルセン,小林健一,菅野裕,吉野雅人,山岸夢人,小島努
- 出版社/メーカー: ピアソン桐原
- 発売日: 2009/04/01
- メディア: 単行本
- 購入: 12人 クリック: 193回
- この商品を含むブログ (58件) を見る
本日はCompositeパターンを取り上げます。
僕の中では、Compositeというと「集約」というイメージがあります。オブジェクト思考とかで。
Rubyによるデザインパターン本では「部分から全体を組み立てる」と紹介しています。
そんじゃ見ていきます。
本の中では、ケーキを作るというプロセスをツリー構造に分解しています。
詳細化したタスクをCompositeパターンで処理しよう!ってかんじ。
- MakeCake(ケーキを作る)
- Make Batter
- AddDryIngredients
- AddLiquids
- Mix
- Package Cake(ケーキを箱詰めにする)
- 省略
- Make Batter
みたいなかんじで、ケーキを作るための作業を詳細化しています。
Compositeパターンでは3つの部品を使います。
- すべてのオブジェクトの基底となるクラス(Component)
- 葉(Leaf)となるクラス
- 小要素をもつ複合的なクラス(Composite)
今回登場するクラス。
- Task
- 基底となるクラス(Component)
- AddDryIngredientsTask
- 単体の作業を表すクラス(Leaf)
- AddLiquidsTask
- 単体の作業を表すクラス(Leaf)
- MixTask
- 単体の作業を表すクラス(Leaf)
- MakeBatterTask
- 複数の作業によって構成されるクラス(Composite)
それでは、コードを見てみます。
まずは、Taskクラスです。
class Task attr_accessor :name def initialize(name) @name = name; end def get_time_required 0.0 end end
属性nameとget_time_requiredメソッドを定義しています。
get_time_requiredメソッドは、作業に必要な時間を返します。
Taskクラスは抽象的なクラスのため、作業時間は0を返しています。
#結果としてサブクラスでオーバーライドすることになります。
つづいて、AddDryIngredientsTaskです。
class AddDryIngredientsTask < Task def initialize super('Add dry ingredients') end def get_time_required 1.0 end end
Taskクラスを継承しています。get_time_requiredメソッドをオーバーライドしています。
AddDryIngredientsTaskクラスはCompositeパターンにおけるLeafにあたるクラスです。
そのため小要素は持たず、自身の作業だけを遂行します。
次に紹介するMixTask、AddLiquidsTaskクラスも同様のLeafにあたるクラスです。
class MixTask < Task def initialize super('Mix that batter up!') end def get_time_required 3.0 end end
class AddLiquidsTask < Task def initialize super('Add Liquids task') end def get_time_required 5.0 end end
つづいて、AddDryIngredientsTask、MixTask、AddLiquidsTaskによって構成されるMakeBatterTaskクラスです。
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
MakeBatterTaskクラスはCompositeパターンにおけるCompositeにあたるクラスです。
自身は小要素となるAddDryIngredientsTask、MixTask、AddLiquidsTaskオブジェクトを保持します。
作業時間を求めるget_time_requiredメソッドでは、各小要素に対して作業時間を問い合わせ、合計した結果を返します。
最後に呼び出しもととなるプログラムです。
task = MakeBatterTask.new
puts task.name
puts task.get_time_required
実行結果です。
Make batter 9.0
と、こんなかんじです。
あとは、
MakeBatterTaskクラスからCompositeにあたる部分をCompositeTaskクラスとして切り出すこともできます。
class CompositeTask < Task def initialize(name) super(name) @sub_tasks = [] end def add_sub_task(task) @sub_tasks << task task.parent_task = self 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
するとMakeBatterTaskはシンプルになります。
class MakeBatterTask < CompositeTask def initialize super('Make batter') add_sub_task(AddDryIngredientsTask.new) add_sub_task(MixTask.new) add_sub_task(AddLiquidsTask.new) end end
また、CompositeTaskには配列を操作するような演算子メソッドを追加すると便利です。
<<メソッドや、[]メソッドなど。
class CompositeTask < Task def initialize(name) super(name) @sub_tasks = [] end def add_sub_task(task) @sub_tasks << task task.parent_task = self end def <<(task) add_sub_task(task) end def remove_sub_task(task) @sub_tasks.delete(task) end def [](index) @sub_tasks[index] end def []=(index, new_value) @sub_tasks[index] = new_value end def total_number_basic_tasks total = 0 @sub_tasks.each { |task| total += task.total_number_basic_tasks } total end def get_time_required time = 0.0 @sub_tasks.each {|task| time += task.get_time_required} time end end
以上、おしまい。