Menu
Coddy logo textTech

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、検証ルールなどがあります。

challenge icon

チャレンジ

簡単

Strategyパターンを使ってテキスト formatting システムを構築しましょう!実行時に切り替えられるさまざまな formatting strategy を作成し、コアコードを変更せずに、同じテキスト processor から異なる出力スタイルを生成できるようにします。

コードを3つのファイルに整理します。

  • formatter.go: strategy interface と具体的な formatting strategy を Define します。

    TextFormatter interface に、strategy のルールに従ってテキストを変換する Format(text string) string method を作成します。

    3つの formatting strategy を Implement します。

    • UppercaseFormatter: テキストをすべて大文字に converts します
    • SnakeCaseFormatter: スペースをアンダースコアに converts し、テキストを lowercase にします
    • TitleFormatter: 各単語の first letter を capitalizes します(単語はスペースで区切られます)
  • processor.go: formatting strategy を使用する context struct を Create します。

    TextFormatter strategy を holds する TextProcessor struct を構築します。以下の method を追加します。

    • active な formatting strategy を変更する SetFormatter(f TextFormatter)
    • current formatter をテキストに applies する Process(text string) string

    また、initial formatter が設定されていない processor を返す NewTextProcessor() constructor も Create します。

  • main.go: 実行時に strategy を切り替える方法を示します。

    テキストを1つ読み取り、次に format operation の count を読み取ります。各 operation について、format type(uppersnake、または 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") に基づいて、
		// プロセッサに適切なフォーマッタを設定し
		// 処理したテキストを出力する
		
	}
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Goオンラインコンパイラ