Mediatorパターン(GoF)
参考書籍一覧(Amazon アソシエイトリンク)
インスタンス同士で協調して処理しなければならないが、インスタンス同士で依存関係を持ちたくない場合に使うパターン。
パート従業員のシフト管理を行うシステムを考える。常に2人の勤務でシフトを組まなければならないとする。
class PartTimeWorker
def initialize(parts)
@parts = parts
end
# 勤務予定を変更する処理
def change_part(day, from_time, to_time)
@parts = @parts.reject { |part| part.day == day }
@parts << Part.new(day, from_time, to_time)
end
end
class Part
attr_accessor :day, :from_time, :to_time
# ...
end
パートタイム従業員個人の処理はこれで問題はない。しかし、「常に2人の勤務でシフトを組まなければならない」という調整ができていない。
以下のように他の従業員を持つことで要求は満たせる。
class PartTimeWorker
def initialize(parts, part_time_workers)
@parts = parts
@part_time_workers = part_time_workers
end
# 勤務予定を変更する処理
def change_part(day, from_time, to_time)
# パート全員の勤務予定を確認している
worker_count = @part_time_workers.count do |worker|
worker.work?(day, from_time, to_time)
end
raise 'シフトが埋まっている' if 2 <= worker_count
@parts = @parts.reject { |part| part.day == day }
@part << Part.new(day, from_time, to_time)
end
def work?(day, from_time, to_time)
# 指定時間に働いているか?
end
end
しかし、さらに「社員が2人以下しか勤務できない場合はパートは3人シフトになる」「繁忙期は社員3人、パート4人のシフトになる」といった追加要求が発生した場合に非常に面倒なことになる。
PartTimeWorker
クラスは社員(Employee
クラス)の勤務状況を知らなければならないし、繁忙期の情報を知らなければいけなくなる。Employee
クラスにおいても繁忙期の情報は知らなければいけない上、その処理はほとんどPartTimeWorker
クラスと同じはずである。
そこでMediatorパターンを使う。
class ScheduleMediator
def initialize(part_time_workers, employees, busy_periods)
@part_time_workers = part_time_workers
@employees = employees
@busy_periods = busy_periods
end
# パートのシフトが埋まっているか
def part_time_workers_shift_full?(day, from_time, to_time)
require_count = headcount_of_require_part_time_workers(day, from_time, to_time)
worker_count = headcount_of_part_time_workers(day, from_time, to_time)
require_count <= worker_count
end
# 社員のシフトが埋まっているか
def employees_shift_full?(day, from_time, to_time)
require_count = headcount_of_require_employees(day, from_time, to_time)
worker_count = headcount_of_employees(day, from_time, to_time)
require_count <= worker_count
end
# 繁忙期か?
def on_busy_period?(day, from_time, to_time)
# 繁忙期ならtrue
end
# 指定時間に勤務予定のパート人数
def headcount_of_part_time_workers(day, from_time, to_time)
@part_time_workers.count { |worker| worker.work?(day, from_time, to_time) }
end
# 指定時間に勤務予定の社員人数
def headcount_of_employees(day, from_time, to_time)
@employees.count { |worker| worker.work?(day, from_time, to_time) }
end
# 指定時間に必要なパート人数
def headcount_of_require_part_time_workers(day, from_time, to_time)
return 4 if on_busy_period?(day, from_time, to_time)
return 3 if headcount_of_employees(day, from_time, to_time) <= 2
2
end
# 指定時間に必要な社員人数
def headcount_of_require_employees(day, from_time, to_time)
return 3 if on_busy_period?(day, from_time, to_time)
0
end
end
class PartTimeWorker
# mediatorはScheduleMediatorのインスタンス
def initialize(parts, mediator)
@parts = parts
@mediator = mediator
end
# 勤務予定を変更する処理
def change_part(day, from_time, to_time)
if @mediator.part_time_workers_shift_full?(day, from_time, to_time)
raise 'シフトが埋まっている'
end
@parts = @parts.reject { |part| part.day == day }
@part << Part.new(day, from_time, to_time)
end
def work?(day, from_time, to_time)
# 指定時間に働いているか?
end
end
これによって、PartTimeWorker
クラスは社員や繁忙期などを気にする必要がなくなった。参照しなければならない情報が増えたり条件が変更されたとしても、それらの修正はScheduleMediator
クラスだけで済む。