Winformアプリで シンプルなコードからStrategyパターンへ移行しよう
このチュートリアルでは、簡単な攻撃プログラムを作成し、それをより柔軟で拡張しやすい設計に改善する方法を学びます。まず、シンプルなコードを書き、その後、Strategyパターンというデザインパターンを導入して、コードの設計を改善していきます。
ステップ1: シンプルなコードを作成する
まずは、以下のシンプルなコードを見てください。このコードは、コンボボックスから選択された攻撃タイプに応じて、メッセージを表示するものです。
public partial class Form1 : Form
{
private string selectedAttack;
public Form1()
{
InitializeComponent();
comboBox1.Items.Add("剣");
comboBox1.Items.Add("魔法");
comboBox1.Items.Add("弓");
selectedAttack = "剣"; // 初期選択
}
private void button1_Click(object sender, EventArgs e)
{
switch (comboBox1.SelectedItem.ToString())
{
case "剣":
selectedAttack = "剣で攻撃した!";
break;
case "魔法":
selectedAttack = "魔法を使った!";
break;
case "弓":
selectedAttack = "弓で攻撃した!";
break;
}
MessageBox.Show(selectedAttack);
}
}
このコードは、シンプルで理解しやすいですが、攻撃タイプが増えるたびにswitch
文を追加する必要があり、コードが複雑になりがちです。
ステップ2: 問題点を理解する
この設計には、以下のような問題があります:
- コードの一貫性の欠如:
switch
文を使用して攻撃タイプごとに異なる処理を追加しているため、メソッドが複数の責任を持ち、肥大化しがちです。これにより、コードが一貫性を欠き、他の開発者が理解しにくくなります。 - 保守性の低下: 新しい攻撃タイプを追加するたびに、
Form1
クラス内でswitch
文に新しいケースを追加しなければなりません。これにより、Form1
クラスが攻撃の実装に依存し、保守が難しくなります。また、コードの変更が複数箇所に広がるため、ミスが発生しやすくなります。 - 拡張性の問題: 他の部分でも同様のロジックが必要になった場合、
switch
文の重複が発生する可能性が高く、拡張が困難です。
Strategyパターンの利点
Strategyパターンを導入することで、switch
文を保持しながらも以下のような改善が見込めます:
- クラスの責任を分離: 各攻撃方法を独立したクラスとして分離することで、
Form1
クラスが「どの攻撃方法を使うか」だけを担当し、「攻撃がどのように行われるか」は個別のクラスに委譲できます。これにより、クラスの責任が明確になります。 - コードの集中化: 新しい攻撃方法を追加する際に、
Form1
クラスではなく、専用のクラスを作成するだけで済みます。これにより、コードの変更箇所が集中し、保守が容易になります。 - 再利用性の向上: 攻撃ロジックが個別のクラスに集約されているため、他のコンテキストでも再利用可能です。これにより、同じロジックを繰り返し書く必要がなくなります。
このように、Strategyパターンを使うことで、コードの冗長性自体を直接的に減らすわけではありませんが、クラスの責任を明確にし、保守性や拡張性を大幅に向上させることができます。
ステップ3: Strategyパターンの基本を学ぶ
Strategyパターンとは?
Strategyパターンは、アルゴリズムや動作をカプセル化し、それらをインターフェースとして定義します。これにより、動作を簡単に切り替えることができ、コードの柔軟性が向上します。
Strategyパターンの構成
- インターフェース: 共通の動作を定義する
IAttack
インターフェースを作成します。 - 具体的なクラス:
IAttack
インターフェースを実装する具体的な攻撃クラス(SwordAttack
、MagicAttack
、BowAttack
)を作成します。 - コンテキストクラス: 攻撃方法を保持し、それを利用するクラス(
Character
)を作成します。
ステップ4: Strategyパターンを適用する
それでは、シンプルなコードをStrategyパターンを使って改善してみましょう。
1. IAttack
インターフェースを作成する
まず、攻撃方法を定義するインターフェースを作成します。
public interface IAttack
{
string Attack();
}
2. 具体的な攻撃クラスを別ファイルで作成する
次に、IAttack
インターフェースを実装する具体的な攻撃クラスを作成します。各クラスは別のファイルに分けて作成してください。
SwordAttack.cs
public class SwordAttack : IAttack
{
public string Attack()
{
return "剣で攻撃した!";
}
}
MagicAttack.cs
public class MagicAttack : IAttack
{
public string Attack()
{
return "魔法を使った!";
}
}
BowAttack.cs
public class BowAttack : IAttack
{
public string Attack()
{
return "弓で攻撃した!";
}
}
クラスを別ファイルで管理することで、コードが整理され、理解しやすくなります。
3. Character
クラスを作成する
キャラクターがどの攻撃方法を使用するかを管理するCharacter
クラスを作成します。
public class Character
{
public string Name { get; set; }
public IAttack AttackType { get; set; }
public Character(string name, IAttack attackType)
{
Name = name;
AttackType = attackType;
}
public string PerformAttack()
{
return $"{Name}は{AttackType.Attack()}";
}
}
4. Form1
クラスを更新する
最後に、Form1
クラスをStrategyパターンに合わせて更新します。
public partial class Form1 : Form
{
private Character character;
public Form1()
{
InitializeComponent();
comboBox1.Items.Add("剣");
comboBox1.Items.Add("魔法");
comboBox1.Items.Add("弓");
character = new Character("勇者", new SwordAttack());
}
private void button1_Click(object sender, EventArgs e)
{
switch (comboBox1.SelectedItem.ToString())
{
case "剣":
character.AttackType = new SwordAttack();
break;
case "魔法":
character.AttackType = new MagicAttack();
break;
case "弓":
character.AttackType = new BowAttack();
break;
}
string result = character.PerformAttack();
MessageBox.Show(result);
}
}
ステップ5: 振り返りと応用
Strategyパターンを導入したことで、攻撃方法が増えた場合でも、簡単に新しいクラスを作成するだけで対応できるようになりました。また、Character
クラスのコードが非常にシンプルになり、他の動作にも応用が利く設計になりました。
振り返り
- 柔軟な設計: 新しい攻撃方法を追加する際に、既存のコードをほとんど変更せずに済むため、柔軟な設計が可能です。
- コードの分離: 各攻撃方法が独立したクラスに分かれているため、コードの見通しが良く、保守性が向上しました。
応用例
このパターンは、攻撃方法に限らず、他の選択肢や動作を切り替えるシーンでも使えます。例えば、キャラクターの移動方法や攻撃対象の選択などにも応用できます。
このチュートリアルを通じて、シンプルな設計からデザインパターンを導入するプロセスを学び、より柔軟で拡張性のあるコードを書けるようになりましょう。
ディスカッション
コメント一覧
まだ、コメントがありません