Как кодировать набор классов перечисления, которые все должны поддерживать публичный статический метод - PullRequest
9 голосов
/ 07 февраля 2011

Я пытаюсь написать некоторый код Java для набора классов перечисления.

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

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

Поскольку класс cache / lookup также будет зависеть от открытых и статических методовопределенные в каждом перечислении, как я могу кодировать свое решение так, чтобы я мог гарантировать, что любое перечисление, которое может быть передано в класс, будет иметь требуемые методы?

Обычный подход состоит в том, чтобы определить интерфейс, но интерфейсыне разрешать статические методы

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

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

Пример перечисления:

public enum MyEnum{
    VALUE_ONE("my data");
    VALUE_TWO("some other data");

    /**
     * Used when mapping enums to database values - if that sounds odd, 
     * it is: it's legacy stuff
     *
     * set via private constructor
     */
    private String myValue; 
    //private constructor not shown

    public static MyEnum lookupEnumByString(String enumValue){
        //find the enum that corresponds to the supplied string
    }

    public String getValue(){
        return myValue;
    }
}

Ответы [ 3 ]

5 голосов
/ 07 февраля 2011

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

// I'm not sure about the right type arguments here
public interface MyEnumInterface<E extends MyEnumInterface & Enum<E>> {
    public static boolean aUsefulNonStaticMethod();
    String getValue();
    MyEnumInfo<E> enumInfo();
}

/** contains some helper methods */
public class MyEnumInfo<E extends MyEnumInterface<E>> {
    private static <E extends MyEnumInterface<E>> MyEnumInfo(Class<E> enumClass) {...}
    // static factory method
    public static <E extends MyEnumInterface<E>> MyEnumInfo<E> infoForClass(Class<E> enumClass) {
        ... return a cached value
    }
    public static <E extends MyEnumInterface<E>> MyEnumInfo(E e) {
         return infoForClass(e.getClass());
    }
    // some helper methods replacing static methods of the enum class     
    E enumForValue(String value) {....}
}

public enum MyEnum implements MyEnumInterface<MyEnum> {
    VALUE_ONE("my data");
    VALUE_TWO("some other data");

    private String myValue; //set via private constructor
    //private constructor not shown

    public boolean aUsefulNonStaticMethod(){
        //do something useful
    }

    public String getValue(){
        return myValue;
    }

    // the ONLY static method in each class
    public static MyEnumInfo<E> staticEnumInfo() {
        return MyEnumInfo.infoForClass(MyEnumClass.class);
    }
    // the non-static version of the above (may be useful or not)
    public MyEnumInfo<E> enumInfo() {
        return MyEnumInfo.infoForClass(getClass());
    }
}

Немного странно, что вы дополнительно используете другую строкуEnum.name (), вам это нужно?

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

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

3 голосов
/ 07 февраля 2011

Это самое близкое, что я могу вспомнить.

У вас есть класс, содержащий ваши общие функции:

class Util{

    //common functionality
    public void doSomething(){

    }
}

Каждый Enum имеет экземпляр этого класса и может при необходимости переопределять его методы:

enum Enum1{
    FOO, 
    BAR;

    private Util util = new Util();

    public Util getUtil() {
        return util;
    }

}

enum Enum2{
    ALICE, 
    BOB;

    private Util util = new Util(){
        @Override
        public void doSomething() {
            //this one has overridden it
        };
    };

    public Util getUtil() {
        return util;
    }
}

Пример использования:

Enum2.ALICE.getUtil().doSomething();
2 голосов
/ 07 февраля 2011

** ПРЕДУПРЕЖДЕНИЕ ** ниже приведен Java-псевдокод, поэтому он не будет компилироваться.

Итак, вы хотите привязать логику к отдельным перечислениям. Это может потребовать, чтобы некоторые перечисления разделяли ту же логику, в то время как другие перечисления имели свою собственную, специфическую логику. Кроме того, вы хотите связать строковые ключи, которые могут не совпадать с именем Enum (что обычно возвращается Enum.name().

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

То есть я бы пошел с композицией объектов через интерфейсы (немного разновидности шаблонов Strategy + Template.)

// at package-level visibility

interface EnumHandler
{
  SomeRetVal doSomething(DBEnum dbEnum);
}

final class DefaultHandler implements EnumHandler
{
  static DefaultHandler _handler = new DefaultHandler();

  SomeRetVal doSomething(DBEnum dbEnum)
  {
      return ping;
  }
}

// at public visibility

public interface Actionable
{
   // meh, you might need such an interface, or maybe not, just added here
   // for illustration purposes. you'll have to make a decision if you need it
   // or not.
   SomeRetVal doSomething();
}

// have the enum implement the interface if you determine you need
// such an interface
public Enum DBEnum implements Actionable
{
   // ONE and THREE share the same logic. TWO has its own.

   ONE("db-key-one" ),
   TWO("db-key-two, new EnumHandler(){
             SomeRetVal doSomething(DBEnum dbEnum){ return pong; } } ),
   THREE("db-key-three");

   // this guy keeps track of enums by key
   static private java.util.Map<String,DBEnum> _MAP = 
     java.util.Collections.unmodifiableMap(
       new java.util.HashMap<String,DBEnum>() );

   final private String _key;
   final private EnumHandler _handler;

   // allows construction of customized handler
   DBEnum(final String key, final EnumHandler handler)
   {
      this._key = key;
      this._handler = handler;

      this._MAP.put(key, this)
   }

   // construct using default handler
   DBEnum(final String key)
   {
      this(key, DefaultHandler._handler);
   }  

   // have toString() return the key instead of this.name()
   public String toString()
   {
       return this._key;
   }

   // implementing Actionable interface (if you choose to use such an interface)
   public SomeRetVal doSomething()
   {
      return this._handler.doSomething(this);
   }

   // get enum by key
   public static DBEnum getByDbKey(final String key)
   {
     DBEnum retVal = this._MAP.get(key);
     if( retVal == null ){ throw new IllegalArgumentException("thingie not found");

     return retVal;
   }

   public static Iterator<String> dbKeys()
   {
     return _map.keySet().iterator();
   }
}

// somewhere else
public static void main(String[] args)
{
    DBEnum.ONE.doSomething();

    DBEnum.geByDBKey( DBEnum.TWO.toString() ).doSomething();

    for( String dbKey : DBEnum.dbKeys() )
    {
        DBEnum.getByDbKey( dbKey ).doSomething();
    }

    // the following will kaput with an IllegalArgumentException
    DBEnum.getDbByKey( "key-that-is-not-there" ).doSomething();
}

Теоретически, можно получить реальные ключи db из файла ресурсов, когда Enum загружается загрузчиком классов. Содержимое (и изменения в содержимом) файла ресурсов может быть элементом развертывания. В этом могут быть существенные преимущества - изменение ключа db не потребует перекомпиляции. НО такой подход усложнит ситуацию. Это было бы что-то, что я реализовал бы после все остальное сделано правильно.

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