C#入門(第16回)

前回振り返り

State パターン

Stateパターンとは、状態がよく変わる処理を管理しやすくするデザインパターンです。状態に応じてオブジェクトの振る舞いが変わる場合に、状態を表すクラスを作成し、オブジェクトの状態を切り替えることで、条件分岐を減らすことができます。

Stateパターンには以下のような役割があります。

  • State: 状態を表す抽象クラスやインタフェース。状態毎に振る舞いが異なるメソッドのインタフェースを定義する。
  • ConcreteState: Stateのサブクラス。具体的な状態を表すクラスで、Stateのメソッドをオーバーライドして実装する。状態の切り替えもこのクラスが行う。
  • Context: 現在の状態を保持するクラス。Stateのインスタンスを持ち、利用者からの要求をStateに委譲する。状態の変更はConcreteStateが呼び出す。
  • Client: Stateパターンを利用するクラス。Contextのインスタンスを作成し、メソッドを呼び出す。
stateパターンのクラス図

前回演習問題の解答 例

問題1

問題文

State パターン を作成してみましょう。

wikipedia のJava実装例をC#で記述してみましょう。


解答例

public interface State
{
    void writeName(StateContext stateContext, String name);
}
class StateA : State
{

    public void writeName(StateContext stateContext, String name)
    {
        Console.WriteLine(name.ToLower());
        // コンテキストをStateBに遷移する
        stateContext.setState(new StateB());
    }
}
class StateB : State
{

    private int count = 0;
    public void writeName(StateContext stateContext, String name)
    {
        Console.WriteLine(name.ToUpper());
        // StateBのwriteName()が2度呼び出された後にコンテキストをStateAに遷移する
        if (++count > 1)
        {
            stateContext.setState(new StateA());
        }
    }
}
public class StateContext
{
    private State myState;
    public StateContext()
    {
        setState(new StateA());
    }

    // 通常は、Stateインタフェースを実装しているクラスによってのみ呼び出される
    public void setState(State newState)
    {
        this.myState = newState;
    }

    public void writeName(String name)
    {
        this.myState.writeName(this, name);
    }
}
public class TestClientState
{
    public static void Main(String[] args)
    {
        StateContext sc = new StateContext();
        sc.writeName("Monday");
        sc.writeName("Tuesday");
        sc.writeName("Wednesday");
        sc.writeName("Thursday");
        sc.writeName("Saturday");
        sc.writeName("Sunday");
    }
}

問題2

問題文

問1のクラスを身近なものにアレンジしてみましょう。


解答例

public interface IState
{
    void action(StateContext stateContext);
}
public class StateContext
{
    private IState? myState;
    public StateContext()
    {
        setState(new SunnyState());
    }

    // 通常は、Stateインタフェースを実装しているクラスによってのみ呼び出される
    public void setState(IState? newState)
    {
        this.myState = newState;
    }
    public bool isNotNullState()
    {
        return this.myState != null;
    }

    public void action()
    {
        this.myState.action(this);
    }
}

class SunnyState : IState
{
    private int cnt = 0;
    public void action(StateContext stateContext)
    {
        if (++cnt > 1)
            Console.Write("まだ");

        Console.WriteLine("晴れているので、洗濯物を干します。");

        int rndm = new Random().Next(3);
        if (rndm == 0)
        {
            stateContext.setState(null);
        }
        else if (rndm == 1)
        {
            stateContext.setState(new RainyState());
        }
        else
        {
            // 変更なし
            stateContext.setState(this);
        }
    }
}

class RainyState : IState
{
    private int cnt = 0;
    public void action(StateContext stateContext)
    {
        if (++cnt > 1)
        {
            Console.WriteLine("まだ雨が降っているので、家にいます。");
        }
        else
        {
            Console.WriteLine("雨が降ってきたので、洗濯物を取り込みます。");
        }

        int rndm = new Random().Next(3);
        if (rndm == 0)
        {
            stateContext.setState(null);
        }
        else
        {
            // 変更なし
            stateContext.setState(this);
        }
    }
}
public class TestClientState
{
    public static void Main(String[] args)
    {

        StateContext sc = new StateContext();
        while (sc.isNotNullState())
        {
            sc.action();
        }
    }
}

今回の演習問題

問題1

問題文

Factory Method パターン を作成してみましょう。

wikipedia のJava実装例をC#で記述してみましょう。

問題2

問題文

問1のクラスを身近なものにアレンジしてみましょう。

コメント