はじめに
本エントリーは某社内で実施するデザインパターン勉強会向けの資料となります。
本エントリーで書籍「Java言語で学ぶデザインパターン入門」をベースに学習を進めますが、サンプルコードはC#に置き換えて解説します。
第1回:Iteratorパターン
第2回:Adapterパターン
第3回:Template Methodパターン
第4回:Factory Methodパターン
第5回:Singletonパターン
第6回:Prototypeパターン
第7回:Builderパターン
第8回:Abstract Factoryパターン
第9回:Bridgeパターン
第10回:Strategyパターン
第11回:Compositeパターン
第12回:Decoratorパターン
第13回:Visitorパターン
ChainOfResponsibilityパターンとは
複数のオブジェクトをチェーン(鎖)のようにつないでおき、要求を処理することが可能なオブジェクトにわたるまで、要求を受け流していくパターンです。
サンプルプログラムのクラス図
ChainOfResponsibilityパターンを使用したサンプルプログラムを紹介します。
各クラスの役割
クラス名 | 役割 |
---|---|
Issue | 発生した問題を表すクラス。問題管理番号(IssueNumber)を持つ |
Support | 問題解決のサポートを行う抽象クラス |
LimitSupport | 問題解決のサポートを行う具象クラス(指定した番号未満の問題を解決) |
OddSupport | 問題解決のサポートを行う具象クラス(奇数番号の問題を解決) |
SpecialSupport | 問題解決のサポートを行う具象クラス(特定の問題を解決) |
Program | Supportたちの連鎖を作り、問題を発生させる動作テスト用クラス |
Issueクラス
Issueクラスは、発生した問題を表現するクラスです。IssueNumberは問題を管理する番号です。
コンストラクタで、IssueNumberを設定します。
namespace ChainOfResponsibilitySample
{
public class Issue
{
public int IssueNumber { get; private set; }
public Issue(int issueNumber)
{
IssueNumber = issueNumber;
}
}
}
Supportクラス
Supportクラスは、問題を解決する連鎖を作るための抽象クラスです。
_nextSupportフィールドは、次の問題解決先を差します。
SetNextSupportメソッドは、次の問題解決先を設定します。
Resolveメソッドは、サブクラスで実装することを想定した抽象メソッドです。戻り値がtrueのときは要求が処理されたことを表し、falseのときは要求はまだ処理されていない(次のサポート先に問題解決を任せる)ことを表します。
SupportIssueメソッドは、Resolveメソッドを呼び出し、戻り値がfalseなら、次のサポートに問題解決を任せます。 次のサポートがいない場合は、連鎖の最後となり、サポートできなかった旨を通知するメッセージを出力します。
using System;
namespace ChainOfResponsibilitySample
{
public abstract class Support
{
private string _staffName;
private Support _nextSupport;
public Support(string staffName)
{
_staffName = staffName;
}
public Support SetNextSupport(Support nextSupport)
{
return _nextSupport = nextSupport;
}
public void SupportIssue(Issue issue)
{
if (Resolve(issue))
{
Console.WriteLine("問題" + issue.IssueNumber + "は" + _staffName + "が解決しました。");
}
else if (_nextSupport != null)
{
_nextSupport.SupportIssue(issue);
}
else
{
Console.WriteLine("問題" + issue.IssueNumber + "は現在のサポート体制では解決できませんでした。");
}
}
protected abstract bool Resolve(Issue issue);
}
}
LimitSupportクラス
LimitSupportクラスは、limitIssueNumberで指定した問題管理番号未満の問題を解決するクラスです。
namespace ChainOfResponsibilitySample
{
public class LimitSupport : Support
{
private int _limitIssueNumber;
public LimitSupport(string staffName, int limitIssueNumber) : base(staffName)
{
_limitIssueNumber = limitIssueNumber;
}
protected override bool Resolve(Issue issue)
{
return issue.IssueNumber < _limitIssueNumber;
}
}
}
OddSupportクラス
OddSupportクラスは、問題管理番号が奇数の問題を解決するクラスです。
namespace ChainOfResponsibilitySample
{
public class OddSupport : Support
{
public OddSupport(string staffName) : base(staffName)
{
}
protected override bool Resolve(Issue issue)
{
return issue.IssueNumber % 2 == 1;
}
}
}
SpecialSupportクラス
SpecialSupportクラスは、指定した問題管理番号のみ問題を解決するクラスです。
namespace ChainOfResponsibilitySample
{
public class SpecialSupport : Support
{
private int _specialNumber;
public SpecialSupport(string staffName, int specialNumber) : base(staffName)
{
_specialNumber = specialNumber;
}
protected override bool Resolve(Issue issue)
{
return issue.IssueNumber == _specialNumber;
}
}
}
Programクラス
動作確認クラスでは、まず各サポートのインスタンスを作成しています。次にSetNextSupportメソッドを使用して、サポートの順番を設定しています。
そして、6件の問題を発生させ、LimitSupportであるNagatomoさんに1次処理をお願いしています。
using System;
namespace ChainOfResponsibilitySample
{
public class Program
{
public static void Main(string[] args)
{
Support limitSupport = new LimitSupport("Nagatomo", 3);
Support oddSupport = new OddSupport("Kagawa");
Support SpecialSupport = new SpecialSupport("Honda", 4);
limitSupport.SetNextSupport(oddSupport).SetNextSupport(SpecialSupport);
var IssueCount = 6;
// 6件の問題が発生
for (int i = 1; i <= IssueCount; i++)
{
limitSupport.SupportIssue(new Issue(i));
}
Console.ReadKey();
}
}
}
実行結果
実行結果は以下のようになります。
問題1はNagatomoが解決しました。
問題2はNagatomoが解決しました。
問題3はKagawaが解決しました。
問題4はHondaが解決しました。
問題5はKagawaが解決しました。
問題6は現在のサポート体制では解決できませんでした。
問題1,2は3未満のため、LimitSupportで、Nagatomoが解決できます。
問題3はNagatomoでは解決できないため、解決処理の責任がKagawaに移ります。OddSupportのKagawaは、問題3は解決できますが偶数である問題4は解決できません。
したがって、問題4の解決処理の責任はHondaに移ります。SpecialSupportのHondaは問題4のみ解決することができます。
問題5は3以上且つ奇数のため、Nagatomo→Kagawaと責任が受け流され、Kagawaが解決します。
問題6は、全てのサポートで解決が不可能であるため、サポートできない旨のメッセージが表示されています。
ChainOfResponsibilityパターン使用によるメリット
- 利用者側は、内部構造を意識することなく、連鎖関係のある任意のオブジェクトに要求をすることで、適切な処理者によって要求が処理される
- 処理者側は、自分ができる処理のみ対応すれば良いので、簡潔に処理が記述できる
ChainOfResponsibilityパターンを使わないほうが良い場合
利用者と処理者の関係が固定的で、処理速度が重要な場合は使用しないほうが良い
振り返り
ChainOfResponsibilityパターンとは
複数のオブジェクトをチェーン(鎖)のようにつないでおき、要求を処理することが可能なオブジェクトにわたるまで、要求を受け流していくパターンです。
サンプルコード
以下に公開しています。
https://github.com/Keenag/DesignPatternStudyGroup/tree/master/ChainOfResponsibilitySample