Menu
Coddy logo textTech

Strategyパターン

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

Strategyパターンを使用すると、一連のアルゴリズムを定義し、それぞれを独自のクラスにカプセル化して、実行時にそれらを入れ替え可能にすることができます。クラスに振る舞いをハードコーディングする代わりに、インターフェースを介して振る舞いを注入することで、それを利用するクラスを変更することなく、戦略(ストラテジー)を切り替えることができます。

さまざまな支払い方法をサポートしたい決済システムを考えてみましょう。各方法を処理するために条件分岐を使用するのではなく、ストラテジーインターフェースを定義し、具体的な実装を作成します:

abstract class PaymentStrategy {
  void pay(double amount);
}

class CreditCardPayment implements PaymentStrategy {
  @override
  void pay(double amount) => print('Paid \$amount with Credit Card');
}

class PayPalPayment implements PaymentStrategy {
  @override
  void pay(double amount) => print('Paid \$amount with PayPal');
}

class ShoppingCart {
  PaymentStrategy _paymentStrategy;

  ShoppingCart(this._paymentStrategy);

  void setPaymentStrategy(PaymentStrategy strategy) {
    _paymentStrategy = strategy;
  }

  void checkout(double total) {
    _paymentStrategy.pay(total);
  }
}

void main() {
  var cart = ShoppingCart(CreditCardPayment());
  cart.checkout(99.99);  // クレジットカードで$99.99を支払いました

  cart.setPaymentStrategy(PayPalPayment());
  cart.checkout(49.99);  // PayPalで$49.99を支払いました
}

ShoppingCart は、支払いがどのように機能するかを知る必要も関与する必要もありません。現在設定されているストラテジーに委譲するだけです。カートのコードに手を加えることなく、新しいストラテジークラスを作成することで、新しい支払い方法を追加できます。このパターンは、アクションを実行する方法が複数あり、それらを動的に選択または切り替えたい場合に最適です。

challenge icon

チャレンジ

簡単

Strategy パターンを使用して、テキストフォーマットシステムを構築しましょう!コアとなるテキストプロセッサを変更することなく、ユーザーが動的にフォーマットを切り替えられるように、テキストにさまざまなフォーマット戦略を適用できるシステムを作成します。

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

  • formatting_strategies.dart: ここに戦略システムを作成します。String format(String text) メソッドを持つ抽象クラス FormattingStrategy を定義します。次に、3つの具体的な戦略を実装します:
    • UppercaseStrategy - テキストをすべて大文字に変換します
    • LowercaseStrategy - テキストをすべて小文字に変換します
    • TitleCaseStrategy - 各単語の最初の文字を大文字にします(スペースで分割し、各単語の最初の文字を大文字に、残りを小文字にします)
    また、FormattingStrategy を保持する TextProcessor クラスも作成します。これには、初期戦略を受け取るコンストラクタ、実行時に戦略を変更するための setStrategy() メソッド、および現在の戦略を適用してフォーマットされた結果を返す process(String text) メソッドが必要です。
  • main.dart: フォーマット戦略をインポートし、Strategy パターンによって動作を動的に切り替える方法を示します。UppercaseStrategy で始まる TextProcessor を作成します。テキスト hello world を処理して結果をプリントします。次に LowercaseStrategy に切り替え、HELLO WORLD を処理してプリントします。最後に TitleCaseStrategy に切り替え、the quick brown fox を処理して結果をプリントします。

これは Strategy パターンの強力さを示しています。TextProcessor はフォーマットがどのように機能するかという詳細を知る必要はありません。単に現在設定されている戦略に委譲するだけで、プロセッサを修正することなく新しいフォーマットオプションを簡単に追加できます!

期待される出力:

HELLO WORLD
hello world
The Quick Brown Fox

チートシート

Strategyパターンは、一連のアルゴリズムを定義し、それぞれを独自のクラスにカプセル化して、実行時にそれらを交換可能にします。振る舞いをハードコーディングする代わりに、インターフェースを通じて注入します。

ストラテジーインターフェースを定義します:

abstract class PaymentStrategy {
  void pay(double amount);
}

具体的なストラテジーの実装を作成します:

class CreditCardPayment implements PaymentStrategy {
  @override
  void pay(double amount) => print('Paid \$amount with Credit Card');
}

class PayPalPayment implements PaymentStrategy {
  @override
  void pay(double amount) => print('Paid \$amount with PayPal');
}

ストラテジーを使用するコンテキストクラスを作成します:

class ShoppingCart {
  PaymentStrategy _paymentStrategy;

  ShoppingCart(this._paymentStrategy);

  void setPaymentStrategy(PaymentStrategy strategy) {
    _paymentStrategy = strategy;
  }

  void checkout(double total) {
    _paymentStrategy.pay(total);
  }
}

ストラテジーを注入して切り替えることで、パターンを使用します:

var cart = ShoppingCart(CreditCardPayment());
cart.checkout(99.99);  // クレジットカードで$99.99支払われました

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

コンテキストクラスは、実装の詳細を知ることなく、振る舞いをストラテジーに委譲します。既存のコードを修正することなく、新しいクラスを作成することで新しいストラテジーを追加できます。

自分で試してみよう

import 'formatting_strategies.dart';

void main() {
  // TODO: UppercaseStrategyを使用してTextProcessorを作成する
  
  // TODO: テキスト 'hello world' を処理して結果を表示する
  
  // TODO: LowercaseStrategyに切り替える
  
  // TODO: 'HELLO WORLD' を処理して結果を表示する
  
  // TODO: TitleCaseStrategyに切り替える
  
  // TODO: 'the quick brown fox' を処理して結果を表示する
}
quiz icon腕試し

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

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