LoginSignup
2
1

More than 5 years have passed since last update.

デザインパターン勉強会 第14回:ChainOfResponsibilityパターン

Posted at

はじめに

本エントリーは某社内で実施するデザインパターン勉強会向けの資料となります。
本エントリーで書籍「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パターンを使用したサンプルプログラムを紹介します。

ChainOfResponsibility_class.JPG


各クラスの役割

クラス名 役割
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


2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1