Menu
Coddy logo textTech

ストラテジーパターン

CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 69/87。

ストラテジーパターン(Strategy Pattern)は、一連のアルゴリズムを定義し、それぞれを独自のクラスにカプセル化して、それらを交換可能にする振る舞いに関するデザインパターンです。特定の振る舞いを直接プログラムに書き込む(ハードコーディングする)代わりに、それらを使用するコードを変更することなく、実行時にアルゴリズムを切り替えることができます。

複数の支払い方法をサポートする決済システムを想像してみてください。ストラテジーパターン(Strategy Pattern)を使用しない場合、煩雑な条件分岐ロジックになってしまいます。このパターンを使用することで、各支払い方法が独自のストラテジー(戦略)になります。

interface PaymentStrategy {
    void pay(int amount);
}

class CreditCardPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " via Credit Card");
    }
}

class PayPalPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " via PayPal");
    }
}

contextクラスは、ストラテジーへの参照を保持し、処理をそのストラテジーに委譲します。重要なのは、コンテキストがどの具体的なストラテジーを使用しているかを知らないということです。コンテキストはインターフェースのみを認識しています:

class ShoppingCart {
    private PaymentStrategy paymentStrategy;
    
    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.paymentStrategy = strategy;
    }
    
    public void checkout(int amount) {
        paymentStrategy.pay(amount);
    }
}

これで、支払いの動作を動的に変更できるようになります:

ShoppingCart cart = new ShoppingCart();

cart.setPaymentStrategy(new CreditCardPayment());
cart.checkout(100);  // クレジットカードで100を支払いました

cart.setPaymentStrategy(new PayPalPayment());
cart.checkout(50);   // PayPalで50を支払いました

ストラテジーパターンは、あるアクションを実行するための複数の方法があり、それらを簡単に切り替えたい場合に真価を発揮します。新しい支払い方法を追加するには、単に PaymentStrategy を実装する新しいクラスを作成するだけで済み、既存のコードを変更する必要はありません。

challenge icon

チャレンジ

簡単

Strategyパターンを使用してテキストフォーマットシステムを構築しましょう!実行時にさまざまなフォーマット戦略を切り替えて、テキストをさまざまな方法で変換できるシステムを作成します。これは、複数の出力形式をサポートする必要があるアプリケーションに最適です。

コードは4つのファイルに分けて構成します:

  • TextFormatter.java: すべてのフォーマット戦略が実装する TextFormatter インターフェースを定義します。文字列を受け取り、フォーマットされたバージョンを返す単一のメソッド String format(String text) を宣言する必要があります。
  • Formatters.java: TextFormatter インターフェースを実装する3つの具体的な戦略を作成します:

    UpperCaseFormatter - テキストをすべて大文字に変換します。

    LowerCaseFormatter - テキストをすべて小文字に変換します。

    TitleCaseFormatter - 各単語の最初の文字を大文字にし、残りを小文字にします。単語はスペースで区切られます。

  • TextEditor.java: フォーマット戦略を使用するコンテキストクラスを作成します。TextEditor には以下を含める必要があります:

    現在の TextFormatter 戦略を保持するためのプライベートフィールド。

    アクティブなフォーマット戦略を変更する setFormatter(TextFormatter formatter) メソッド。

    現在のフォーマッタをテキストに適用し、Published: [formatted text] と出力する publishText(String text) メソッド。フォーマッタが設定されていない場合は、変更せずに Published: [original text] と出力する必要があります。

  • Main.java: 戦略システムを統合しましょう!フォーマットするテキスト(String)とフォーマットスタイル(String: "upper", "lower", または "title")の2つの入力を受け取ります。

    TextEditor を作成し、フォーマッタを設定する前に、まず入力テキストで publishText を呼び出します(デフォルトの動作を示します)。

    次に、入力されたスタイルに基づいて適切なフォーマッタ戦略を設定し、同じテキストで再度 publishText を呼び出して、フォーマットされた結果を表示します。

入力は、フォーマットするテキスト(String)とフォーマットスタイル(String)の順に2つ受け取ります。

例えば、入力が hello worldtitle の場合、出力は以下のようになります:

Published: hello world
Published: Hello World

TextEditor が特定のフォーマットロジックについて何も知らないことに注目してください。単に現在設定されている戦略に委譲しているだけです。これにより、エディタのコードに触れることなく、新しいフォーマットスタイルを簡単に追加できるようになります!

チートシート

ストラテジーパターン(Strategy Pattern)は、一連のアルゴリズムを定義し、それぞれを独自のクラスにカプセル化して、それらを交換可能にする振る舞いに関するデザインパターンです。これを使用することで、アルゴリズムを利用する側のコードを変更することなく、実行時にアルゴリズムを切り替えることができます。

主要な構成要素

Strategyインターフェース: すべての具体的なストラテジーに共通のインターフェースを定義します。

interface PaymentStrategy {
    void pay(int amount);
}

具体的なストラテジー(Concrete Strategies): 特定のアルゴリズムを使用してStrategyインターフェースを実装します。

class CreditCardPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " via Credit Card");
    }
}

class PayPalPayment implements PaymentStrategy {
    public void pay(int amount) {
        System.out.println("Paid " + amount + " via PayPal");
    }
}

コンテキストクラス(Context Class): ストラテジーへの参照を保持し、処理をそのストラテジーに委譲します。コンテキストはインターフェースのみを認識し、具体的な実装については関知しません。

class ShoppingCart {
    private PaymentStrategy paymentStrategy;
    
    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.paymentStrategy = strategy;
    }
    
    public void checkout(int amount) {
        paymentStrategy.pay(amount);
    }
}

使用方法

ストラテジーは実行時に動的に変更できます。

ShoppingCart cart = new ShoppingCart();

cart.setPaymentStrategy(new CreditCardPayment());
cart.checkout(100);  // クレジットカードで100支払われました

cart.setPaymentStrategy(new PayPalPayment());
cart.checkout(50);   // PayPalで50支払われました

利点

ストラテジーパターンは、あるアクションを実行する方法が複数あり、それらを簡単に切り替えたい場合に最適です。新しいストラテジーを追加するには、単にStrategyインターフェースを実装する新しいクラスを作成するだけでよく、既存のコードを変更する必要はありません。

自分で試してみよう

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String text = scanner.nextLine();
        String style = scanner.nextLine();
        
        // TODO: TextEditorのインスタンスを作成する
        
        // TODO: 入力テキストを使用してpublishTextを呼び出す(フォーマッタを設定する前)
        
        // TODO: スタイルの入力("upper"、"lower"、または"title")に基づいて、
        // 適切なフォーマッタを設定し、再度publishTextを呼び出す
        
    }
}
quiz icon腕試し

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

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