2014-12-30 3 views
1

궁극적 인 목표는 클라이언트가 mainState으로 구성된 항목을 만들 수있는 StateTransitionTable을 갖는 것입니다. 그 주 상태 (ArrayList에 저장 됨)에서 점프. 이 응용 프로그램 인 Script은 루프에서 실행되며 전환 테이블은 다소 자체적으로 작동해야합니다.메서드 수준에서 선언 된 제네릭 형식을 사용할 수 없습니다. 다른 형식으로 선언 된 제네릭 형식이 필요합니다.

이 경우 enum을 사용하지 마십시오. 그들은 잘 확장되지 않습니다.

각 상태는 ScriptTransitionTable 인스턴스에 모두 액세스해야합니다. 스크립트는 의무를 수행하는 것이며, 테이블은 인덱스를 사용하여 다음 상태로 전환합니다. 인덱스는 전환이 항목에 추가 된 순서에 따라 다릅니다. 생성 된 첫 번째 항목은 사용 된 첫 번째 항목입니다.

응용 프로그램의 루프에서 현재 항목에 저장된 상태에 액세스해야하며 process을 호출해야합니다. 현재 스크립트 인스턴스를 전달 Script 전화 <T extends Script> process(T) : 방법은 currentEntry에서 mainState을 잡고와 Script 인스턴스와 현재 테이블 인스턴스를 전달하여 process(T, TransitionTable) 방법을 호출하는

//this method is called repeatedly 
public void loop() { 
    table.process(this); 
} 

. 주의 프로세스 메서드 내에서 script 매개 변수를 사용하여 스크립트에 대한 정보에 액세스하고 테이블을 사용하여 전환을 수행 할 수 있어야합니다. 나에게 오류를주는 한 문장을 제외하고 나는 모든 것을 순서대로 가졌다.

public interface State<T extends Script> { 
    void process(T script, TransitionTable table); 
} 

TransitionTable : 기록을 위해

public class TransitionTable { 
    private Map<State<?>, StateNode> entries = new HashMap<>(); 
    private StateNode currentNode, startNode; 

    public <T extends Script> void process(T script) { 
     currentNode.mainState.process(script, this); //Compile-time error here 
    } 

    public StateNode createEntry(State<?> state) { 
     StateNode node = new StateNode(state); 
     map.put(state, node); 

     if(startNode == null) 
      startNode = currentNode = node; 

     return node; 
    } 

    public void transitionTo(int index) { 
     State<?> nextState = currentNode.states.get(index); 
     if(nextState == null) 
      nextNode = startNode.mainState; 

     currentNode = entries.get(nextNode); 
    } 

    public static final class StateNode { 
     private ArrayList<State<?>> states = new ArrayList<>; 
     private State<?> mainState; 

     public StateNode(State<?> state) { 
      mainState = state; 
     } 

     public StateNode addTransition(State<?> state) { 
      states.add(state); 
      return this; 
     } 
    } 
} 

process에 대한 제네릭 형식 선언은 실제 스크립트의 유형의 용도를 허용 싼 해킹입니다. 다른 유형을 입력 할 수 있지만 항상 동일한 유형입니다. 싸구려 핵.

내가 오류는 다음과 같습니다

방법 과정 (캡처 # 4의 StateTransitionTable?) 유형의 국가에서는 인수 (T, StateTransitionTable)

적용되지 않습니다 이 이유에 대한 이유를 이해합니다. 클라이언트가 State의 유형 매개 변수와 다른 메소드의 유형 매개 변수에 대한 하위 유형을 지정할 가능성이 있습니다. 내가 이해할 수없는 것은 이런 상황에 어떻게 대처해야하는지입니다.

나는 이와 같은 디자인 문제에 대한 태그가 있음을 알고 있지만 이름은 기억이 안납니다. 누군가 그것을 추가 할 수 있다면 그것은 인정 될 것입니다. 관련 정보를 빠뜨린 것 같으면

+0

당신이 물어 보지 않았지만, GoF에서 State 패턴을 사용 해본 적이 있습니까? –

+0

@EricStein 이것은 점프 상태를 지정하는 복잡성이 추가된다는 점을 제외하면 실제로 상태 패턴과 매우 유사합니다. 나는 내가 바꿀 상태에 대한 참조가 없기 때문에'table.changeState (state)'와 같은 것을 사용하여 쉽게 상태를 변경할 수 없다. 그래서'transitionTo' 메소드가 인덱스를 받아들이고, 클라이언트가'addTransition'을 사용하여 액세스 할 수있는 상태를 지정할 수있게했습니다. 내가 현재 상태에서 (반복 할 필요없이) 점프하려는 상태에 액세스 할 수 있다면, 나는 –

답변

1
interface State<T extends Script> { 

} 

우리는 State의 서브 클래스가를 인스턴스화 될 때까지 일 무슨 T 확신 할 수 없다. Script의 하위 유형이 될 것이지만 어느 것이 확실하지는 않습니다.

TransitionTable 내에서 상태를 인스턴스화하지 않으므로 TransitionTable에 전달되는 상태에 대해 Script 하위 유형이 사용되고 있는지 여부를 알 수 없습니다. 어떤 하위 유형이 주에 사용되는지 확실하지 않으므로 <T extends Script>이 올바른 하위 유형인지는 확신 할 수 없습니다.

이 문제를 해결하기 위해 TransitionTable에 제네릭 형식 인수를 사용했습니다. 상태의 타입. 나는 정기적으로 중첩 된 클래스에 정적 중첩 클래스에서 StateNode을 변경, 그래서 제네릭 형식을 상속 '

State<DemoScript> start = new StartState(); 
State<DemoScript> walkToA = new WalkToAState(); 
State<DemoScript> walkToB = new WalkToBState(); 
State<DemoScript> dance = new DanceState(); 

TransitionTable<State<DemoScript>> table = new TransitionTable(); 
table.createNode(start).addTransition(walkToA).addTransition(walkToB); 
table.createNode(dance).addTransition(walkToA).addTransition(walkToB); 
table.createNode(walkToA).addTransition(dance); 
table.createNode(walkToB).addTransition(dance); 

public void loop() { 
    table.getCurrentState().process(this, table); 
} 

돈 : 그런 다음 내 모든 유형을 선언 곳으로 처리를 제기

public class TransitionTable<T extends Script<?>> { 
    private Map<T, StateNode> entries = new HashMap<>() 
    private StateNode current, start; 

    public T getCurrentState() { 
     return current.state; 
    } 

    public StateNode createNode(T state) { 
     StateNode entry = new StateNode(state); 
     entries.put(state, entry); 

     if (startNode == null) { 
      startNode = currentNode = entry; 
     } 
     return entry; 
    } 

    public class StateNode { 
     private ArrayList<T> nodes = new ArrayList<>(); 
     private T state; 

     private StateNode(T state) { 
      this.state = state; 
     } 

     public StateNode addTransition(T state) { 
      nodes.add(state); 
      return this; 
     } 
    } 
} 

그것이 얼마나 장황한 지에 대해 의견을 말하지 않는다; 나는 그것을 앞으로 나아갈 것입니다.

+0

이 솔루션은 확실히 좋습니다. 왜냐하면 대부분 존재하지 않는 전제 조건으로 작업했기 때문입니다. 구현시 TransitionTable의 각 인스턴스는 실행될 스크립트 유형 1 개만 지원합니다. 여러 스크립트 유형이 지원되어야한다고 생각했지만 분명히 오해했습니다. –

+0

@DavidtenHove 나는 당신이 그것을 어떻게 가정했는지를 알 수있다; 메서드에 대한 type 매개 변수가 해당 메서드에'Script' 유형을 전달하기위한 값싼 해킹이라고 명시해야했습니다. 메소드가 thay generic을 사용하여 다른 'Script' 유형을 받아 들일 수 있었지만, 같은 유형이 될 것입니다. 그것이 나쁜 디자인이 당신을 얻는 것입니다 : 오해들. 나는 미래에 더 분명하게 될 것을 확신 할 것이다. 오해로 인해 유감입니다. –

+0

문제 없습니다. 오해를 해결하는 것은이 웹 사이트가있는 것입니다 :) –

0

어떻게 든 처리하려는 스크립트가 현재 상태로 받아 들여질 수 있는지 확인해야합니다. 이 컴파일을하는 (쉬운) 방법을 보지 마라. 런타임에이 작업을 수행하는 한 가지 방법은 Class<T> 참조를 해당 주에 저장하고 Class.isAssignableFrom으로 전화하는 것입니다.

또 다른 방법은 호출 코드가 문제 자체를 분류하도록하는 것입니다.당신은 당신의 TransitionTable에서 process 방법을 제거하고 이런 식으로 대체하여이 작업을 수행 할 수 있습니다 :

public <T extends Script> Processor<T> getProcessorForCurrentState() { 
    //This cast can't go wrong because we haven't bound T yet 
    State<T> current = (State<T>)currentNode.mainState; 
    return new Processor<T>(current); 
} 

public static class Processor<T extends Script> { 
    private State<T> state; 
    private Processor(State<T> state) { 
     this.state = state; 
    } 

    public void process(T script) { 
     //TODO make sure this isn't called twice 
     this.state.process(script); 
    } 
} 

그러나 다시,이 표에 대신 클라이언트 코드를 검사하는 런타임을 릴레이합니다. 이것이 바람직한 경우 상황에 따라 다릅니다.

+0

수 주셔서 감사하지만,이 솔루션은 만족스럽지 않습니다. 나는 런타임 해결책을 찾고 있지 않으며 절대적으로 필요하지 않는 한 나는 down-casting의 팬이 아니다. 다시 한번,이 시간을내어 주셔서 감사합니다. 그러나 실제로 문제에 대한 해결책을 찾았습니다. 지금은 대답을 게시 할 것입니다. –

+0

내 대답을 게시했습니다. 관심이 있다면 –

관련 문제