One approach you could try is having you states work for a specific type of entity, for example states to manipulate a BattleUnit
and states to manipulate a Troop
. So, for example you would start off with a base state similar to:
public abstract class State<T> where T : Unit
{
protected State(T unit)
{
this.Unit = unit;
}
protected T Unit { get; private set; }
public abstract void Handle();
}
You would then create the Engaging
and Idle
states for you BattleUnit
:
public class EngagingState : State<BattleUnit>
{
public EngagingState(BattleUnit unit)
: base(unit)
{
}
public override void Handle()
{
// Here you can implement the "engaging" logic for a BattleUnit.
this.Unit.X = ????;
this.Unit.Y = ????;
}
}
You would then do the same for the Idle
state:
public class IdleState : State<BattleUnit>
{
...
}
These two states could be applied to any instance that derives from BattleUnit
(assuming BattleUnit
is not abstract!).
For you Troop
class you would create two classes called MarchingState
and ChasingState
and the structure would be similar to the classes outlined above, but the base class would be State<Troop>
. In the Handle
method you would perform the "Marching" and "Chasing" logic for a Troop
instance. Now if you later decided that you would like a "Chasing" state for a vehicle, then you could create the following:
public class VehicleChasingState : State<Vehicle>
{
public VehicleChasingState(Vehicle unit)
: base(unit)
{
}
public override void Handle()
{
// Here you can implement the "chasing" logic for a Vehicle.
this.Unit.X = ????;
this.Unit.Y = ????;
if(this.Unit.HasRadar)
{
// Do special "chasing" logic when the vehicle has radar.
}
}
}
This class would then accept a Vehicle
instance in the constructor and the Handle
method would perform the "Chasing" logic for a vehicle. One reason for having different states for different entities, even if the purpose of the state is similar, is to allow you to implement special logic within your state, e.g. in this case the "Chasing" logic for a Vehicle
checks for the existence of radar, but in the "Chasing" state for a Troop
you would not want to do this (in this example anyway :)).
EDIT 1:
@zoujyjs ... In answer to your comment there are really two ways to solve this:
The first would be to create a IState
interface that will contain a void Handle()
method and would be implemented by the base State<T>
class. Your Unit
class would then have a protected IState CurrentState { get; set; }
property. Each of the derived classes, at some point, would assign a new state to this property:
this.CurrentState = new EngagingState(this); // ...this is inside a BattleUnit instance
// or
this.CurrentState = new MarchingState(this); // ...this is inside a Troop instance
Then at some point the Unit
class would simply call this.CurrentState.Handle()
. The main point of having an IState
, without generic parameters, is the Unit
class doesn't need to know the type of instance being managed by the state. All it needs to know is it has to call the Handle
method. The added benefit of having the generic parameter on the State<T>
class is to make it easier to implement logic.
But, the generic parameter is not necessary which leads me onto the second option:
You could remove the generic parameter and the Unit
property from the base State
class and each derived state would still accept a concrete type, i.e. BattleUnit or Troop, but instead of passing it onto the base State
class it would hold a reference as a member variable. Therefore your Handle
methods would look somethig like:
public override void Handle()
{
_unit.X = ????;
_unit.Y = ????;
}
Your Unit
class can then hold a reference to State
and you can ignore the need for the IState
interface mentioned above.