WinformアプリでシンプルなコードからDecoratorパターンへ移行しよう

このチュートリアルでは、簡単な装飾プログラムを作成し、それをより柔軟で拡張しやすい設計に改善する方法を学びます。まず、シンプルなコードを書き、その後、Decoratorパターンというデザインパターンを導入して、コードの設計を改善していきます。

ステップ1: シンプルなコードを作成する

まずは、以下のシンプルなコードを見てください。このコードは、キャラクターに対して基本の攻撃と追加の効果を順次表示するものです。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        string attack = "剣で攻撃した!";

        if (checkBox1.Checked)
        {
            attack += " 炎のエンチャントを追加!";
        }

        if (checkBox2.Checked)
        {
            attack += " 氷のエンチャントを追加!";
        }

        MessageBox.Show(attack);
    }
}

このコードはシンプルで理解しやすいですが、装飾効果が増えるたびに条件文が追加され、コードが複雑になりがちです。

ステップ2: 問題点を理解する

この設計には、以下のような問題があります:

  • コードの肥大化: 攻撃に新しい効果を追加するたびに、条件文を追加する必要があり、コードが冗長で複雑になります。
  • 保守性の低下: 条件が増えるごとに、コードの可読性が下がり、バグが入り込みやすくなります。
  • 拡張性の問題: 新しい効果を追加するためには、既存のコードを変更しなければならず、変更箇所が複数に広がるため、拡張が困難です。

Decoratorパターンの利点

Decoratorパターンを導入することで、以下のような改善が見込めます:

  • コードの分離: 各効果を独立したクラスとして分離することで、攻撃クラスが単一の責任を持つようになり、コードの可読性が向上します。
  • 柔軟な拡張: 新しい装飾効果を追加する際には、既存のコードを変更する必要がなくなり、新しいデコレータクラスを作成するだけで対応できます。

ステップ3: Decoratorパターンの基本を学ぶ

Decoratorパターンは、オブジェクトに動的に新しい機能を追加する方法です。基本のコンポーネントと、それを装飾するデコレータクラスを使用します。

Decoratorパターンの構成

  • コンポーネントインターフェース: 共通の機能を定義するIAttackインターフェースを作成します。
  • 基本コンポーネントIAttackインターフェースを実装する基本の攻撃クラスBasicAttackを作成します。
  • デコレータクラスIAttackインターフェースを実装し、他のIAttackオブジェクトをラップして追加機能を提供するデコレータクラス(FireDecoratorIceDecorator)を作成します。

ステップ4: Decoratorパターンを適用する

IAttackインターフェースを作成する

まず、攻撃を定義するインターフェースを作成します。

public interface IAttack
{
    string Execute();
}

基本の攻撃クラスを作成する

次に、IAttackインターフェースを実装する基本の攻撃クラスを作成します。

public class BasicAttack : IAttack
{
    public string Execute()
    {
        return "剣で攻撃した!";
    }
}

デコレータクラスを作成する

デコレータクラスは、基本の攻撃に追加の機能を持たせます。ここでは、炎と氷のエンチャントを追加するデコレータを作成します。

public class FireDecorator : IAttack
{
    private readonly IAttack _attack;

    public FireDecorator(IAttack attack)
    {
        _attack = attack;
    }

    public string Execute()
    {
        return _attack.Execute() + " 炎のエンチャントを追加!";
    }
}
public class IceDecorator : IAttack
{
    private readonly IAttack _attack;

    public IceDecorator(IAttack attack)
    {
        _attack = attack;
    }

    public string Execute()
    {
        return _attack.Execute() + " 氷のエンチャントを追加!";
    }
}

Form1クラスを更新する

最後に、Form1クラスをDecoratorパターンに合わせて更新します。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        IAttack attack = new BasicAttack();

        if (checkBox1.Checked)
        {
            attack = new FireDecorator(attack);
        }

        if (checkBox2.Checked)
        {
            attack = new IceDecorator(attack);
        }

        MessageBox.Show(attack.Execute());
    }
}

ステップ5: 振り返りと応用

Decoratorパターンを導入したことで、攻撃に新しい効果を追加する際に、既存のコードをほとんど変更せずに済むようになりました。また、攻撃ロジックがクラスに分かれているため、コードが簡潔で理解しやすくなりました。

振り返り

  • 柔軟な設計: 新しい効果を追加する際に、既存のコードに影響を与えないため、柔軟な設計が可能です。
  • コードの分離: 各装飾効果が独立したクラスに分かれているため、コードの見通しが良く、保守性が向上しました。

応用例

このパターンは、攻撃効果に限らず、他の動作やオプションの追加でも応用できます。例えば、キャラクターの防御効果や特殊効果の追加などにも使えます。


このようにして、シンプルな設計からデザインパターンを導入し、より柔軟で拡張性のあるコードを書けるように学びましょう。