Может понадобиться небольшой фон, но пропустите Задача если вы чувствуете себя уверенно. Надеемся, что итоговое резюме будет иметь смысл.Как отделить модуль, управляемый событиями?
Резюме
У меня есть 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
в Game
, как я мог это сделать избегая при этом нарушения принципа «Разделение интерфейса»? (объявив все методы прослушивания: onKeyPressed,
onMouseMoved , etc.. inside of
Игра`, даже если они не могут быть использованы).
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.
'registerUsing' был способом избежать« игры », зная о ее контейнерах (что« InputDispatcher »является одним из них). 'InputListenerRegistrar' отделяет регистрацию от' InputDispatcher'. Это позволяет мне удалять замену 'InputDispatcher' всем, что я хотел бы, поощряя повторное использование. –
Что касается 'DefaultEventListener', я не хочу выполнять больше проверки типов, чем я уже делаю (что я только делаю, потому что JSFML в основном заставляет его). Использование этого метода по-прежнему нарушает ISP, поскольку методы, которые не используются, будут объявлены. Несмотря на то, что 'DemoGame' не должен объявлять об этом, он все еще находится в интерфейсе' DemoGame'. «Ничего не делают» сами методы являются запахами кода. Я мог бы сохранить 'registerUsing' по умолчанию и не обновлять его в' KeyListener' и 'MouseListener', если бы я хотел такого поведения. –
Вот как это делают несколько существующих guis. (Win32, MFC, WinForms, Swing, SWT, gui, которые я построил во всей полноте и т. Д.) Итак, как это или нет, вонючий или нет, так много людей, которые имеют большой опыт в этих дела решили это сделать. И вы знаете, как все думают, что они знают лучше, и они будут делать это по-другому? Ну, в этом случае они обычно не делают этого по-другому. Должна быть причина для этого. Делай то, что будешь с этой информацией. –