Strategyパターンを理解したい我
以下を読んでみる。
目的
アルゴリズムの集合を定義し、各アルゴリズムをカプセル化して、それらを交換可能にする。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のオブジェクトには、様々なオブジェクトからの呼び出しにおいて状態を共有化できたとしても、そのような状態を持たせるべきではない。
以下を読む。
例がわかりやすい。
アルゴリズム部分が条件分岐で別れている場合など積極的に使ってみたい。
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()