Enum с большим количеством логических свойств

11

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

Каждой странице присваивается четырехбуквенный код страницы, и эти коды страниц в настоящее время перечислены в классе как статические строки:

public class PageCodes {
    public static final String FOFP = "FOFP";
    public static final String FOMS = "FOMS";
    public static final String BKGD = "BKGD";
    public static final String ITCO = "ITCO";
    public static final String PURF = "PURF";
    // etc..
}

И часто в коде мы видим такой код ( 1-я форма ):

if (PageCode.PURF.equals(destinationPageCode) || PageCodes.ITCO.equals(destinationPageCode)) {
    // some code with no obvious intent
} 
if (PageCode.FOFP.equals(destinationPageCode) || PageCodes.FOMS.equals(destinationPageCode)) {
    // some other code with no obvious intent either
} 

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

Текущее решение

Они ifбыли частично упрощены с помощью списков страниц, которые объявлены разными людьми в разных классах. Это делает код похожим ( 2-я форма ):

private static final List<String> pagesWithShoppingCart = Collections.unmodifiableList(Arrays.asList(PageCodes.ITCO, PageCodes.PURF));
private static final List<String> flightAvailabilityPages = Collections.unmodifiableList(Arrays.asList(PageCodes.FOMS, PageCodes.FOFP));

// later in the same class
if (pagesWithShoppingCart.contains(destinationPageCode)) {
    // some code with no obvious intent
} 
if (flightAvailabilityPages.contains(destinationPageCode)) {
    // some other code with no obvious intent either
} 

... который выражает намерение намного лучше. Но...

Текущая проблема

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

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

Новое решение

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

public enum Page {
    FOFP(true, false),
    FOMS(true, false),
    BKGD(false, false),
    PURF(false, true),
    ITCO(false, true),
    // and so on

    private final boolean isAvailabilityPage;
    private final boolean hasShoppingCart;

    PageCode(boolean isAvailabilityPage, boolean hasShoppingCart) {
        // field initialization
    }

    // getters
}

Тогда условный код теперь выглядит так ( 3-я форма ):

if (destinationPage.hasShoppingCart()) {
    // add some shopping-cart-related data to the response
}
if (destinationPage.isAvailabilityPage()) {
    // add some info related to flight availability
}

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

Новая проблема

Одна проблема, которую я вижу, состоит в том, что будет примерно 10 таких логических выражений, что делает конструктор действительно большим, и может быть трудно получить правильное объявление при добавлении страницы. У кого-нибудь есть лучшее решение?

Джоффри
источник

Ответы:

13

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

Это облегчает добавление / удаление функции на страницу и делает определения страниц мгновенно читаемыми даже при наличии 30-40 потенциальных функций.

public enum PageFeature {
    AVAIL_PAGE,
    SHOPPING_CART;
}

public enum Page {
    FOFP(AVAIL_PAGE),
    FOMS(AVAIL_PAGE),
    BKGD(),
    PURF(SHOPPING_CART, AVAIL_PAGE),

    private final EnumSet<PageFeature> features;

    PageCode(PageFeature ... features) {
       this.features = EnumSet.copyOf(Arrays.asList(features));
    }

    public boolean hasFeature(PageFeature feature) {
       return features.contains(feature);
    }
 }
biziclop
источник
Я думал об этом, но здесь разработчик не обязан давать ответ на все вопросы: «Это полезная страница?», «У него есть корзина?» и т.д. Когда их много, их легко забыть.
Джоффри
5
@Joffrey Когда вы думаете об этом, наличие десяти логических значений также не заставляет их давать ответ, по крайней мере, не тот ответ, о котором они думают. Наиболее вероятный сценарий состоит в том, что они просто скопируют определение другой страницы или просто позволят IDE заполнить все параметры false, а затем изменят один или два (возможно, неправильный, так как отслеживать так сложно). Там нет идеальной защиты от глупости, и наступает момент, когда вы должны доверять своим разработчикам делать правильные вещи.
Бизиклоп
Правильно, я не думал об этом таким образом :) И я не думаю, что небольшая дополнительная «защита от дурака», данная булевыми значениями, стоит ухудшения восприимчивости, поэтому я, вероятно, пойду с решением vararg. Спасибо за ваше понимание!
Джоффри
1
Хорошее решение, проголосовал. Хотя та часть меня, которая была написана на 6502-х, хочет разбить все на части. :-)
user949300
@ user949300 первое vararg-подобное решение, о котором я думал, было на самом деле битовыми масками :), но настоящий vararg с типом enum чище
Joffrey