状态处理程序依赖于状态模式中的具体上下文



我有一个从Unit派生的基本上下文BattleUnit,它有两个状态IdleEngaging。为了处理状态,这两个状态将有一个对BattleUnit实例的引用。从BattleUnit我派生了一个Troop,它是可移动的,因此有两个额外的状态MarchingChasing。所以如果我的State被设计为:

public abstract class State {
    protected Unit unit;
    public abstract void Handle();
}

IdleEngaging状态下,我想处理BattleUnit的实例。我可以进攻,也可以无所事事。但是我需要操作BattleUnit的接口,Unit可能没有退出。同样,我想在MarchingChasing中处理Troop:BattleUnit的实例。

那么我可以将Unit转换为我期望的类吗?即:

public class Engaging : State {
    public void Handle() {
        Troop trp = (Troop)unit;
        // trp.troopMethod() which is not virtual
    }
}

PS:我有一个State的实例在我的Unit像:

public class Unit{
    private State state;
    //...
}

有更好的方法吗?

您可以尝试的一种方法是让您的状态为特定类型的实体工作,例如操作BattleUnit的状态和操作Troop的状态。因此,例如,您可以从类似于

的基本状态开始:
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();
}

然后为您创建EngagingIdle状态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 = ????;        
    }
}

然后对Idle状态执行相同的操作:

public class IdleState : State<BattleUnit>
{
    ...
}

这两种状态可以应用于任何从BattleUnit派生的实例(假设BattleUnit不是抽象的!)

对于您的Troop类,您将创建两个名为MarchingStateChasingState的类,结构将类似于上面概述的类,但基类将是State<Troop>。在Handle方法中,您将为Troop实例执行"行进"one_answers"追逐"逻辑。现在,如果您稍后决定您想要车辆的"追逐"状态,那么您可以创建以下内容:

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.
        }
    }
}

这个类将在构造函数中接受Vehicle实例,Handle方法将执行车辆的"追逐"逻辑。对于不同的实体有不同的状态的一个原因,即使状态的目的是相似的,是允许你在你的状态中实现特殊的逻辑,例如,在这种情况下,Vehicle的"追逐"逻辑检查雷达的存在,但在Troop的"追逐"状态下,你不会想要这样做(在这个例子中无论如何:))。

编辑1:

@zoujyjs……对于你的评论,有两种方法可以解决这个问题:

第一个是创建一个IState接口,它将包含一个void Handle()方法,并将由基本的State<T>类实现。然后,您的Unit类将具有protected IState CurrentState { get; set; }属性。在某些时候,每个派生类都将为这个属性分配一个新的状态:

this.CurrentState = new EngagingState(this); //  ...this is inside a BattleUnit instance
//  or
this.CurrentState = new MarchingState(this); //  ...this is inside a Troop instance

然后在某个时刻Unit类会简单地调用this.CurrentState.Handle()。没有泛型参数的IState的主要意义在于,Unit类不需要知道由状态管理的实例的类型。它所需要知道的是它必须调用Handle方法。在State<T>类上使用泛型参数的另一个好处是使实现逻辑更容易。

但是,泛型参数不是必需的,这就引出了第二个选项:

你可以从基础State类中删除通用参数和Unit属性,每个派生状态仍然会接受一个具体类型,即BattleUnit或Troop,但不是将其传递给基础State类,而是将其作为成员变量保存引用。因此,您的Handle方法看起来像:

public override void Handle()
{
    _unit.X = ????;
    _unit.Y = ????;
} 

你的Unit类可以保存对State的引用,你可以忽略对上面提到的IState接口的需要。

让State接受类型为Unit的泛型参数如何?

public abstract class State<out T> where T : Unit
{
   protected T unit;
   public abstract void Handle();
}
public class Engaging : State<Troop> {
  public void Handle() {
    unit.troopMethod();
}

最新更新