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

时间:2014-05-13 15:07:57

标签: c# oop design-patterns state

我有一个源自BattleUnit的基本上下文Unit,它有两个州IdleEngaging。并且这两个状态将引用BattleUnit的实例来处理状态。从BattleUnit我推导出一个Troop,它可以移动,因此有另外两个状态MarchingChasing。因此,如果我的State被设计为:

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

IdleEngaging状态下,我想处理BattleUnit的实例。我可以攻击或只是闲着。但我需要操纵BattleUnit的界面,Unit可能不会退出Troop:BattleUnit。同样,我想在MarchingChasing中处理public class Engaging : State { public void Handle() { Troop trp = (Troop)unit; // trp.troopMethod() which is not virtual } } 的实例。

那么我可以将单位投射到我期望的班级吗?即:

State

PS:我的Unit中有一个public class Unit{ private State state; //... } 的实例,如:

{{1}}

有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

如何让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();
}

答案 1 :(得分:1)

您可以尝试的一种方法是让状态为特定类型的实体工作,例如操纵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();
}

然后,您将为Engaging创建IdleBattleUnit州:

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的类,其结构类似于上面列出的类,但基类为{{1} }。在State<Troop>方法中,您将执行&#34; Marching&#34;和#34;追逐&#34; Handle实例的逻辑。如果你后来决定你想要一个&#34;追逐&#34;对于车辆的状态,您可以创建以下内容:

Troop

然后,此类将接受构造函数中的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方法将执行&#34;追逐&#34;车辆的逻辑。即使状态的目的相似,使不同实体具有不同状态的一个原因是允许您在您的州内实施特殊逻辑,例如:在这种情况下,&#34;追逐&#34; Handle的逻辑检查是否存在雷达,但在&#34;追逐&#34;状态为Vehicle你不想这样做(在这个例子中反正:))。

编辑1:

@zoujyjs ...在回答你的评论时,有两种方法可以解决这个问题:

第一种方法是创建一个Troop接口,该接口将包含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>类中删除泛型参数和Unit属性,并且每个派生状态仍然会接受具体类型,即BattleUnit或Troop,而不是将其传递到基础State类它将引用作为成员变量。因此,您的State方法看起来像是:

Handle

您的public override void Handle() { _unit.X = ????; _unit.Y = ????; } 课程可以保留对Unit的引用,您可以忽略对上述State界面的需求。