LoginSignup
1
3

More than 3 years have passed since last update.

[iOS/Swift] アプリ開発の実務的アプローチで学ぶデザインパターン ~Chain Of Responsibility~

Posted at

この記事シリーズは、iOS/Swiftエンジニアである執筆者個人が、
ごく普通のiOSアプリ開発でよくある状況
Swiftのコアライブラリやフレームワークで使われているパターン
着目してデザインパターンを学び直してみた記録です。

関連記事一覧
[iOS/Swift] アプリ開発の実務的アプローチで学ぶデザインパターン

Chain Of Responsibilityパターン概要

  • 全体を統率するクラスを作らず、個々のクラスが責務を「たらい回し」して、責務を果たせるクラスに行き当たった時点でそのクラスが処理を行います。
  • GoFのデザインパターンでは振る舞いに関するパターンに分類されます。

使い所

  • iOSエンジニアにとってはおなじみの、UIKitの「Responder Chain」という仕組みがChain Of Responsibilityパターンに則って構築されています。

Responder Chainとは?

UIResponderオブジェクトの繋がりのことで、この繋がりの順番にしたがって
touchイベントなどの処理ができるUIResponderオブジェクト(=first responder)を見つけて処理を行う仕組みです。

引用:【Swift】Responder Chainの仕組み

  • アプリ開発での使い所は思い当たらなかったのですが、iOSフレークワーク内部で使われている仕組みであるわけですから、概念を理解しておいて損はないかと思います。

サンプルコード

Swiftバージョンは 5.1 です。

protocol Withdrawing {
    // 引き出す
    func withdraw(amount: Int) -> Bool
}

// 札束クラス
final class MoneyPile: Withdrawing {
    // 額面
    let value: Int
    // 枚数
    var quantity: Int
    // 責務をたらい渡す次のオブジェクト
    var next: Withdrawing?

    init(value: Int, quantity: Int, next: Withdrawing?) {
        self.value = value
        self.quantity = quantity
        self.next = next
    }

    func withdraw(amount: Int) -> Bool {

        var amount = amount

        func canTakeSomeBill(want: Int) -> Bool {
            return (want / self.value) > 0
        }

        var quantity = self.quantity

        while canTakeSomeBill(want: amount) {

            if quantity == 0 {
                break
            }

            amount -= self.value
            quantity -= 1
        }

        guard amount > 0 else {
            return true
        }

        if let next = self.next {
            return next.withdraw(amount: amount)
        }

        return false
    }
}

// ATMクラス
final class ATM: Withdrawing {
    // $100の札束
    private var hundred: Withdrawing
    // $50の札束
    private var fifty: Withdrawing
    // $20の札束
    private var twenty: Withdrawing
    // $10の札束
    private var ten: Withdrawing

    private var startPile: Withdrawing {
        return self.hundred
    }

    init(hundred: Withdrawing,
           fifty: Withdrawing,
          twenty: Withdrawing,
             ten: Withdrawing) {

        self.hundred = hundred
        self.fifty = fifty
        self.twenty = twenty
        self.ten = ten
    }

    func withdraw(amount: Int) -> Bool {
        return startPile.withdraw(amount: amount)
    }
}

// Usage
// 札束オブジェクトを生成して $10 -> $20 -> $50 -> $100 と数珠つなぎにする
let ten = MoneyPile(value: 10, quantity: 6, next: nil)
let twenty = MoneyPile(value: 20, quantity: 2, next: ten)
let fifty = MoneyPile(value: 50, quantity: 2, next: twenty)
let hundred = MoneyPile(value: 100, quantity: 1, next: fifty)

// ATMオブジェクトを札束オブジェクトを格納して生成する
var atm = ATM(hundred: hundred, fifty: fifty, twenty: twenty, ten: ten)

// ATMには合計$300しか入っていないため$310は引き出せない
atm.withdraw(amount: 310)   // false

// $300は引き出せる
atm.withdraw(amount: 100)   // true

引用:
https://github.com/ochococo/Design-Patterns-In-Swift#-chain-of-responsibility

1
3
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
1
3