How to enforce an object field or property on a derived class without type issues?

darthstevenus

I'm making a turn-based game in Unity 3D. Since it's turn-based, I'm trying to implement a state machine to control the general flow of gameplay. The player will control several units, like in games like XCOM, and I think a state machine for each unit may also be useful to control whether a unit is idle, currently selected, destroyed, etc. Weapons could possibly use them as well with states like idle and targeting.

Point is, I'm trying to abstract my state logic as much as possible so it's universal among the different state machines while also DRYing up my code so I'm not repeating things. To accomplish this, I'm of course making use of interfaces, such as my IState interface here:

namespace MechGame.Common.States
{
    public interface IState
    {
        void EnterState();
        void ExitState();
        void UpdateState();
    }
}

The problem comes when I try to enforce fields on derived classes. I've been told many times to think of interfaces as a contract, telling a class "you must implement X, Y, and Z." I know some objects in my state machine logic will need some private member fields. I'd like to declare those fields in my base classes in order to DRY up my code, almost enforcing them the way an interface enforces methods. The problem is how do I declare the types of these object fields in a way that is abstract? For instance, each state machine will have a state manager that extends the StateManager class:

namespace MechGame.Common.States
{
    public interface IStateManager
    {
        State CurrentState { get; }
        void NextState();
        void TransitionToState(State state);
    }
}
using UnityEngine;

namespace MechGame.Common.States
{
    public abstract class StateManager : MonoBehaviour, IStateManager
    {
        protected State _current;
        public State CurrentState { get => _current; }

        public abstract void NextState();

        public virtual void TransitionToState(State state)
        {
            if(_current != null)
                _current.ExitState();

            _current = state;
            _current.EnterState();
        }

        public virtual void Update()
        {
            _current.UpdateState();
        }
    }
}

So for instance, I've got a class called CombatStateManager which extends StateManager and will control the flow of the game, player turn, enemy turn, etc. The problem is when I need to reference something like the StateManager's CurrentState field, which is of the abstract type State, I need to caste it to something more specific, such as in the OnCellClick method of the following class:

using MechGame.Common.States;
using MechGame.Selection;

namespace MechGame.Combat.States
{
    public class CellEventDispatcher : IStateEventDispatcher
    {
        private CombatStateManager _manager;

        public CellEventDispatcher(CombatStateManager manager)
        {
            _manager = manager;
        }

        public void DeregisterEventHandlers()
        {
            SelectionManager.OnCellClick -= OnCellClick;
        }

        void OnCellClick(int cellIndex, int buttonIndex)
        {
            ((CombatState)_manager.CurrentState).CellEventHandler.OnCellClick(cellIndex, buttonIndex);
        }

        public void RegisterEventHandlers()
        {
            SelectionManager.OnCellClick += OnCellClick;
        }
    }
}

This might not seem like too big a deal, but it's a problem that's beginning to crop up in many places. Basically, like how an interface enforces that some methods must be implemented, I'd like to have my base classes enforce that any children implement some necessary fields. But it's leading to type issues as seen above. Is there no good way to enforce fields on child classes like I want to? If not, how else could I DRY up that code? I don't know much about generics, is that something that would be useful here?

Jack T. Spades

You could implement a generic variant. Though this only works if each StateManager always works with only the same type of state.

For example:

    public interface IStateManager<TState> where TState : IState
    {
        TState CurrentState { get; }
        void NextState();
        void TransitionToState(TState state);
    }

    public class StateManager<TState>
     :  MonoBehaviour, IStateManager<TState> where TState : IState
    {
        //...
    }

    public class CombatStateManager : StateManager<CombatState>
    {
        //...
    }

Now when you access CurrentState on a CombatStateManager instance, it will always be of CombatState. The point here being always. So it cannot be another state type (unless derived from CombatState).

Also, as a bit of addon. It is unnecessary to have both, an abstract base class AND an interface. Unless you plan on the interface being a thing that will also be included in other, unrelated, classes an abstract base is enough. Your definition of what an interface does is not wrong but in practice it is better suited to think of it as "addon" functionality or if some sort of "multi inheritance" is what you're after.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How to return an object in its derived type without converting back to base class type?

How to enforce argument to be instance of constructor/class that is property of some object?

Specify field type in derived class

How can I narrow the type of a readonly property of a derived class?

Accessing property of derived type in a class derived from a generic class

How can I directly access my base class property in the derived class without using methods or constructors?

Declare object of derived class without specifying it

How to deserialize derived class with other derived class as property

How to access qabstractlistmodel derived class object as a property of another class from qml?

How to create an object of class property of type List<class>

How to assign values base class properties inside a derived class object without listing them one by one?

How to use FieldValue.increment() on the property of an object type document field

How to use a derived property on a base class?

How to call a property of the base class if this property is being overwritten in the derived class?

How to override a virtual base class property with a derived class property

C++ How to override class field with a different type (without template)?

How to define type of object property without using an interface

Convert base class instance to derived class without type casting in typescript

How to use a TypeScript generic function to enforce a property existing on a type passed in?

How to type-enforce object value to match object key?

Get derived class property from parent without repetitive casting

Create a new class with derived fields without duplicating field names

How to add object property to satisfy class type in Typescript?

How to return derived type while operating over base and derived class?

Cannot add a derived class object to a list of it's base class type

How to change reference of Base object to Derived which was instatiated as Derived type

Override child class inherited property with more derived type

How does GetType() knows the type of a derived class?

c# inheritance: change field data type and value in derived class