An Embedded Engineer’s Blog

とある組み込みエンジニアの備忘録的なブログです。

UMLのステートマシン図を実装する for C# - その3

まえがき

今回は、前回説明したサンプルのエアコンステートマシンを実装していきます。

f:id:an-embedded-engineer:20190414174321p:plain
エアコンステートマシン


StateMachine クラス

StateMachine クラスを継承したステートマシンを実装します。
エアコンステートマシンでは、3 つのステートマシンを実装する必要があります。

名称 詳細 備考
ModelStateMachine エアコン全体のステートマシン
RunningStateMachine Running 状態のサブステートマシン
CleanStateMachine Clean 状態のサブステートマシン


ModelStateMachine クラス

ModelStateMachine クラスは、エアコンモデルの AirConditioner クラス(後述)を引数として受け取るコンストラクタを定義します。
コンストラクタ内では、各状態(State)からモデルの処理を呼び出し可能にするため、エアコンモデルを Public プロパティにセットします。
その後、StateMachine クラスで定義された ChangeToInitalState メソッドを呼び出すことで初期状態への遷移を開始します。
また、継承元の StateMachine クラスで宣言された抽象メソッドの GetInitialState を定義し、ステートマシンの初期状態を返すようにします。


public class ModelStateMachine : StateMachine
{
    // エアコンモデル
    public AirConditioner Model { get; }

    // コンストラクタ
    public ModelStateMachine(AirConditioner model)
    {
        this.Model = model;

        this.ChangeToInitialState();
    }

    // 初期状態取得
    protected override State GetInitialState()
    {
        return InitialState.Instance;
    }
}


RunningStateMachine クラス

ModelStateMachine クラスはサブステートマシンなので、親である ModelStateMachine クラスを引数として受け取るコンストラクタを定義します。
コンストラクタでは、受け取った ModelStateMachine クラス、およびそこから取得できるエアコンモデル(AirConditioner クラス)を Public プロパティにセットします。 あとは ModelStateMachine クラスと同様、ChangeToInitialState メソッドの呼び出しと、GetInitialState メソッドの定義を行います。
RunningStateMachine の初期状態は、前回説明したとおり、Cool 状態となります。 (システム初期化後初めて Running 状態に遷移した場合は、履歴が残されていないため、Cool 状態に遷移する)

public class RunningStateMachine : StateMachine
{
    // 親ステートマシン
    public ModelStateMachine Parent { get; }

    // エアコンモデル
    public AirConditioner Model { get; }

    // コンストラクタ
    public RunningStateMachine(ModelStateMachine parent)
    {
        this.Parent = parent;

        this.Model = parent.Model;

        this.ChangeToInitialState();
    }

    // 初期状態取得
    protected override State GetInitialState()
    {
        return CoolState.Instance;
    }
}


CleanStateMachine クラス

CleanStateMachine クラスも RunningStateMachine と同様に定義します。 CleanStateMachine の初期状態は StainLevelAnalysis 状態となります。

public class CleanStateMachine : StateMachine
{
    // 親ステートマシン
    public ModelStateMachine Parent { get; }

    // エアコンモデル
    public AirConditioner Model { get; }

    // コンストラクタ
    public CleanStateMachine(ModelStateMachine parent)
    {
        this.Parent = parent;

        this.Model = parent.Model;

        this.ChangeToInitialState();
    }

    // 初期状態取得
    protected override State GetInitialState()
    {
        return StainLevelAnalysisState.Instance;
    }
}


Trigger クラス

Trigger クラスを継承した各トリガを実装します。
エアコンステートマシンでは、7 つのトリガを実装する必要があります。

名称 詳細 備考
InitializedTrigger 初期化完了トリガ
SwitchStartTrigger スタートボタン押下トリガ
SwitchStopTrigger ストップボタン押下トリガ
SwitchCoolTrigger 冷房ボタン押下トリガ
SwitchHeatTrigger 暖房ボタン押下トリガ
SwitchDryTrigger 除湿ボタン押下トリガ
SwitchCleanTrigger クリーニングボタン押下トリガ


各トリガはシステム内で 1 つのインスタンスのみを持つべきなので、シングルトンで実装します。
また、状態遷移時にエフェクトを実行する必要がある場合は、Effect クラスを継承したエフェクトをベースクラスのコンストラクタに受け渡します。

InitializedTrigger クラス

public sealed class InitializedTrigger : Trigger
{
    public static InitializedTrigger Instance { get; private set; } = new InitializedTrigger();

    public InitializedTrigger() : base("Initialized Trigger")
    {
    }
}


SwitchStartTrigger クラス

public sealed class SwitchStartTrigger : Trigger
{
    // シングルトンインスタンス
    public static SwitchStartTrigger Instance { get; private set; } = new SwitchStartTrigger();

    // コンストラクタ
    public SwitchStartTrigger() : base("Switch Start Trigger", SwitchStartEffect.Instance)
    {
    }
}


SwitchStopTrigger クラス

public sealed class SwitchStopTrigger : Trigger
{
    // シングルトンインスタンス
    public static SwitchStopTrigger Instance { get; private set; } = new SwitchStopTrigger();

    // コンストラクタ
    public SwitchStopTrigger() : base("Switch Stop Trigger", SwitchStopEffect.Instance)
    {
    }
}


SwitchCoolTrigger クラス

public sealed class SwitchCoolTrigger : Trigger
{
    // シングルトンインスタンス
    public static SwitchCoolTrigger Instance { get; private set; } = new SwitchCoolTrigger();

    // コンストラクタ
    public SwitchCoolTrigger() : base("Switch Cool Trigger")
    {
    }
}


SwitchHeatTrigger クラス

public sealed class SwitchHeatTrigger : Trigger
{
    // シングルトンインスタンス
    public static SwitchHeatTrigger Instance { get; private set; } = new SwitchHeatTrigger();

    // コンストラクタ
    public SwitchHeatTrigger() : base("Switch Heat Trigger")
    {
    }
}


SwitchDryTrigger クラス

public sealed class SwitchDryTrigger : Trigger
{
    // シングルトンインスタンス
    public static SwitchDryTrigger Instance { get; private set; } = new SwitchDryTrigger();

    // コンストラクタ
    public SwitchDryTrigger() : base("Switch Dry Trigger")
    {
    }
}


SwitchCleanTrigger クラス

public sealed class SwitchCleanTrigger : Trigger
{
    // シングルトンインスタンス
    public static SwitchCleanTrigger Instance { get; private set; } = new SwitchCleanTrigger();

    // コンストラクタ
    public SwitchCleanTrigger() : base("Switch Clean Trigger")
    {
    }
}


Effect クラス

Effect クラスを継承した各エフェクトを実装します。
エアコンステートマシンでは、3 つのエフェクトを実装する必要があります。

名称 詳細 備考
SwitchStartEffect スタートボタン押下エフェクト
SwitchStopEffect ストップボタン押下エフェクト
CleanEndEffect クリーニング完了エフェクト


各エフェクトはシステム内で 1 つのインスタンスのみを持つべきなので、シングルトンで実装します。
また、ベースクラスのEffectクラスで宣言された抽象メソッドExecuteActionをオーバーライドすることで、エフェクト固有の動作を定義します。

SwitchStartEffect クラス

SwitchStartEffectクラスでは、スタートボタンが押下され、Running状態に遷移する際に行われる処理を定義します。

protected override void ExecuteAction(StateMachine context)
{
    // 親ステートマシン(ModelStateMachine)を取得
    var stm = context.GetAs<ModelStateMachine>();

    // モデル(AirConditioner)を取得
    var model = stm.Model;

    // エアコンのスタート処理を呼び出し
    model.Start();
}


ソース全体を示します。

public sealed class SwitchStartEffect : Effect
{
    // シングルトンインスタンス
    public static SwitchStartEffect Instance { get; private set; } = new SwitchStartEffect();

    // コンストラクタ
    public SwitchStartEffect() : base("Switch Start Effect")
    {
    }

    // エフェクトアクション
    protected override void ExecuteAction(StateMachine context)
    {
        var stm = context.GetAs<ModelStateMachine>();

        var model = stm.Model;

        model.Start();
    }
}


SwitchStopEffect クラス

SwitchStoptEffectクラスでは、ストップボタンが押下され、Stop状態に遷移する際に行われる処理を定義します。

protected override void ExecuteAction(StateMachine context)
{
    // 親ステートマシン(ModelStateMachine)を取得
    var stm = context.GetAs<ModelStateMachine>();

    // モデル(AirConditioner)を取得
    var model = stm.Model;

    // エアコンの停止処理を呼び出し
    model.Stop();
}


ソース全体を示します。

public sealed class SwitchStopEffect : Effect
{
    // シングルトンインスタンス
    public static SwitchStopEffect Instance { get; private set; } = new SwitchStopEffect();

    // コンストラクタ
    public SwitchStopEffect() : base("Switch Stop Effect")
    {
    }

    // エフェクトアクション
    protected override void ExecuteAction(StateMachine context)
    {
        var stm = context.GetAs<ModelStateMachine>();

        var model = stm.Model;

        model.Stop();
    }
}


CleanEndEffect クラス

CleanEndEffectでは、クリーニング処理が完了し、Running状態に遷移する際に行われる処理を定義します。

// エフェクトアクション
protected override void ExecuteAction(StateMachine context)
{
    // 親ステートマシン(ModelStateMachine)を取得
    var stm = context.GetAs<ModelStateMachine>();

    // モデル(AirConditioner)を取得
    var model = stm.Model;

    // エアコンのクリーニング完了処理を呼び出し
    model.CleanEnd();
}


ソース全体を示します。

public sealed class CleanEndEffect : Effect
{
    // シングルトンインスタンス
    public static CleanEndEffect Instance { get; private set; } = new CleanEndEffect();

    // コンストラクタ
    public CleanEndEffect() : base("Clean End Effect")
    {
    }

    // エフェクトアクション
    protected override void ExecuteAction(StateMachine context)
    {
        var stm = context.GetAs<ModelStateMachine>();

        var model = stm.Model;

        model.CleanEnd();
    }
}


次回予告

次回も引き続きステートマシンの実装を行っていきます。

an-embedded-engineer.hateblo.jp