概要
GoFのデザインパターンのMediatorパターンについて。
多数のメンバー(同僚、Colleague)を利用する処理を調停者(Mediator)を通して行うパターン。
個別のメンバーにばらばらに重複するような処理を持たせずに一か所で管理することで
保守性を向上させる。また、メンバーの追加も容易になる。
登場人物
Mediator = 調停者
ConcreteMediator = 具象調停者
Colleague = 同僚
ConcreteColleague1= 具象同僚1
ConcreteColleague2= 具象同僚2
実装サンプル
サンプル概要
Todo管理を行うHTML生成ツールを想定します。
通常のTodo文字列を表すTodoTextは文字列と実行済みフラグを保持します。
重要なTodo文字列を表すImportantTodoTextは太字で表示され、文字列と実行済みフラグを保持します。
実行済みの場合は取り消し線付きでテキストを表示します。
通常Todo文字列もしくは重要Todo文字列タスクの実行有無が変更された場合
・進捗率を表す数字の表示を更新
・進捗率を表すバーの表示を更新
を行います。
登場人物
Mediator = TodoManager:TODO管理の調停者。
ConcreteMediator = HtmlTodoManager:HTMLによるTODO管理の調停者。
Colleague = TodoParts:Todoの構成要素。同僚役。
ConcreteColleague = TodoText:タスク文字列。
ConcreteColleague1= NormalTodoText:通常タスク文字列。具象同僚1役。
ConcreteColleague2= ImportantTodoText:重要タスク文字列。具象同僚2役。
ConcreteColleague3= TodoProgressNumber:進捗を表す数字(now/maxで表示)具象同僚3役。
ConcreteColleague4= TodoProgressBar:進捗を表すバー。完了部は緑。未完了部は赤で全体の比率を表す。具象同僚4役。
サンプルコード
出力結果
todo_manager
# encoding: Shift_JIS =begin rdoc = TodoManagerクラス =end class TodoManager NOT_OVERRRIDE = 'not override error' def add_todo_parts(todo_parts) raise NOT_OVERRRIDE end def change_complete(todo_parts) raise NOT_OVERRRIDE end end
HtmlTodoManager
# encoding: Shift_JIS require_relative './todo_manager' require_relative './normal_todo_text' require_relative './important_todo_text' =begin rdoc = HtmlTodoManagerクラス =end class HtmlTodoManager < TodoManager attr_accessor :todo_parts_map,:todo_text_list TODO_LIST="todo_list" PROGRESS_NUMBER="progress_number" PROGRESS_BAR="progress_bar" def initialize() @todo_parts_map = Hash.new @todo_text_list = Array.new @todo_parts_map[TODO_LIST] = @todo_text_list end def add_todo_parts(key,todo_parts) if (todo_parts.class == NormalTodoText) || (todo_parts.class == ImportantTodoText) @todo_parts_map[key].push(todo_parts) else @todo_parts_map[key] = todo_parts end end def change_complete() html = "" todo_parts_map[HtmlTodoManager::PROGRESS_NUMBER].update todo_text_list html << todo_parts_map[HtmlTodoManager::PROGRESS_NUMBER].html todo_parts_map[HtmlTodoManager::PROGRESS_BAR].update todo_text_list html << todo_parts_map[HtmlTodoManager::PROGRESS_BAR].html @todo_text_list.each{|todo_parts| if todo_parts.class == NormalTodoText if todo_parts.is_complete todo_parts.html="#{NormalTodoText::COMPLETE_START}#{todo_parts.text}#{NormalTodoText::DIV_END}\n" else todo_parts.html="#{NormalTodoText::NOT_COMPLETE_START}#{todo_parts.text}#{NormalTodoText::DIV_END}\n" end elsif todo_parts.class == ImportantTodoText if todo_parts.is_complete todo_parts.html="#{ImportantTodoText::IMP_COMPLETE_START}#{todo_parts.text}#{ImportantTodoText::IMP_DIV_END}\n" else todo_parts.html="#{ImportantTodoText::IMP_NOT_COMPLETE_START}#{todo_parts.text}#{ImportantTodoText::IMP_DIV_END}\n" end end html << todo_parts.html } puts html end end
TodoParts
# encoding: Shift_JIS =begin rdoc = TodoPartsクラス =end class TodoParts attr_accessor :todo_manager,:is_complete,:html NOT_OVERRRIDE = 'not override error' def initialize(todo_manager) @todo_manager = todo_manager end def update() raise NOT_OVERRRIDE end def set_complete(is_complete) raise NOT_OVERRRIDE end end
TodoText
# encoding: Shift_JIS require_relative './todo_parts' =begin rdoc = TodoTextクラス =end class TodoText < TodoParts attr_accessor :text NOT_OVERRRIDE = 'not override error' def initialize(todo_manager,text) super(todo_manager) @text=text end def set_complete(is_complete) @is_complete=is_complete @todo_manager.change_complete end end
NormalTodoText
# encoding: Shift_JIS require_relative './todo_text' =begin rdoc = NormalTodoTextクラス =end class NormalTodoText < TodoText NOT_COMPLETE_START="<div style='text-decoration:none;'>" COMPLETE_START="<div style='text-decoration:line-through;'>" DIV_END="</div>" end
ImportantTodoText
# encoding: Shift_JIS require_relative './todo_text' =begin rdoc = ImporantTodoTextクラス =end class ImportantTodoText < TodoText IMP_NOT_COMPLETE_START="<strong><div style='text-decoration:none;'>" IMP_COMPLETE_START="<strong><div style='text-decoration:line-through;'>" IMP_DIV_END="</div></strong>" end
TodoProgressNumber
# encoding: Shift_JIS require_relative './todo_parts' =begin rdoc = ProgressNumberクラス =end class ProgressNumber < TodoParts def update(todo_text_list) complete_count=0 uncomplete_count=0 todo_text_list.each{|todo| if todo.is_complete complete_count+=1 else uncomplete_count+=1 end } @html="<div>進捗状況:#{complete_count}/#{complete_count+uncomplete_count}</div>\n" end end
TodoProgressBar
# encoding: Shift_JIS require_relative './todo_parts' =begin rdoc = ProgressBarクラス =end class ProgressBar < TodoParts def update(todo_text_list) complete_count=0 uncomplete_count=0 todo_text_list.each{|todo| if todo.is_complete complete_count+=1 else uncomplete_count+=1 end } complete_percent = complete_count.to_f/(complete_count+uncomplete_count).to_f complete_percent = complete_percent*100 uncomplete_percent = 100-complete_percent @html="<meter min='0' value='#{complete_percent}' max='100'>#{complete_percent}%</meter>" end end
main
# encoding: Shift_JIS require_relative './html_todo_manager' require_relative './todo_parts' require_relative './normal_todo_text' require_relative './important_todo_text' require_relative './progress_number' require_relative './progress_bar' manager = HtmlTodoManager.new progress_number = ProgressNumber.new(manager) manager.add_todo_parts(HtmlTodoManager::PROGRESS_NUMBER,progress_number) progress_bar = ProgressBar.new(manager) manager.add_todo_parts(HtmlTodoManager::PROGRESS_BAR,progress_bar) todo1 = NormalTodoText.new(manager,"normal_todo1") manager.add_todo_parts(HtmlTodoManager::TODO_LIST,todo1) todo2 = NormalTodoText.new(manager,"normal_todo2") manager.add_todo_parts(HtmlTodoManager::TODO_LIST,todo2) important_todo1 = ImportantTodoText.new(manager,"important_todo1") manager.add_todo_parts(HtmlTodoManager::TODO_LIST,important_todo1) important_todo2 = ImportantTodoText.new(manager,"important_todo2") manager.add_todo_parts(HtmlTodoManager::TODO_LIST,important_todo2) todo1.set_complete(false) puts "<hr />" todo2.set_complete(true) puts "<hr />" important_todo1.set_complete(true) puts "<hr />" important_todo2.set_complete(true) puts "<hr />" todo1.set_complete(true) puts "<hr />"
出力
<div>進捗状況:0/4</div> <meter min='0' value='0.0' max='100'>0.0%</meter><div style='text-decoration:none;'>normal_todo1</div> <div style='text-decoration:none;'>normal_todo2</div> <strong><div style='text-decoration:none;'>important_todo1</div></strong> <strong><div style='text-decoration:none;'>important_todo2</div></strong> <hr /> <div>進捗状況:1/4</div> <meter min='0' value='25.0' max='100'>25.0%</meter><div style='text-decoration:none;'>normal_todo1</div> <div style='text-decoration:line-through;'>normal_todo2</div> <strong><div style='text-decoration:none;'>important_todo1</div></strong> <strong><div style='text-decoration:none;'>important_todo2</div></strong> <hr /> <div>進捗状況:2/4</div> <meter min='0' value='50.0' max='100'>50.0%</meter><div style='text-decoration:none;'>normal_todo1</div> <div style='text-decoration:line-through;'>normal_todo2</div> <strong><div style='text-decoration:line-through;'>important_todo1</div></strong> <strong><div style='text-decoration:none;'>important_todo2</div></strong> <hr /> <div>進捗状況:3/4</div> <meter min='0' value='75.0' max='100'>75.0%</meter><div style='text-decoration:none;'>normal_todo1</div> <div style='text-decoration:line-through;'>normal_todo2</div> <strong><div style='text-decoration:line-through;'>important_todo1</div></strong> <strong><div style='text-decoration:line-through;'>important_todo2</div></strong> <hr /> <div>進捗状況:4/4</div> <meter min='0' value='100.0' max='100'>100.0%</meter><div style='text-decoration:line-through;'>normal_todo1</div> <div style='text-decoration:line-through;'>normal_todo2</div> <strong><div style='text-decoration:line-through;'>important_todo1</div></strong> <strong><div style='text-decoration:line-through;'>important_todo2</div></strong> <hr />