2014-05-13 3 views
1

에서 파생 된 기본 컨텍스트 BattleUnit에는 두 개의 상태 IdleEngaging이 있습니다. 이 두 상태는 상태를 처리하기 위해 BattleUnit의 인스턴스에 대한 참조를 갖습니다. BattleUnit에서 나는 Troop을 파생 시켰습니다. 따라서 두 개의 추가 상태 인 MarchingChasing이 있습니다. 내 State이로 설계한다면 :상태 핸들러는 상태 패턴의 컨텍스트에 종속적입니다.

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

상태 Idle에서와 Engaging 나는 BattleUnit의 인스턴스를 처리합니다. 나는 공격하거나 그냥 그냥 놀 수 있습니다. 하지만 Unit에서 종료되지 않는 BattleUnit의 인터페이스를 조작해야합니다. 마찬가지로 Troop:BattleUnit의 인스턴스를 Marching 또는 Chasing에 처리하려고합니다.

그래서 예상 한대로 유닛을 캐스팅 할 수 있습니까? 즉 :

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

PS :

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

더 나은 방법이 있나요 : 내가 좋아하는 Unit 내에서 State의 인스턴스가?

+0

State-class의 Unit에 대한 참조가 정말로 필요합니까? Unit에서 Handle-method를 호출하면 메서드에 매개 변수로 자신을 전달할 수 있습니다. Handle-method에서 올바른 유형으로 유닛을 캐스팅해야합니다. 그러나이 변경으로 인해 디자인이 조금 더 간단해질 수 있습니다. – artokai

+0

예, 해결 방법입니다. 나는 이것이 기본적으로 이중 파견 문제라고 말하는 다른 사람에게 물었다. @YuvalItzchakov의 해결책을 수정하면 효과가 있습니다. 그것은 추상 클래스 State를 유지하면서 템플릿은 구체적인 State를 유지합니다. – zoujyjs

답변

1

한 가지 방법은 예를 들어 상태가 BattleUnit를 조작하고 Troop를 조작하는 상태에 대해 국가가, 엔티티의 특정 유형을 위해 일을 갖는 것입니다. 그런 다음 BattleUnit 당신을위한 EngagingIdle 상태를 만들 것

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

:

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이 추상적이지는 않습니다!).

당신이 Troop 클래스는 MarchingStateChasingState라는 두 개의 클래스를 만들 것이며, 구조가 위에 설명 된 클래스와 유사 할 것이지만, 기본 클래스는 State<Troop> 될 것입니다 들어. Handle 메서드에서는 Troop 인스턴스에 대해 "Marching"및 "Chasing"논리를 수행합니다. 나중에 당신이 차량에 대해 "쫓는"상태 싶다고 결심했다면 이제, 당신은 만들 수 있습니다 다음

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의 "Chasing"논리는 레이더의 존재 여부를 확인하지만 Troop의 "Chasing"상태에서는이 작업을 수행하지 않을 것입니다. :)).

편집 1 :

@zoujyjs ... 당신의 의견에 대한 답변에서이 문제를 해결하는 방법은 두 가지 정말이 있습니다 :

첫 번째는 void Handle() 방법을 포함하는 IState 인터페이스를 생성하는 것 및 베이스 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 방법은 somethig에 같이 보일 것입니다 :

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

귀하의 Unit 클래스는 다음 State에 대한 참조를 보유 할 수 있으며, 위에서 언급 한 IState 인터페이스에 대한 필요성을 무시할 수 있습니다.

+0

이런 식으로'Unit'에 'State state'에 대한 참조를 유지해야하지만 Unit 상태가 숨겨져있는'Troop'에 또 다른'State state'가 있습니다.그러나 어쨌든 더 복잡한 요구 사항을 얻는다면 이것은 좋은 생각입니다. 나는 이제 해결 방법으로 캐스트를 사용하는 것 같아요. 나는 여기에 너무 많은 시간을 보냈는데, 우선 내 마감 시간을 맞추기가 더 좋다. :) – zoujyjs

+0

@zoujyjs ... 귀하의 의견을 고려하여 답변을 업데이트했습니다. "캐스트"방식을 사용하는 것으로 알고 있지만 그 대답은 다른 사람에게 유용 할 수 있습니다. :) – MotoSV

+0

동의 함 :) ..... – zoujyjs

1

상태에 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(); 
} 
+0

나는 이것이 잘될 것이라고 생각하지 않는다. 왜냐하면 나 또한 현재 상태를 나타내는'BattleUnit'에'State' 인스턴스를 가지고 있기 때문에 상태를 바꿀 규칙이 있습니다. 이렇게하면 모든 상태가 공통 루트에서 파생되지 않고 다른 루트가 파생됩니다. 나 맞아? – zoujyjs

+0

아니요 단위는 여전히 모든 것의 뿌리입니다. 상속 계층 구조를 변경하지 않았습니다. –

+0

이제 상태가 더 이상 동일하지 않습니다. 나는 단위로 상태를 바꿀 수 없다. – zoujyjs