ストラテジーパターン
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 を実装する新しいクラスを作成するだけで済み、既存のコードを変更する必要はありません。
チャレンジ
簡単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 world と title の場合、出力は以下のようになります:
Published: hello world
Published: Hello WorldTextEditor が特定のフォーマットロジックについて何も知らないことに注目してください。単に現在設定されている戦略に委譲しているだけです。これにより、エディタのコードに触れることなく、新しいフォーマットスタイルを簡単に追加できるようになります!
チートシート
ストラテジーパターン(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を呼び出す
}
}このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4継承
継承の基本 (extends)super キーワードメソッドのオーバーライド (@Override)コンストラクタチェーンObject クラス単一継承と多段階継承なぜ多重継承ができないのかまとめ:従業員の階層構造