Chain of Responsibilityパターン
Chain of Responsibilityパターンは、あるオブジェクトのメソッドで処理できなかった場合は別オブジェクトのメソッドに処理を投げる、という仕組みを実装するデザインパターンです。
自分なりにChain of Responsibilityパターンをクラス図にまとめてみました。
Chain of Responsibilityパターンのクラス図
Chain Of Responsibilityパターンの解説
抽象クラスのHandlerが自分と同じHandler型の属性(図では属性名next)を持つ。HandlerクラスのsetNextメソッドでは、引数に渡されるHandlerオブジェクトを属性nextにセットする処理を行う。
抽象メソッド(図ではfunc)は、Handlerを継承する具象クラスHandler1,2,3で実装する。
具象クラスHandler1,2,3のfuncメソッドでは、任意の処理を行うが、例えばその結果がfalseになる場合など、ある条件を満たさなかった場合には、次クラス(属性のnext)のfuncメソッドを呼び出す、という処理にすることで、Handler1で処理できなかったものはHandler2へ、Handler2で処理できなかったものはHandler3へ、と処理を渡していくことができる。
このようにすることで、Client側からHandler型のfuncメソッドを呼び出せば、そのあとの処理はHandler型が連鎖しながら処理を行ってくれることになるため、Client内で全ての場合の処理を記述する必要性がなくなる。
ただし、自分で処理できなかった場合の次のHandlerクラスはどれなのかをどこかしらで指定する必要がある、つまり責任の連鎖の順番を指定する必要がある。それをClientクラスで行ってしまうと、結局連鎖の順番をClientが意識しておく必要があるということにはなる。
なお、funcメソッドが、「funcメソッドの実行結果がfalseだった場合はnextのfuncを呼び出す」というように各Handlerクラスで共通化できる場合は、抽象クラスのHandlerにfuncメソッドを共通化し、具象クラスではmyfunc()メソッドのように各々の処理を実装し、funcメソッドでmyfuncを呼び出すような形にしてもよい。
Chain Of Responsibilityパターンをどういう場合に使うか
入力値チェックを行う場合、ログの出力を行う場合などに使える。
入力値のケースを考えてみよう。例えば、未入力チェック→最大文字数チェック→形式チェックという流れでエラーチェックを行う場合、各々のチェック用のクラスを作成し(図のHandler1,2,3にあたる)、そのvalidateメソッド(図のfuncメソッドにあたる)で、未入力チェックの場合、未入力だったら「入力してください」というエラーメッセージを生成し、未入力でなかった場合は次の最大文字数チェックのvalidateメソッドを呼び出す、という形になる。
PlantUMLでChain Of Responsibilityパターンのクラス図を記述する
文頭のChain Of Responsibilityパターンのクラス図をPlantUMLで書いたときのソースです。
@startuml Chain Of Responsibility
class Client{
}
abstract class Handler{
# next : Handler
--
# abstract func()
+ setNext(Handler)
}
class Handler1 extends Handler{
+ func()
}
class Handler2 extends Handler{
+ func()
}
class Handler3 extends Handler{
+ func()
}
Client -r-> Handler : >
Handler o-> Handler
@enduml
参考サイトURL
Chain of Responsibility パターン – Wikipedia
PHPによるデザインパターン入門 – Chain of Responsibility〜処理のたらい回し