Update 2023.11.19 2017.05.05

Python デザインパターン サンプルコード Chain of Responsibility
結城 浩「Java言語で学ぶデザインパターン入門」をPython化
Python3(3.11)で動くソースコード(.pyファイル .ipynbファイル)あります
「anaconda3」on .py「PyCharm」.ipynb「Jupyter Notebook」

◆◆Chain of Responsibility パターンの使われる場面◆◆

GoFのC++サンプルは GUI のオンラインヘルプです。解決策が複数ある場合に解決策を提示するために問題側を解析してしまうと,問題側と解決側の関係が密になりすぎる傾向になります。そうならないように,問題側をいじらないというのがChain of Responsibility パターンです。

結城氏は「責任のたらい回し」と言っていますが,むしろ,組織で責任を持って対応するというのが正しいと思われます。

例えば,役所に相談に来た人に対して,窓口の人が相談内容を聴き出すのも対応の1つですが,そういう対応ではなく Chain of Responsibility パターンでは,複数の部署が順に対応するという解決策をとります。ということでこのパターンは問題を選ばないと変な対応になってしまうかもしれません。

Java サンプルは全く実用的でない例になっていて,問題側は問題に番号を付けるだけ,解決側は問題の番号を見て解決可能かを示すだけという内容になっています。Chain of Responsibility パターンのサンプルとしてこれで十分なのかもしれません。


(2023-11-19)Python3.11で動作確認済み


◆◆Chain of Responsibility パターンとは◆◆

GoFによれば,Chain of Responsibility パターンの目的は, 「1つ以上のオブジェクトに要求を処理する機会を与えることにより,要求を送信するオブジェクトと受信するオブジェクトの結合を避ける。受信する複数のオブジェクトをチェーン状につなぎ,あるオブジェクトがその要求を処理するまで,そのチェーンに沿って要求を渡していく。」

GoFによれば,Chain Of Responsibility パターンは,次のような場合に使うことができる。
・要求を処理するオブジェクトの候補が複数存在し,最終的にどのオブジェクトが担当するのかは,前もってわからない場合。担当オブジェクトは自動的に決められる。
・受け手を明確にせずに,複数あるオブジェクトの1つに対して要求を発行したい場合。
・要求を処理することができるオブジェクトの集合が動的に明確化される場合。

GoFによれば,Chain of Responsibility パターンの関連するパターンは次のようなものである。
Composite パターン:Chain Of Responsibility パターンは,しばしば Composite パターンとともに適用される。その場合,component の親オブジェクトを successor にすることができる。

◆◆Chain of Responsibility パターンのサンプルのクラス構成◆◆

ソースコードは巻末にあり,ソースファイルはこのWebの左上隅にありダウンロードできます。
       Trouble                        Support
      (getNumber)             (setNext, support, resolve, done, fail)
                       ↑              ↑           ↑                ↑
                  NoSupport       LimitSupport   OddSupport      SpecialSupport
                (resolve)         (resolve)     (resolve)      (resolve)
class Trouble は,問題を提示します。
class Support は,抽象クラスでありテンプレートです。
class NoSupport は,解決側の1つであり,いつも解決できないという対応をします。
class LimitSupport は,解決側の1つであり,ある番号以下だけ解決するという対応をします。
class OddSupport は,解決側の1つであり,奇数番号だけ解決するという対応をします。
class SpecialSupport は,解決側の1つであり,ある番号だけ解決するという対応をします。

◆◆Chain of Responsibility パターンの実装◆◆

このサンプルでは,問題は番号を付けるだけで問題の内容はありません。解決策は4種類あり,担当者と組み合わせて全部で6つの解決策が用意されます。

4種類の解決策は,class NoSupport, LimitSupport, OddSupport, SpecialSupport のメソッド resolve に書かれています。このメソッドは class Support の抽象メソッドを実装したものです。この4種類の解決策と担当者の組み合わせは,メインルーチンでインスタンス化されることによりできます。ここでは全部で6つの解決策が用意されます。

class Support の抽象メソッド resolve 以外は問題の解決策を順に提示するシステムになっています。メソッド setNext は順番を決めます。使い方はメインルーチンに次のように書きます。
    alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred)
これでインスタンスに親子関係を記録できます。問題の提示と解決策巡りはメインルーチンでメソッド support を呼ぶだけです。メソッド support は次のようになっています。

22
23
24
25
26
27
28
    def support(self, trouble):     # トラブル解決の手順
        if self.resolve(trouble):
            self.done(trouble)
        elif self.next:
            self.next.support(trouble)
        else:
            self.fail(trouble)

解決策の内容は本来サブクラスに書くものです。このスーパークラスでは対応の結果の回答と次の対応を呼ぶことに専念しています。

◆◆ソースコード◆◆

このWebページの左上隅からダウンロードできます。

ソースファイルは1つです。
・ChainofResponsibility;Chain of Responsibility パターンのPythonサンプル

【ChainofResponsibility】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from abc import ABCMeta, abstractmethod

class Trouble(object):
    def __init__(self, number):     # トラブルの生成
        self.number = number        # トラブル番号
    def getNumber(self):            # トラブル番号を得る
        return self.number
    def __str__(self):              # インスタンスがprintされると呼ばれる
        return "[Trouble {}]".format(self.number)

class Support(metaclass=ABCMeta):

    def __init__(self, name):       # トラブル解決者の生成
        self.name = name            # このトラブル解決者の名前
        self.next = None            # たらい回しの先
    def setNext(self, next):        # たらい回しの先を設定
        self.next = next
        return next
    def support(self, trouble):     # トラブル解決の手順
        if self.resolve(trouble):
            self.done(trouble)
        elif self.next:
            self.next.support(trouble)
        else:
            self.fail(trouble)
    def __str__(self):              # インスタンスがprintされると呼ばれる
        return "[{}]".format(self.name)
    @abstractmethod
    def resolve(self, trouble):     # 解決用メソッド
        pass
    def done(self, trouble):
        print("{} is resolved by {}.".format(trouble, self))
    def fail(self, trouble):        # 未解決
        print("{} cannot be resolved.".format(trouble))

class NoSupport(Support):
                                    # コンストラクタなし
    def resolve(self, trouble):     # 解決用メソッド
        return False                # 自分は何も処理しない

class LimitSupport(Support):
    def __init__(self, name, limit): # コンストラクタ
        Support.__init__(self, name)
        self.limit = limit          # この番号未満なら解決できる
    def resolve(self, trouble):     # 解決用メソッド
        if trouble.getNumber() < self.limit:
            return True
        else:
            return False

class OddSupport(Support):
                                    # コンストラクタなし
    def resolve(self, trouble):     # 解決用メソッド
        if trouble.getNumber() % 2 == 1:
            return True
        else:
            return False

class SpecialSupport(Support):
    def __init__(self, name, number): # コンストラクタ
        Support.__init__(self, name)
        self.number = number        # この番号だけ解決できる
    def resolve(self, trouble):     # 解決用メソッド
        if trouble.getNumber() == self.number:
            return True
        else:
            return False

def main():
    alice = NoSupport("Alice")
    bob = LimitSupport("Bob", 100)
    charlie = SpecialSupport("Charlie", 429)
    diana = LimitSupport("Diana", 200)
    elmo = OddSupport("Elmo")
    fred = LimitSupport("Fred", 300)
    # 連鎖の形成
    alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred)
    # さまざまなトラブル発生
    for i in range(0, 500, 33):
        alice.support(Trouble(i))

if __name__ == '__main__':
    main()
"""標準出力
[Trouble 0] is resolved by [Bob].
[Trouble 33] is resolved by [Bob].
[Trouble 66] is resolved by [Bob].
[Trouble 99] is resolved by [Bob].
[Trouble 132] is resolved by [Diana].
[Trouble 165] is resolved by [Diana].
[Trouble 198] is resolved by [Diana].
[Trouble 231] is resolved by [Elmo].
[Trouble 264] is resolved by [Fred].
[Trouble 297] is resolved by [Elmo].
[Trouble 330] cannot be resolved.
[Trouble 363] is resolved by [Elmo].
[Trouble 396] cannot be resolved.
[Trouble 429] is resolved by [Charlie].
[Trouble 462] cannot be resolved.
[Trouble 495] is resolved by [Elmo].
"""

以上

トップページに戻る
inserted by FC2 system