c# – What’s the best way to design a state machine with different object inheritance

Your question is a bit light on specific context. One particular distinction here is if your A-state machine and B-state machine can be abstracted into a common ancestor, the base-state machine.

Specifically, are you able to abstract all of the state machine features that A and B use, so that the logic for A using its AStateMachine is exactly the same as B using its BStateMachine?


If yes, then simply use this base-state machine as the type. This is the best in terms of reusability. When this is possible, this is the best approach to take.

public class BaseStateMachine {}
public class AStateMachine : BaseStateMachine {}
public class BStateMachine : BaseStateMachine {}

public class Base
{
    private readonly BaseStateMachine _state;

    public Base(BaseStateMachine state)
    {
        _state = state;
    }

    public void DoJob()
    {
        Console.WriteLine(_state.MyValue);
    }
}

public class A : Base
{
    public A(AStateMachine state) : base(state)
    {

    }
}

public class B : Base
{
    public B(BStateMachine state) : base(state)
    {

    }
}

This is by far the easier approach, since the logic for both A and B only has to be written once (in Base). However, this relies on the idea that the handling between them is similar enough that the state machines have the same reusable interface.

If this is possible, this is usually the best approach to take. But that’s an if

Note: I’m ignoring the distinction between interfaces and base classes here, as they are equivalent for the current discussion. Whether you use the BaseStateMachine class or IStateMachine interface, the same principle applies.


If no, this is not possible, and the logic for A using its AStateMachine is fundamentally different from B using its BStateMachine, then your Base should define abstract/virtual methods where A and B can override it with their own implementation. Since the assumption here is that there is no default implementation before it gets overridden, abstract is the way to go.

public class AStateMachine
{
    public string GetFoo() => "State from A";
}

public class BStateMachine
{
    public int GetBar() => 123;
}

public abstract class Base
{
    protected abstract string GetStateValue();

    public void DoJob()
    {
        string result = GetStateValue();

        Console.WriteLine(result);
    }
}

public class A : Base
{
    private readonly AStateMachine _state;

    public A(AStateMachine state)
    {
        _state = state;
    }

    protected override string GetStateValue()
    {
        return _state.GetFoo();
    }
}

public class B : Base
{
    private readonly BStateMachine _state;

    public B(BStateMachine state)
    {
        _state = state;
    }

    protected override string GetStateValue()
    {
        int stateValue = _state.GetBar();

        return stateValue.ToString();
    }
}

Notice how Base itself doesn’t have or use a state machine. Since the two state machines are inherently different, they shouldn’t be stored in Base, but rather in A and B themselves. Base can access its virtual methods to gain access to the content of the state machine. Base cannot access the state machine directly, since Base cannot account for every different way of handling every different state machine (doing so would constitute an OCP violation).

This is more cumbersome to write because you need to individually implement the state machine handling logic for both A and B, but it gives you a great degree of freedom on how these machines can be handled in wildly different ways while still ensuring that your Base has a reusable way of accessing these wildly different state machines.