Help us understand the problem. What is going on with this article?

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

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

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

Strategy/Stateパターン概要

  • 条件によって違う処理内容を外部クラスに追い出し、どのクラスを使うかを実行時に選択するパターンです。
  • 具体的には、switch文の「caseが多い/caseごとの処理内容が濃い」場合に、caseごとにクラスを分離させることが多いと思います。
  • 「ユニットテストしやすく」「拡張がしやすい(影響範囲を限定できる)」というメリットがあります。
  • GoFのデザインパターンでは振る舞いに関するパターンに分類されます。
  • StrategyとStateは設計としては同じで、作成側の意図が何にあるかの違いです(と私は解釈しています)。
    • Strategyは性質の違いによる振る舞いの切り替え
    • Stateは状態の変化による振る舞いの切り替え

Strategyパターンの使い所

  • switch文の caseが多い/caseごとの処理内容が濃い 場合

Stateパターンの使い所

私なりの見解では、普通のiOSアプリでは使いどころを見つけるのはなかなか難しいと思います。

一番利用したい場面は『データの「取得開始前」「取得中」「取得成功」「取得エラー」という状態の変化でViewを更新する』ですが、UIKitがそのような設計にマッチしないためです。

サンプルコード

Xcode 11.3 / Swift 5.1 です。
Playgroundにコピペすれば動作します。

Strategyパターンを適用しないサンプル

// 認証パラメータ
struct AuthInfo {
    var id = ""
    var password = ""
    var token = ""

    init(id: String, password: String) {
        self.id = id
        self.password = password
    }
    init(token: String) {
        self.token = token
    }
}

// 認証管理クラス
final class AuthManager {
    enum AuthType {
        case idPassword
        case token
    }

    static func authenticate(by type: AuthType, with authInfo: AuthInfo) {
        switch type {
        case .idPassword:
            if authInfo.id == "id" && authInfo.password == "password" {
                print("ID Password: auth success")
            } else {
                print("ID Password: invalid id or password")
            }
        case .token:
            if authInfo.token == "token" {
                print("Token: auth success")
            } else {
                print("Token: invalid token")
            }
        }
    }
}

AuthManager.authenticate(by: .idPassword, with: AuthInfo(id: "id", password: "password"))
// ID Password: auth success

AuthManager.authenticate(by: .token, with: AuthInfo(token: "token"))
// Token: auth success

Strategyパターンを適用したサンプル

// 認証パラメータ
struct AuthInfo {
    var id = ""
    var password = ""
    var token = ""

    init(id: String, password: String) {
        self.id = id
        self.password = password
    }
    init(token: String) {
        self.token = token
    }
}

// MARK: - Protocol
// 認証プロトコル
protocol AuthStrategy {
    func authenticate(_ authInfo: AuthInfo)
}

// MARK: - Context
// 認証を実行する役割
struct AuthContext {
    let strategy: AuthStrategy

    func execute(with authInfo: AuthInfo) {
        strategy.authenticate(authInfo)
    }
}

// MARK: - Concreate Strategies
// IDパスワード認証
struct IdPasswordAuthStrategy: AuthStrategy {
    func authenticate(_ authInfo: AuthInfo) {
        if authInfo.id == "id" && authInfo.password == "password" {
            print("ID Password: auth success")
        } else {
            print("ID Password: invalid id or password")
        }
    }
}
// トークン認証
struct TokenAuthStrategy: AuthStrategy {
    func authenticate(_ authInfo: AuthInfo) {
        if authInfo.token == "token" {
            print("Token: auth success")
        } else {
            print("Token: invalid token")
        }
    }
}

// MARK: - Usage
var context = AuthContext(strategy: IdPasswordAuthStrategy())
context.execute(with: AuthInfo(id: "id", password: "password"))
// ID Password: auth success

context = AuthContext(strategy: TokenAuthStrategy())
context.execute(with: AuthInfo(token: "token"))
// Token: auth success
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした