Реализация немногофилда с использованием java enums

StackOverflow https://stackoverflow.com/questions/5346477

  •  27-10-2019
  •  | 
  •  

Вопрос

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

static int DOCUMENT_STATUS_NO_STATE = 0
static int DOCUMENT_STATUS_OK = 1
static int DOCUMENT_STATUS_NO_TIF_FILE = 2
static int DOCUMENT_STATUS_NO_PDF_FILE = 4

Это позволяет довольно легко указать состояние, в котором находится документ, установив соответствующие флаги. Например:

status = DOCUMENT_STATUS_NO_TIF_FILE | DOCUMENT_STATUS_NO_PDF_FILE;

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

class DocumentStatus{

    public enum StatusFlag {

        DOCUMENT_STATUS_NOT_DEFINED(1<<0),
        DOCUMENT_STATUS_OK(1<<1), 
        DOCUMENT_STATUS_MISSING_TID_DIR(1<<2),
        DOCUMENT_STATUS_MISSING_TIF_FILE(1<<3),
        DOCUMENT_STATUS_MISSING_PDF_FILE(1<<4),
        DOCUMENT_STATUS_MISSING_OCR_FILE(1<<5),
        DOCUMENT_STATUS_PAGE_COUNT_TIF(1<<6),
        DOCUMENT_STATUS_PAGE_COUNT_PDF(1<<7),
        DOCUMENT_STATUS_UNAVAILABLE(1<<8);


        private final long statusFlagValue;

        StatusFlag(long statusFlagValue) {
            this.statusFlagValue = statusFlagValue;
        }

        public long getStatusFlagValue(){
            return statusFlagValue;
        } 

       }


    /**
     * Translates a numeric status code into a Set of StatusFlag enums
     * @param numeric statusValue 
     * @return EnumSet representing a documents status
     */
    public EnumSet<StatusFlag> getStatusFlags(long statusValue) {
        EnumSet statusFlags = EnumSet.noneOf(StatusFlag.class);
        StatusFlag.each { statusFlag -> 
            long flagValue = statusFlag.statusFlagValue
            if ( (flagValue&statusValue ) == flagValue ) {
               statusFlags.add(statusFlag);
            }
        }
        return statusFlags;
    }


    /**
     * Translates a set of StatusFlag enums into a numeric status code
     * @param Set if statusFlags
     * @return numeric representation of the document status 
     */
    public long getStatusValue(Set<StatusFlag> flags) {
        long value=0;
        flags.each { statusFlag -> 
            value|=statusFlag.getStatusFlagValue() 
        }
        return value;
    }

     public static void main(String[] args) {

        DocumentStatus ds = new DocumentStatus();
        Set statusFlags = EnumSet.of(
            StatusFlag.DOCUMENT_STATUS_OK,
            StatusFlag.DOCUMENT_STATUS_UNAVAILABLE);

        assert ds.getStatusValue( statusFlags )==258 // 0000.0001|0000.0010

        long numericStatusCode = 56;
        statusFlags = ds.getStatusFlags(numericStatusCode);

        assert !statusFlags.contains(StatusFlag.DOCUMENT_STATUS_OK);
        assert statusFlags.contains(StatusFlag.DOCUMENT_STATUS_MISSING_TIF_FILE);
        assert statusFlags.contains(StatusFlag.DOCUMENT_STATUS_MISSING_PDF_FILE);
        assert statusFlags.contains(StatusFlag.DOCUMENT_STATUS_MISSING_OCR_FILE);

    }

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

Решение

Ваш подход - это именно то, чтобы сделать это.

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

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

public enum StatusFlag {

    DOCUMENT_STATUS_NOT_DEFINED,
    DOCUMENT_STATUS_OK, 
    DOCUMENT_STATUS_MISSING_TID_DIR,
    DOCUMENT_STATUS_MISSING_TIF_FILE,
    DOCUMENT_STATUS_MISSING_PDF_FILE,
    DOCUMENT_STATUS_MISSING_OCR_FILE,
    DOCUMENT_STATUS_PAGE_COUNT_TIF,
    DOCUMENT_STATUS_PAGE_COUNT_PDF,
    DOCUMENT_STATUS_UNAVAILABLE;


    public long getStatusFlagValue(){
        return 1 << this.ordinal();
    } 

}

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

Немного лучшим способом - хранить результат 1 << this.ordinal() в поле, когда значения перечисления построены. Таким образом, вам не нужно предоставлять каждое значение вручную, и флаг вычисляется только один раз.

public enum StatusFlag {

  DOCUMENT_STATUS_NOT_DEFINED,
  DOCUMENT_STATUS_OK, 
  DOCUMENT_STATUS_MISSING_TID_DIR,
  DOCUMENT_STATUS_MISSING_TIF_FILE,
  DOCUMENT_STATUS_MISSING_PDF_FILE,
  DOCUMENT_STATUS_MISSING_OCR_FILE,
  DOCUMENT_STATUS_PAGE_COUNT_TIF,
  DOCUMENT_STATUS_PAGE_COUNT_PDF,
  DOCUMENT_STATUS_UNAVAILABLE;

  public final int flag;

  StatusFlag() { 
    this.flag = 1 << this.ordinal();
  } 
}

РЕДАКТИРОВАТЬ: Также может быть полезно иметь метод ISFLAGSET.

Не дайте свои значения перечисления. Использовать EnumSet объединить их и использовать Enum.ordinal() При постоянном состоянии преобразовать в/из одного целого числа. Вы также можете найти Class.getEnumConstants() Полезно при реконструкции набора из целого числа.

Я сделал полную библиотеку для этой проблемы:http://claude-martin.ch/enumbitset/

Основная цель состояла в том, чтобы хранить наборы типов перечисления в Bitfields. Но это также поддерживает другие типы.

При этом вам не понадобятся какие -либо дополнительные методы, такие как ваш "getStatusFlags ()". Его можно использовать на любом существующем типе перечисления, просто добавив интерфейс Enumbitsetherper (Он используется как «черта»). Каждая константа перечисления может затем создать «Enumbitset», который имеет все методы перечисления и битсет Java. Затем вы можете работать с этими наборами констант перечисления и преобразовать их в значения Битфилда.

Он поддерживает многие форматы, такие как Biginteger и Long, чтобы легко хранить ценность в немного поле. Но обратите внимание, что это работает только с Java версией 8 и новее.

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