Шаблон проектирования Java: система событий, несколько действий для данного актера - PullRequest
0 голосов
/ 10 января 2011

У меня есть система, которая управляет событиями, которые происходят с несколькими участниками, используя простую систему на основе очередей. Event - это простой класс, имеющий время и абстрактный метод fire(). Actor - это простой интерфейс, который реализует два метода - think() и act(). Каждый актер имеет 2 основных события, которые реализованы так:

public class ActorThinkEvent extends Event {
    private Actor actor;

    public ActorThinkEvent(Actor actor, long time) {
        super(time);
        this.actor = actor;
    }

    public void fire() {
        Main.game.queue.add(actor.think());
    }
}
public abstract class ActorActEvent extends Event {
    protected Actor actor;

    protected ActorActEvent(Actor actor, long time) {
        super(time);
        this.actor = actor;
    }

    public void fire() {
        long spent = act();
        Main.game.queue.add(new ActorThinkEvent(actor, time + spent));
    }

    public abstract long act();
}

Таким образом, «думающий» вызов исправлен, предполагается, что он генерирует ActorActEvent, который будет добавлен в глобальную очередь, в то время как «действующий» вызов выполняется с помощью пользовательского класса на основе ActorActEvent, который реализует действие, просто возвращая потраченное время, и новое событие «Think» будет добавлено в очередь автоматически.

Проблема, которую я вижу здесь, заключается в том, что реализация классов на основе ActorActEvent сводится к простому делегированию ActorActEvent.act() некоторому методу класса Actor с предварительно сохраненными аргументами, т.е. все мои классы ActEvent выглядят очень похоже:

public class ActorMoveEvent extends ActorActEvent {
    private Coords delta;

    public ActorMoveEvent(Actor actor, long time, Coords delta) {
        super(actor, time);
        this.delta = delta;
    }

    public long act() {
        return actor.moveBy(delta);
    }
}
public class ActorReloadEvent extends ActorActEvent {
    public ActorReloadEvent(Actor actor, long time) {
        super(actor, time);
    }

    public long act() {
        return actor.reload();
    }
}
public class ActorPickupEvent extends ActorActEvent {
    private ItemStash wantedItems;

    public ActorPickupEvent(Actor actor, long time, ItemStash wantedItems) {
        super(actor, time);
        this.wantedItems = wantedItems;
    }

    public long act() {
        return actor.pickupItems(wantedItems);
    }
}

Я мог бы закончить с несколькими десятками таких классов. Если я правильно понимаю, это классическая реализация Шаблон команды . Однако я не чувствую себя хорошо во всех этих классах делегирования и особенно в том, что пишу их все вручную.

Я думал об использовании отражения Java и таких вещей, как Method.invoke () , при передаче экземпляра Method и предварительно сохраненных аргументов ему в универсальном классе ActorActEvent, однако это было бы довольно медленный. Я думал написать генератор для всех этих классов, но для меня это выглядит довольно неуклюжим решением.

Если бы это был какой-то современный скриптовый / функциональный язык, я бы в конечном итоге использовал структуру, похожую на блок / закрытие, сначала подготовив ее и вызвав ее, когда придет время. Увы, я не знаю, как сделать это эффективно в Java.

Есть ли какие-нибудь яркие идеи о том, что я могу сделать, чтобы улучшить ситуацию?

1 Ответ

1 голос
/ 10 января 2011

Мой совет - идти по пути отражения. Беспокоиться, что «это будет довольно медленно» в этом случае преждевременно. В последнее десятилетие производительность рефлексии была довольно приличной, и вы сможете кэшировать свои Method объекты, чтобы сэкономить время, затрачиваемое на повторную обработку имен (что, вероятно, является самым большим ударом по производительности). Этот подход является гибким и быстрым в реализации.

Если вам действительно нравятся ваши замыкания, вы можете поэкспериментировать с анонимными внутренними классами для ваших событий. Есть несколько ограничений - вам все равно придется объявлять ваши методы, чтобы они были более многословными, и вам нужно объявить ваши переменные как окончательные, чтобы получить доступ к ним из внутреннего класса. Этот подход даст вам всю производительность и безопасность статических типов, но вам все равно придется иметь класс для каждого события.

Исторически события проявляли поведение «худшего из обоих миров» и проходили около Object с, полагаясь на цель, чтобы знать, как разыгрывать и бороться с ними.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...