java 제네릭을 디자인 문제(태계)
-
18-09-2019 - |
문제
내가 만든 상태계 및고 싶은 그것을 활용하는 제네릭에서 java.현재는지 볼 수 있는 방법이 작품을 만들고 꽤 고 코드입니다.이 디자인 문제에 접근해왔다 많은 시간 전에 임한 입력.heres 거친다.
class State { ... }
만 하나의 사본은 각각 별개의 주체(주로 익명의 클래스에 묶여 static final 변수)그것은 사용자 지정 데이터에 대한 각각의 상태에 있습니다.각 국가 객체에는 부모 상태(한 뿌리 상태)
class Message { ... }
각각의 메시지가 별도로 만들어지고 각 사용자 지정 데이터가 있습니다.그들은 서브 클래스는니다.거기에 하나의 루트 메시지를 클래스입니다.
class Handler { ... }
각각의 핸들러가 만든 한번만 찾으로 특정 국가/메시지 콤보입니다.
class StateMachine { ... }
현재의 추적을 유지 현재 상태와 모든 목록(State
,Message
) -> Handler
매핑됩니다.그것은 다른 기능을 뿐입니다.내가 노력을 계속 이 클래스 일반적이고 서브 클래스와 입력 매개변수로 사용상에서 나의 프로그램,그리고 각각의 시간과 다른 설정의 Message
's/ State
's/고 Handler
's.다른 StateMachine
's 것 서로 다른 매개변수를 자신의 처리기입니다.
접근
이 있는 상태 기계적 매핑됩니다.
class StateMachine<MH extends MessageHandler> {
static class Delivery {
final State state;
final Class<? extends Message> msg;
}
HashMap<Delivery, MH> delegateTable;
...
}
class ServerStateMachine extends StateMachine<ServerMessageHandler> {
...
}
할 수 있는 사용자 정의 처리기 위한 방법 이 특정한 상태 기계입니다.에 대한 매개 변수 처리기입니다.프로세스 방법을 덮어쓸 수 있습니다.그러나 처리기할 수 없는 매개 변수가 있는 메시지에 의해 유형입니다.
문제: 이를 사용하는 것 instanceof
정신 확인에 대한 각각의 메시지 처리기는(그것은 확실히 메시지를 받고 그것이 기대하).
접근 B
로 만들 수 있습니다 각각의 메시지를 핸들러를 매개 변수가 있는 메시지 입력
class MessageHandler<M extends Message> {
void process(M msg) { .... }
}
문제: 유형*삭제를 방지 나를 저장에서 이러한에서 좋은 hashmap 이후 모든 MessageHandler
's 에 입력됩니다.는 경우에 저장할 수 있는 그들을 지도에서,내가 할 수 있 적이 그들을 그들에게 전화와 적절한 가지 인자가.
접근 C
이 있는 상태의 개체를 처리하는 모든 메시지입니다.
class State<M extends Message> { ... }
class ServerState<M extends ServerMessage> extends State<M> { ... }
나는 메시지 처리기에 묶여 특정 상태계국(의 안에 그들을 두),(각각의 인스턴스에 상태 기계 것이 자신의 목록의 유효한다)수 있는 처리기의 것에 특정 유형입니다.(서버 상태계->서버에 메시지 처리기).
문제: 각 국가만을 처리할 수 있 하나의 메시지는 유형입니다.당신은 또한 당신을 잃게 부모는 아이디어 국가의 처리할 수 있는 다른 메시지보다는 아이다.유형*삭제를 방지합 StateMachine
에서 호출하면 현재 미국 프로세스 방법이 있습니다.
접근 D
메시지의 과정에 자신을 기반으로 상태가 됩니다.
문제: 정말로 간주므로,각각의 메시지가 있어야 한 다른 처리기의 현재 상태를 기반으로 기계 상태가 됩니다.보낸 사람이 알 수 없습니다 현재 StateMachine
's state.
E 접근법
에 대해 잊지 제네릭하고 하드 드 코드 상태 메시지 처리 가치 문입니다.
문제: 정신
안전 솔루션:
귀하의 의견을 주셔서 감사합니다 모두 나는 생각한 문제는 내가 줄어들지 않았이 좋은 문제(너무 많은 토론)heres 내가 지금입니다.
public class State { }
public class Message { }
public class MessageHandler<T extends Message> { }
public class Delivery<T extends Message> {
final State state;
final Class<T> msgClass;
}
public class Container {
HashMap<Delivery<? extends Message>, MessageHandler<? extends Message>> table;
public <T extends Message> add(State state, Class<T> msgClass, MessageHandler<T> handler) {
table.put(new Delivery<T>(state, msgClass), handler);
}
public <T extends Message> MessageHandler<T> get(State state, T msg) {
// UNSAFE - i cannot cast this properly, but the hashmap should be good
MessageHandler<T> handler = (MessageHandler<T>)table.get(new Delivery<T>(state, msg.getClass()));
return handler;
}
}
해결책
에 대한 접근 B,을 사용하지 않"좋은"hashmap.대신,쓰기,이종 형식이 안전한 컨테이너의 매핑 처리기 등 개체:
interface Handler<T extends Message> {
...}
interface Message {...}
interface HandlerContainer {
<T extends Message> void register(Class<T> clazz, Handler<T> handler);
<T extends Message> Handler<T> getHandler(T t);
}
class HandlerContainerImpl implements HandlerContainer {
private final Map<Class<?>,Handler<?>> handlers = new HashMap<Class<?>,Handler<?>>();
<T extends Message> void register(Class<T> clazz, Handler<T> handler) {
if (clazz==null || handler==null) {
throw new IllegalArgumentException();
}
handlers.put(clazz,handler);
}
//Type safety is assured by the register message and generic bounds
@SuppressWarnings("unchecked")
<T extends Message> Handler<T> getHandler(T t) {
return (Handler<T>)handlers.get(t.getClass());
}
}
다른 팁
E 와 함께 열거를 나타내는 상태 메시지는 아마도 가장 쉬운 방법입니다.하지만 그것은 아주 확장할 수 있습니다.
C 를 사용하는 방문자가 본 상태에서 클래스 파견 유형 메시지처럼 수 있는 최고의 옵션이 있습니다.선택 사 instanceof
그리고 방문자,나는 생각하는 방문자가 약간 청소기(이기는 하지만 여전히 어색).유형 erasure 문제가 야기한 주목할만한 어려움 및 처리에 메시지가 보이 다소니다.일반적인 상태계 표기는 국가의 중심으로 제어합니다.또한,당신은 있을 수 있는 방문자가 추상 클래스는 메시지에 대한 유형에 오류가 발생에 모든 국가할 수 있으므국 오류가-가을에 잘못된 메시지를위한 무료입니다.
C+방문자 것이 매우 유사하는 방법을 자주 사용을 구현하는 경우 주 컴퓨터에서 C 언어로 첫 번째 클래스 기능-"상태"에 의해 표현된 함수 포인터를 처리하는 메시지 현재 상태에서,그와 함께 반환하는 함수 포인터를 다음 국가능(가능하게 자).국기 제어 루프 단지를 가져오는 다음 메시지를 전달하는 현재 상태,기능 및 업데이트의 개념"현재의"때 그것을 반환합니다.
접근 E.에 대해 잊지 제네릭 사용하고,인터페이스가 있습니다.
class Message { ... }
class State { ... }
class Machine {
static State handle(State current, Message msg) {
...
}
}
class CustomMessage extends Message { ... }
class CustomState extends State { ... }
class CustomMachine {
static CustomState handle(CustomState current, CustomMessage msg) {
// custom cases
...
// default: generic case
return Machine.handle(current, msg);
}
}
접근 방식에서는 몇 가지 장소를 사용할 수 있습니다.일반 사용 POJO 클래스고에 주석을 처리할 수 있도록 관리자 입력 클래스를 실행하는 국기:
public class MyState {
@OnEntry
public void startStuff() {
...
}
@OnExit()
public void cleanup() {
..
}
}
거기에 몇 가지 개발의 구현,나는 생각한 과학적 도구 하나는 좋은 그러나 나는 오른쪽의 링크를 찾을 지금:http://mina.apache.org/introduction-to-mina-statemachine.html http://weblogs.java.net/blog/carcassi/archive/2007/02/finite_state_ma_1.html http://hubris.ucsd.edu/shared/manual.pdf
방법 F:
에 대해 잊지 제네릭지 않는 한 당신은 유형-특정 패턴이 있습니다.를 정의한 부부의 인터페이스는 당신이 원하는 시스템이 포함 아마도 같은 뭔가
interface StateMachineState<R extends StateMachineState,T> {
/* returns next state */
R execute(T otherState);
}
그리고 특정 상태 기계,열거형 사용을 확장하는 StateMachineState:
class OtherState {
public double x1;
public int i;
}
enum MyState extends StateMachineState<MyState,OtherState>
{
FOO {
MyState execute(OtherState otherState) {
otherState.x1 += 3.0;
otherState.i++;
return BAR;
}
},
BAR {
MyState execute(OtherState otherState) {
otherState.x1 -= 1.0;
otherState.i--;
return (i % 3 == 0) ? FOO : BAR;
}
},
}
다음을 할 수 있는 다음과 같습니다.
MyState state = MyState.FOO;
OtherState otherState = new OtherState();
otherState.i = 77;
otherState.x1 = 3.14159;
while (true)
{
state = state.execute(otherState);
/* do something else here */
}
(주의:코드는지 확인하에 대한 구문 오류)