Несколько перечислений против одного enum

Я рассматривал пример реализации Publisher (AsyncIterablePublisher.java) спецификации реактивных потоков, когда я наткнулся на что-то, что я не понимаю, почему это было сделано таким образом.

static interface Signal {};
enum Cancel implements Signal { Instance; };
enum Subscribe implements Signal { Instance; };
enum Send implements Signal { Instance; };
enum Signal {
  Cancel,
  Subscribe,
  Send;
}

может кто-нибудь объяснить мне, почему было бы лучше? Преимущества / недостатки?

5 ответов


не будьте слишком строги, вот моя интерпретация этого кода. Назовем владельца реактивных потоков Роландом.

сначала Роланду нужен общий интерфейс для всех inboundSignals

static interface Signal {};

ConcurrentLinkedQueue<Signal> inboundSignals = new ConcurrentLinkedQueue<Signal>();

сигналы, такие как Cancel, Subscribe и Send всегда одна и та же цель неизменна и происходит очень часто, поэтому неплохо реализовать их как Синглтон Джошуа Блоха:

enum Cancel    implements Signal { Instance; };
enum Subscribe implements Signal { Instance; };
enum Send      implements Signal { Instance; };

другой способ сделать то же самое похоже на ваше предложение и мое любимое:

enum CommonSignals implements Signal{

    Cancel {
        @Override
        void debug() {
            System.out.println("Cancel");
        }
    },

    Subscribe {
        @Override
        void debug() {
            System.out.println("Subscribe");
        }
    },

    Send {
        @Override
        void debug() {
            System.out.println("Send");
        }
    };

    abstract void debug();

    [...] some other methods I could need in the future
}

как вы можете видеть, это другая реализация. Но идея та же-сигнал, что и синглтон

мы идем дальше и находим этот код:

static final class Request implements Signal {
    final long n;
    Request(final long n) { // every Request has different value of n
        this.n = n;
    }
};

С inboundSignals может содержать несколько Request объекты невозможно реализовать этот тип сигнала, как синглтон. Поэтому он не может быть членом CommonSignals или реализуется в виде enum.


вывод

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


для типа использования в AsyncIterablePublisher две формы эквивалентны, и, возможно, последнее, одно перечисление с несколькими константами, более естественно.

На самом деле, первая форма очень редка. Я вижу один аргумент в пользу его использования (но он настолько редок, что это означает, что этот момент обычно не так важен): когда вы определяете каждую константу в своем собственном перечислении, вы получаете возможность определить различные методы / поля, такие как:

enum Cancel implements Signal { Instance; };
enum Send implements Signal { 
  Instance; 
  public void someSendSpecificMethod() { ... }
}

Так что теперь вы можете сделать Send.Instance.someSendSpecificMethod(). Очень неловко, очень редко.


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

вы можете проверить этот сайт на хороший пример перечисления с интерфейсы:

http://www.selikoff.net/2011/06/15/java-enums-can-implement-interfaces/


Привет Майкл,

в чем разница с наличием только одного перечисления? У вас не было бы трех синглетов, но все же три однозначно определенных объекта.:

конечно, это можно сделать, но тогда мне придется придумай ему имя. (как ConcreteSignal), моя выбранная кодировка избегает этого:)

enum Signal {
  Cancel, Subscribe, Send;
}

Я думаю, что это также потокобезопасно, и если вы действительно хотите иметь общий интерфейс, которым вы можете поделиться с объектами, например, вы можете сделать это так:

enum ConcreteSignal implements Signal {
  Cancel,
  Subscribe,
  Send;
}

вы могли бы определенно сделать, как вы сделали выше, однако я бы поспорил, что они [отмена, подписка, отправка] не более конкретны, чем запрос (что также является сигналом): https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/examples/src/main/java/org/reactivestreams/example/unicast/AsyncIterablePublisher.java#L49

кроме того, это также может побудить использовать методы перечисления для перечислить все возможные сигналы, которые он не мог, так как запрос не сигнал. Смысл?

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

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

поэтому, если "запрос" не будет нуждаться в каких-либо параметрах, я бы сделал, как вы предложено: enum Signal { отмена, запрос, подписка, отправка }

Тайна раскрыта :-).


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