Closed2

Strategyパターンを理解したい我

ほへとほへと

以下を読んでみる。
https://www.amazon.co.jp/オブジェクト指向における再利用のためのデザインパターン-エリック-ガンマ/dp/4797311126


目的

アルゴリズムの集合を定義し、各アルゴリズムをカプセル化して、それらを交換可能にする。Strategyパターンを利用することで、アルゴリズムを、それを利用するクライアントからは独立に変更することができるようになる。

適用可能性

・関連する多くのクラスが振る舞いのみ異なっている場合。
・複数の異なるアルゴリズムを必要とする場合。
・アルゴリズムがクライアントが知るべきではないデータを利用している場合。
・クラスが多くの振る舞いを定義しており、これらがオペレーション内で複数の条件分として現れている場合。この時、多くの条件分を利用するかわりに、条件分岐後の処理をStrategyクラスに移し換える。

構造

// MARK: - Strategy

protocol Strategy {
    func algorithmInterface()
}

class ConcreteStrategyA: Strategy {
    func algorithmInterface() {
        // Aの処理
    }
}

class ConcreteStrategyB: Strategy {
    func algorithmInterface() {
        // Bの処理
    }
}

// MARK: - Context

class Context {
    let strategy: Strategy
    
    init(strategy: Strategy) {
        self.strategy = strategy
    }
    
    func repair() {
        strategy.algorithmInterface()
    }
}

構成要素

・Strategyクラス
サポートするすべてのアルゴリズムに共通のインターフェースを宣言する。Contextクラスは、ConcreteStrategyクラスにより定義されるアルゴリズムを呼び出すためにこのインターフェースを利用する。

・ConcreteStrategyクラス
Strategyクラスのインターフェースを使用して、アルゴリズムを実装する。

・Contextクラス
Strategyのオブジェクトに対する参照を保持する。
StrategyクラスがContextクラスのデータにアクセスするためのインターフェースを定義しても良い。
Contextオブジェクトは、クライアントからの要求をStrategyのオブジェクトに送るクライアントは通常、ConcreteStrategyオブジェクトを作成し、これをContextオブジェクトに渡す。その後、クライアントはContextオブジェクトだけとやり取りをする。ConcreteStrategyオブジェクトとして様々なものが用意されており、クライアントはこの中から選択することになる。

オーバーヘッドに気をつける

残さなけらばならない状態は、Contextオブジェクトの側に持たせ、ConcreteStrategyオブジェクトに対して要求を出す際にこれを一緒に渡すようにする。共有されるStrategyのオブジェクトには、様々なオブジェクトからの呼び出しにおいて状態を共有化できたとしても、そのような状態を持たせるべきではない

ほへとほへと

以下を読む。
https://rookie-programmer.jp/?p=142


例がわかりやすい。
アルゴリズム部分が条件分岐で別れている場合など積極的に使ってみたい。

protocol SortStrategy {
    func sort(_ students: [Student]) -> [Student]
}
class NameSort: SortStrategy { // 名前順
    func sort(_ students: [Student]) -> [Student] {
        return students.sorted(by: { $0.name < $1.name })
    }
}

class HeightSort: SortStrategy { // 身長順
    func sort(_ students: [Student]) -> [Student] {
        return students.sorted(by: { $0.height < $1.height })
    }
}

class ScoreSort: SortStrategy { // 成績順
    func sort(_ students: [Student]) -> [Student] {
        return students.sorted(by: { $0.score < $1.score })
    }
}
struct StudentPrinter { // 生徒をソートして出力する構造体
    let students: [Student]
    let sortType: SortStrategy

    func sortPrint() {
        sortType
            .sort(students) // 具体的な並び替えの処理は移譲する
            .forEach { student in
                print("----------------------")
                print("名前: \(student.name)")
                print("身長: \(student.height)")
                print("成績: \(student.score)")
                print("----------------------\n")
            }
    }
}

let studentPrinter = StudentPrinter(
    students: [
        Student(name: "Tanaka", height: 170, score: 70),
        Student(name: "Suzuki", height: 175, score: 65),
        Student(name: "Sato", height: 160, score: 80),
        Student(name: "Yamada", height: 165, score: 85),
        Student(name: "Ito", height: 180, score: 60)
    ],
    sortType: ScoreSort()
)

studentPrinter.sortPrint()
このスクラップは5ヶ月前にクローズされました