Вопрос

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

Это было полезно?

Решение

Что насчет этого?

HashMap<Integer, Runnable> map = new HashMap<Integer, Runnable>();
map.put(Register.ID, new Runnable() { 
    public void run() { functionA(); }
});
map.put(NotifyMessage.ID, new Runnable() { 
    public void run() { functionB(); }
});
// ...
map.get(id).run();

(Если вам нужно передать некоторые аргументы, определите свой собственный интерфейс с функцией, имеющей подходящий параметр, и используйте его вместо запуска).

Другие советы

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

Java не имеет первоклассных указателей функций. Чтобы достичь аналогичной функциональности, вы должны определить и реализовать интерфейс. Вы можете облегчить использование анонимных внутренних классов, но это все еще не очень красиво. Вот пример:

public interface PacketProcessor
{
    public void processPacket(Packet packet);
}

...

PacketProcessor doThing1 = new PacketProcessor()
{
    public void processPacket(Packet packet)
    {
        // do thing 1
    }
};
// etc.

// Now doThing1, doThing2 can be used like function pointers for a function taking a
// Packet and returning void

Вы когда -нибудь использовали Swing/AWT? Их иерархия события решает аналогичную проблему. То, как Java выполняет функции, есть с интерфейсом, например,

public interface ActionHandler {
    public void actionPerformed(ActionArgs e);
}

Затем, если вы хотите сопоставить целые числа на эти объекты, вы можете использовать что -то вроде java.util.HashMap<Integer,ActionHandler> Чтобы справиться с этим. Фактические реализации могут быть либо в анонимных классах (лучшее приближение Java «Lambda») или в правильных классах. Вот анонимный класс:

HashMap<Integer,ActionHandler> handlers;
handlers.put(ACTION_FROB, new ActionHandler() {
    public void actionPerformed(ActionArgs e) {
        // Do stuff
        // Note that any outer variables you intend to close over must be final.
    }
});
handlers.get(ACTION_FROB).actionPerformed(foo);

(Редактировать) Если вы хотите быть еще более оскорбительным, вы можете инициализировать хэшмап, как так:

HashMap<Integer,String> m = new HashMap<Integer,String>() {{
    put(0,"hello");
    put(1,"world");
}};

Да, но использование интерфейса означает, что вы должны создать интерфейс для каждого обратного вызова, что означает каждую функцию, которую вы хотите передать. Создание класса делегатов для обработки этого дает вам (не истинно указатель функции), но функция, которая должна быть передана, и если вы злоупотребляете общим, чтобы быть типом возврата, вам не нужно отбрасывать, что сокращает узкое место почти до ничего.

Делегат C# (MulticastDelegate, чтобы быть правильным) получает информацию от использования Method MethodInfo, который является тем же, что вам нужно сделать для класса делегатов, используя java.lang.reflect.method. Я опубликовал свой код для класса Delegate (T) в другой форме этого сайта, посвященной этой разлучной проблеме. Я делаю это, потому что (да) из C ++, мне нужен лучший способ прохождения функций (особенно void), а затем создавать интерфейс для функции или более. Теперь я могу выбрать функцию, заполняющую информацию о параметре для нее. Вуаля! Хороший и пригодный для использования без заметной теряния в скорости от JIT или JVM. И если у меня было только изучение программирования Java всего за неделю, любой программист Java может сделать это.

Кроме того, он очень хорошо служит при создании базового слушателя и базового интерфейса для прохождения слушателя. Больше не нужно писать другого слушателя, потому что имя функции изменилось. Создание класса делегатов имеет большие преимущества, так как очень полезные и проходимые.

Вы можете интерфейс статические методы. Этот метод позволяет вам также указать параметры. Объявите свой интерфейс ...

public interface RouteHandler {
    void handleRequest(HttpExchange t) throws IOException;
}

И твоя карта ...

private Map<String, RouteHandler> routes = new HashMap<>();

Затем реализуйте статические методы, которые соответствуют интерфейсу/параметрам ...

public static void notFound(HttpExchange t) throws IOException {
    String response = "Not Found";

    t.sendResponseHeaders(404, response.length());
    OutputStream os = t.getResponseBody();
    os.write(response.getBytes());
    os.close();
}

Затем вы можете добавить эти методы в свою карту ...

routes.put("/foo", CoreRoutes::notFound);

и назовите их следующими ...

RouteHandler handler = routes.get("/foo");
handler.handleRequest(exchange);

Другой подобный поставщик может использовать поставщиков Java 8:

Map<Integer, Supplier<T> suppliers = new HashMap();
suppliers.put(1, () -> methodOne());
suppliers.put(2, () -> methodTwo());

// ...

public T methodOne() { ... }
public T methodTwo() { ... }

// ...

T obj = suppliers.get(id).run();

Вы можете сделать это благодаря использованию цепочки схемы ответственности.

Это шаблон, который связывает разные объекты, с которыми сталкиваются как связанный список. т.е. каждый объект имеет ссылку на следующую в цепочке. Объекты в цепи обычно обрабатывают одно конкретное поведение. Поток между объектами очень похож на операцию с переключателем.

Есть некоторые GotChas, такие как, это распространяет вашу логику, чрезмерно длинная цепь может вызвать проблемы с производительностью. Но наряду с этими GotChas у вас есть преимущество повышенной тестируемости и более сильной сплоченности. Также вы не ограничиваются использованием Enum, Byte, Int Short и Char -эксплуатации в качестве триггера для ветвления.

Проверьте закрытие, как они были реализованы в библиотеке Lambdaj. У них на самом деле поведение, очень похожее на делегаты C#:

http://code.google.com/p/lambdaj/wiki/closures

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top