可能需要一点背景知识,但如果您有信心,请跳至问题。希望总结能说明问题。
概括
我有一个InputDispatcher
将事件(鼠标、键盘等)分派给一个Game
对象。
我想InputDispatcher
独立扩展Game
:InputDispatcher
应该能够支持更多的事件类型,但Game
不应该被迫使用所有这些。
背景
本项目使用 JSFML。
输入事件通过Window
类通过pollEvents() : List<Event>
. 您必须自己进行调度。
我创建了一个GameInputDispatcher
类来将事件处理与处理窗口框架等事情分离。
Game game = ...;
GameInputDispatcher inputDispatcher = new GameInputDispatcher(game);
GameWindow window = new GameWindow(game);
//loop....
inputDispatcher.dispatch(window::pollEvents, window::close);
game.update();
window.render();
此示例的循环已被简化
class GameInputDispatcher {
private Game game;
public GameInputDispatcher(Game game) {
this.game = game;
}
public void dispatch(List<Event> events, Runnable onClose) {
events.forEach(event -> {
switch(event.type) {
case CLOSE: //Event.Type.CLOSE
onClose.run();
break;
default:
// !! where I want to dispatch events to Game !!
break;
}
}
}
}
问题
在 ( ) 正上方的代码中,我可以通过在默认情况下创建和调用GameInputDispatcher
来分派事件。Game
Game#onEvent(Event)
game.onEvent(event)
但这将强制Game
编写用于排序和调度鼠标和键盘事件的实现:
class DemoGame implements Game {
public void onEvent(Event event) {
// what kind of event?
}
}
问题
如果我想从InputDispacher
into提供事件Game
,我怎么能这样做,同时避免违反接口隔离原则?(通过声明所有监听方法:onKeyPressed,
onMouseMoved , etc.. inside of
Game`,即使它们可能不会被使用)。
Game
应该能够选择它想要使用的输入形式。支持的输入类型(如鼠标、键、操纵杆...)应该通过 缩放InputDispatcher
,但Game
不应强制支持所有这些输入。
我的尝试
我建立:
interface InputListener {
void registerUsing(ListenerRegistrar registrar);
}
Game
将扩展此接口,允许InputDispatcher
依赖InputListener
并调用该registerUsing
方法:
interface Game extends InputListener { }
class InputDispatcher {
private MouseListener mouseListener;
private KeyListener keyListener;
public InputDispatcher(InputListener listener) {
ListenerRegistrar registrar = new ListenerRegistrar();
listener.registerUsing(registrar);
mouseListener = registrar.getMouseListener();
keyListener = registrar.getKeyListener();
}
public void dispatch(List<Event> events, Runnable onClose) {
events.forEach(event -> {
switch(event.type) {
case CLOSE:
onClose.run();
break;
case KEY_PRESSED:
keyListener.onKeyPressed(event.asKeyEvent().key);
break;
//...
}
});
}
}
Game
子类型现在可以实现任何支持的侦听器,然后注册自己:
class DemoGame implements Game, MouseListener {
public void onKeyPressed(Keyboard.Key key) {
}
public void registerUsing(ListenerRegistrar registrar) {
registrar.registerKeyListener(this);
//...
}
}
尝试问题
尽管这允许Game
子类型只实现它们想要的行为,但它强制任何Game
声明registerUsing
,即使它们没有实现任何侦听器。
这可以通过创建registerUsing
一个default
方法来解决,让所有侦听器扩展InputListener
以重新声明该方法:
interface InputListener {
default void registerUsing(ListenerRegistrar registrar) { }
}
interface MouseListener extends InputListener {
void registerUsing(ListenerRegistrar registrar);
//...listening methods
}
但这对于我选择创建的每个听众来说都是非常乏味的,违反了 DRY。