C#のストラテジーパターンでアルゴリズムを賢く切り替える!初心者向け完全解説
生徒
「プログラムを書いていると、条件によって処理を大きく変えたい時があるのですが、if文だらけになってしまいます。」
先生
「それはストラテジーパターンという設計の工夫を知ると、驚くほどスッキリ解決できますよ。」
生徒
「ストラテジーパターンですか?なんだか難しそうな名前ですね。」
先生
「大丈夫です。料理の道具を持ち替えるのと同じくらいシンプルな考え方です。一緒に学んでいきましょう!」
1. ストラテジーパターンとは何か?
ストラテジーパターンとは、コンピュータプログラミングにおける「設計パターン」の一種です。設計パターンとは、過去のプログラマーたちが苦労して見つけた「こう書くとプログラムが壊れにくく、修正しやすくなる」という賢い書き方のひな形のことです。ストラテジー(Strategy)は日本語で「戦略」という意味です。
例えば、あなたが料理を作ると想像してください。切るという作業をするのに、包丁を使うか、ハサミを使うか、フードプロセッサーを使うかを選べますよね。プログラムも同じで、目的は「データを処理すること」ですが、その「手段(戦略)」を必要に応じてパッと切り替えられるようにするのが、このパターンの目的です。これを知ることで、プログラムの保守性が劇的に向上します。
2. なぜif文だらけだとダメなのか?
プログラミングを始めたばかりの頃は、条件分岐のためにif文をたくさん使ってしまいます。しかし、条件が増えるたびにif文を書き足すと、プログラム全体が複雑になり、どこに何の処理が書いてあるのか分からなくなる「スパゲッティコード」という状態になりがちです。保守性とは、プログラムを後から直したり、新しい機能を追加したりする時のしやすさのことです。
例えば、割引計算をするプログラムで、会員ランクによって計算方法を変えるとします。if文が10個も20個も並ぶと、一つの修正をするだけで他の箇所に影響が出て、エラーの原因になります。ストラテジーパターンを使えば、計算方法ごとに箱を分けて整理できるため、修正する場所が明確になります。
3. インターフェースを使ってルールを決める
ストラテジーパターンを使うためには、まず「インターフェース」という仕組みを知る必要があります。インターフェースは、クラスが守るべき「ルールブック」のようなものです。「この機能を使うなら、必ずこの名前の命令を用意してください」と強制することで、プログラム全体を安全に動かします。
まずは、どんな計算方法であっても「計算する」という共通の命令を持つためのインターフェースを作成します。
public interface IDiscountStrategy
{
double Calculate(double price);
}
ここでは、IDiscountStrategyというインターフェースを定義しました。これがあれば、どんな割引ルールを作っても、Calculateという名前で計算を実行できることが約束されます。
4. 具体的なアルゴリズムを個別に作る
次に、先ほどのルールに従って、実際の割引計算の中身を作ります。ここがストラテジー(戦略)の部分です。例えば、ノーマル会員用の割引と、ゴールド会員用の割引を別々のクラスとして用意します。
// ノーマル会員は割引なし
public class NormalDiscount : IDiscountStrategy
{
public double Calculate(double price) => price;
}
// ゴールド会員は20パーセントオフ
public class GoldDiscount : IDiscountStrategy
{
public double Calculate(double price) => price * 0.8;
}
このように、割引の方法を別々のクラス(設計図)に分けることで、もし「プラチナ会員のルールも追加したい」となった時も、既存のプログラムをいじらずに新しいクラスを追加するだけで済みます。これがプログラミングにおける拡張性です。
5. コンテキストで戦略を切り替える
次に、実際にその戦略を呼び出すための「コンテキスト」を作ります。コンテキストとは、戦略を実際に実行する場面や状況のことです。このコンテキストが、どの戦略を使うかを保持します。
public class ShoppingCart
{
private IDiscountStrategy _strategy;
public void SetStrategy(IDiscountStrategy strategy)
{
_strategy = strategy;
}
public double GetFinalPrice(double price)
{
return _strategy.Calculate(price);
}
}
このShoppingCartというクラスは、どの割引戦略が選ばれているかを知りません。ただ、「セットされた戦略を使って計算する」という命令を実行するだけです。これにより、ShoppingCart本体のコードを変更することなく、柔軟な振る舞いが可能になります。
6. 実際にプログラムを動かしてみよう
それでは、作成したクラスを使って実際に割引計算を実行してみましょう。C#のプログラムは、メインのメソッドから始まります。
var cart = new ShoppingCart();
// ゴールド会員の戦略をセット
cart.SetStrategy(new GoldDiscount());
Console.WriteLine(cart.GetFinalPrice(1000)); // 結果は800
// ノーマル会員の戦略をセット
cart.SetStrategy(new NormalDiscount());
Console.WriteLine(cart.GetFinalPrice(1000)); // 結果は1000
実行結果は以下のようになります。
800
1000
このように、if文でいちいち条件を確認しなくても、戦略クラスを入れ替えるだけで処理が自動的に切り替わります。これがストラテジーパターンの威力です。プログラムが非常に読みやすくなり、後からの変更にも強くなるのです。