Strategy パターン
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 90/107。
Strategy パターンを使用すると、アルゴリズムのファミリーを定義し、それぞれをカプセル化して、実行時に相互に交換できるようになります。複数のオブジェクトが 1 つのイベントに反応する Observer とは異なり、Strategy では 1 つのオブジェクトが異なる振る舞いを動的に切り替えられます。
Goでは、アルゴリズム用のinterfaceを定義し、異なる実装をcontext構造体に注入することで、Strategyを実装します:
type PaymentStrategy interface {
Pay(amount float64) string
}
type CreditCard struct{}
func (c CreditCard) Pay(amount float64) string {
return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}
type PayPal struct{}
func (p PayPal) Pay(amount float64) string {
return fmt.Sprintf("Paid %.2f via PayPal", amount)
}context 構造体は strategy interface への参照を holds し、context のコードを変更せずにアルゴリズムを入れ替えられるようにします。
type Checkout struct {
strategy PaymentStrategy
}
func (c *Checkout) SetStrategy(s PaymentStrategy) {
c.strategy = s
}
func (c *Checkout) ProcessPayment(amount float64) string {
return c.strategy.Pay(amount)
}これで、実行時に支払い方法を変更できます。
checkout := &Checkout{}
checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00)) // クレジットカードで100.00を支払いました
checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00)) // PayPalで50.00を支払いましたStrategyは、operationを実行する複数の方法があり、ユーザー入力、設定、または実行時の条件に基づいてそのうち1つを選択する必要がある場合に最適です。一般的な使用例には、ソートアルゴリズム、圧縮method、検証ルールなどがあります。
チャレンジ
簡単Strategyパターンを使ってテキスト formatting システムを構築しましょう!実行時に切り替えられるさまざまな formatting strategy を作成し、コアコードを変更せずに、同じテキスト processor から異なる出力スタイルを生成できるようにします。
コードを3つのファイルに整理します。
formatter.go: strategy interface と具体的な formatting strategy を Define します。TextFormatterinterface に、strategy のルールに従ってテキストを変換するFormat(text string) stringmethod を作成します。3つの formatting strategy を Implement します。
UppercaseFormatter: テキストをすべて大文字に converts しますSnakeCaseFormatter: スペースをアンダースコアに converts し、テキストを lowercase にしますTitleFormatter: 各単語の first letter を capitalizes します(単語はスペースで区切られます)
processor.go: formatting strategy を使用する context struct を Create します。TextFormatterstrategy を holds するTextProcessorstruct を構築します。以下の method を追加します。- active な formatting strategy を変更する
SetFormatter(f TextFormatter) - current formatter をテキストに applies する
Process(text string) string
また、initial formatter が設定されていない processor を返す
NewTextProcessor()constructor も Create します。- active な formatting strategy を変更する
main.go: 実行時に strategy を切り替える方法を示します。テキストを1つ読み取り、次に format operation の count を読み取ります。各 operation について、format type(
upper、snake、またはtitle)を読み取り、appropriate な formatter を processor に設定し、元のテキストを Process して、結果を print します。
以下の入力が提供されます。
- 1行目: formatting するテキスト
- 2行目: format operation の number
- 以降の行: 各 operation の format type
たとえば、次の入力が与えられた場合:
hello world today
3
upper
snake
title出力は次のようになります。
HELLO WORLD TODAY
hello_world_today
Hello World Todayまた、次の入力が与えられた場合:
Go Programming Language
2
snake
upper出力は次のようになります。
go_programming_language
GO PROGRAMMING LANGUAGEさらに、次の入力が与えられた場合:
design patterns are useful
1
title出力は次のようになります。
Design Patterns Are Useful同じ TextProcessor でも、formatter strategy を切り替えるだけでまったく異なる出力が生成されることに注目してください。processor は、どの specific formatter を使用しているかを知る必要も、気にする必要もありません。現在設定されている strategy に単純に delegates します。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// フォーマットするテキストを読み取る
text, _ := reader.ReadString('\n')
text = strings.TrimSpace(text)
// 操作の数を読み取る
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
// 新しいテキストプロセッサを作成する
processor := NewTextProcessor()
// 各フォーマット操作を処理する
for i := 0; i < count; i++ {
formatType, _ := reader.ReadString('\n')
formatType = strings.TrimSpace(formatType)
// TODO: formatType ("upper", "snake", or "title") に基づいて、
// プロセッサに適切なフォーマッタを設定し
// 処理したテキストを出力する
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ