Chain of Responsibility

デザインパターンとは

デザインパターンとはオブジェクト思考開発における先人たちが作り上げてきた便利な設計図です。

Gang of Four通称Gofが1994年に出版した『オブジェクト指向における再利用のためのデザインパターン』の中で23個の設計図が紹介されています。

Note

デザインパターンのサンプルコードはSwift4でまとめます。

チェインオブレスポンシビリティパターンとは?

Chain of Responsibilityパターンは責任の鎖という意味で、自分より上の責任の処理することのできるオブジェクトを保持しておくことで、オブジェクト同士の結びつきを緩めることができます。

UIKitではViewタップ時の処理に使われているようです。

サンプルコード

サンプルコードではTECH SCOREさんの解説がわかりやすかったので参考にして、Swiftに置き換えてみます。

生徒が遠足に行く際『バナナはおやつに入るのか』や『親が心配するので携帯電話を持っていっても良いか』などの質問が生徒から飛んできた際に、担任先生は全ての質問に回答することができない(質問が,権限の範囲外である)可能性があります。

バナナの質問はその場で返答できますが、携帯電話の所持に関しては学年主任の先生に聞かなければわからないという時、生徒の質問を先生は権限を持っている上の立場の人(学年主任)に聞きます。

Responsibleプロトコル

各クラスはこのプロトコルに準拠させます。setNextではboss(自分より権限を持っている人) を登録しています。

protocol Responsible {
    var boss: Responsible? { get set }
    var responsibleLebel: Int { get }
    
    func ableToJudge(level: Int) -> Bool
    func judge(question: String, level: Int)
}
extension Responsible {
    mutating func setNext(responsible: Responsible) -> Responsible{
        boss = responsible
        return boss!
    }
}

準拠させたChainクラス

今回は3つのクラスを作成しました。学生、担任の先生、学年主任です。

実際に処理を行なっているのはjudgeメソッドです。自分のresponsibleLebel と引数で受け取った権限のレベルを照らし合わせ権限の範囲内であれば自分で処理をし、権限の範囲外であればboss(立場が上のオブジェクト)に判断を委ねます。

class Student: Responsible {
    var boss: Responsible?
    var responsibleLebel: Int = 1
    
    func judge(question: String, level: Int) {
        if ableToJudge(level: level){
            print("持っていく!")
            return
        }
        //先生に聞く
        if let boss = boss{
            boss.judge(question: question, level: level)
            return
        }
        print("聞ける人がいませんでした。")
    }
    func ableToJudge(level: Int) -> Bool {
        if responsibleLebel > level {
            return true
        }
        return false
    }
}

class HomeroomTeature: Responsible {
    var boss: Responsible?
    var responsibleLebel: Int = 5

    func judge(question: String, level: Int) {
        if ableToJudge(level: level){
            //判定処理
            print("バナナはおやつにはいりません!")
            return
        }
        //学年主任に聞く
        if let boss = boss{
            boss.judge(question: question, level: level)
            return
        }
        print("聞ける人がいませんでした。")
    }
    func ableToJudge(level: Int) -> Bool {
        if responsibleLebel > level {
            return true
        }
        return false
    }
}

class TherfTeature: Responsible {
    var boss: Responsible?
    var responsibleLebel: Int = 10
    
    func judge(question: String, level: Int) {
        if ableToJudge(level: level) {
            //判定処理
            print("もしもの時のために携帯電話の持ち込みを許可します")
            return
        }
        if let boss = boss{
            boss.judge(question: question, level: level)
            return
        }
        print("聞ける人がいませんでした...")
    }
    func ableToJudge(level: Int) -> Bool {
        if responsibleLebel > level {
            return true
        }
        return false
    }
}

呼び出し元

学生→担任の先生→学年主任と自分より権限を持っているオブジェクトに判断を委ね鎖のように繋がっていることがわかります。

var student = Student()
var teature = HomeroomTeature()
var theafTeature = TherfTeature()

//自分より権限のあるオブジェクトを設定
student.setNext(responsible: teature)
teature.setNext(responsible: theafTeature)

student.judge(question: "ハンカチを持っていく?", level: 0)
student.judge(question: "バナナはおやつに入りますか?", level: 3)
student.judge(question: "携帯電話を持っていってもいいですか?", level: 8)
student.judge(question: "シャープペンシル禁止の校則をなくしてください", level: 20)

コンソール

4つの質問ですが、持っている責任levelで誰が処理をするのか決まっていることがわかります。校則の質問は学年主任の先生の持っている権限を超えているため教頭先生や校長先生のオブジェクトを作成し判断を委ねる必要があります。

持っていく!
バナナはおやつにはいりません!
もしもの時のために携帯電話の持ち込みを許可します
聞ける人がいませんでした...

コード一覧

import UIKit

protocol Responsible {
    var boss: Responsible? { get set }
    var responsibleLebel: Int { get }
    
    func ableToJudge(level: Int) -> Bool
    func judge(question: String, level: Int)
}
extension Responsible {
    mutating func setNext(responsible: Responsible) -> Responsible{
        boss = responsible
        return boss!
    }
}

class Student: Responsible {
    var boss: Responsible?
    var responsibleLebel: Int = 1
    
    func judge(question: String, level: Int) {
        if ableToJudge(level: level){
            print("持っていく!")
            return
        }
        //先生に聞く
        if let boss = boss{
            boss.judge(question: question, level: level)
            return
        }
        print("聞ける人がいませんでした。")
    }
    func ableToJudge(level: Int) -> Bool {
        if responsibleLebel > level {
            return true
        }
        return false
    }
}

class HomeroomTeature: Responsible {
    var boss: Responsible?
    var responsibleLebel: Int = 5

    func judge(question: String, level: Int) {
        if ableToJudge(level: level){
            //判定処理
            print("バナナはおやつにはいりません!")
            return
        }
        //学年主任に聞く
        if let boss = boss{
            boss.judge(question: question, level: level)
            return
        }
        print("聞ける人がいませんでした。")
    }
    func ableToJudge(level: Int) -> Bool {
        if responsibleLebel > level {
            return true
        }
        return false
    }
}

class TherfTeature: Responsible {
    var boss: Responsible?
    var responsibleLebel: Int = 10
    
    func judge(question: String, level: Int) {
        if ableToJudge(level: level) {
            //判定処理
            print("もしもの時のために携帯電話の持ち込みを許可します")
            return
        }
        if let boss = boss{
            boss.judge(question: question, level: level)
            return
        }
        print("聞ける人がいませんでした...")
    }
    func ableToJudge(level: Int) -> Bool {
        if responsibleLebel > level {
            return true
        }
        return false
    }
}

var student = Student()
var teature = HomeroomTeature()
var theafTeature = TherfTeature()

//自分より権限のあるオブジェクトを設定
student.setNext(responsible: teature)
teature.setNext(responsible: theafTeature)

student.judge(question: "ハンカチを持っていく?", level: 0)
student.judge(question: "バナナはおやつに入りますか?", level: 3)
student.judge(question: "携帯電話を持っていってもいいですか?", level: 8)
student.judge(question: "シャープペンシル禁止の校則をなくしてください", level: 20)

参考文献

Chain of Responsibility パターン

Swiftで学ぶデザインパターン14 (Chain of Responsibilityパターン)

3分でわかるデザインパターン入門(GoF)

デザインパターン

前の記事

Command
デザインパターン

次の記事

Proxy