簡體   English   中英

java泛型設計問題(狀態機)

[英]java generics design problem (state machine)

我創建了一個狀態機,並希望它利用java中的泛型。 目前我沒有看到我可以使這項工作的方式,並獲得漂亮的代碼。 我確定此設計問題已經多次接近,我正在尋找一些輸入。 這是一個粗略的輪廓。

class State { ... }

每個不同的狀態對象只有一個副本(大多數是與靜態最終變量綁定的匿名類),它具有每個狀態的自定義數據。 每個狀態對象都有一個狀態父(有一個根狀態)

class Message { ... } 

每條消息都是單獨創建的,每條消息都有自定義數據。 他們可以互相分類。 有一個根消息類。

class Handler { ... } 

每個處理程序只創建一次並處理特定的狀態/消息組合。

class StateMachine { ... }

當前跟蹤當前狀態,以及所有( StateMessage ) - > Handler映射的列表。 它還有其他功能。 我試圖保持這個類通用,並使用類型參數將其子類化,因為它在我的程序中使用了很多次,並且每次使用不同的Message / State /和Handler的集合。 不同的StateMachine將為其處理程序提供不同的參數。

方法A.

讓狀態機跟蹤所有映射。

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

允許我為這個特定的狀態機擁有自定義處理程序方法。 可以覆蓋handler.process方法的參數。 但是,處理程序無法通過消息類型進行參數化。

問題:這涉及對每個消息處理程序使用instanceof sanity檢查(確保它獲得它期望的消息)。

方法B.

讓我們按消息類型參數化每個消息處理程序

class MessageHandler<M extends Message> {
  void process(M msg) { .... }
}

問題:類型擦除會阻止我將它們存儲在一個漂亮的hashmap中,因為所有的MessageHandler都會以不同的方式輸入。 如果我可以將它們存儲在地圖中,我將無法撤回它們並用適當的爭論來稱呼它們。

方法C.

讓狀態對象處理所有消息。

class State<M extends Message> { ... }
class ServerState<M extends ServerMessage> extends State<M> { ... }

我有消息處理程序綁定到特定的狀態機狀態(通過將它們放在里面),(狀態機的每個實例都有自己的有效狀態列表),這允許處理程序具有特定類型。 (服務器狀態機 - >服務器消息處理程序)。

問題:每個州只能處理一種消息類型。 您也失去了父狀態可以處理與子狀態不同的消息的想法。 type erasure還可以防止StateMachine調用當前狀態的進程方法。

方法D.

根據狀態自己擁有消息的進程。

問題:從未真正考慮過,因為每個消息應該具有基於當前狀態機狀態的不同處理程序。 發件人不會知道當前StateMachine的狀態。

方法E.

忘記使用switch語句進行泛型和硬代碼狀態/消息處理。

問題: 理智

不安全的解決方案

感謝大家的投入,我認為問題是我沒有把這個減少到好問題(太多的討論)繼承人我現在擁有的東西。

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

}

方法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);
  }
}

帶有代表狀態和消息的枚舉的E可能是最簡單的。 但它不是非常可擴展的。

C使用狀態類中的訪問者模式來分發消息類型,看起來它可能是您最好的選擇。 鑒於instanceof和Visitor之間的選擇,我認為訪客稍微清潔一些(盡管仍然很尷尬)。 類型擦除問題確實帶來了顯着的困難,並且消息中的處理似乎有些倒退。 典型的狀態機符號以狀態為控制中心。 此外,您可以讓消息類型的Visitor抽象類在所有狀態上拋出錯誤,從而允許狀態在無效消息上免費獲得錯誤。

C + Visitor與我在C語言中實現狀態機或使用第一類函數的語言時經常使用的方法非常類似 - “state”由指向當前狀態中的函數處理消息的指針表示,該函數返回指向下一個狀態函數(可能是自身)的指針。 狀態機控制循環僅獲取下一條消息,將其傳遞給當前狀態函數,並在返回時更新“當前”的概念。

我在一些地方看到的方法是使用注釋。 使用常規POJO類並注釋它們由運行狀態機的管理器類型類處理:

public class MyState {
 @OnEntry
 public void startStuff() {
  ...
 }

 @OnExit() 
 public void cleanup() {
  ..
 } 
}

有幾個比較發達的實現,我認為科學工具箱一個是好的,但我現在不能找到合適的鏈接: http://mina.apache.org/introduction-to-mina-statemachine.html HTTP://博客。 java.net/blog/carcassi/archive/2007/02/finite_state_ma_1.html http://hubris.ucsd.edu/shared/manual.pdf

對於方法B,不要使用“漂亮”的hashmap。 相反,將異構的類型安全容器映射處理程序寫入Class對象:

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

    }

}

方法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 */        
}

(警告:代碼未經雙重檢查語法錯誤)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM