Enum o wielu właściwościach boolowskich

11

Obecnie pracuję nad aplikacją internetową, w której często musimy uwarunkować logikę serwera na podstawie strony, która zostanie zwrócona użytkownikowi.

Każda strona otrzymuje 4-literowy kod strony, a te kody stron są obecnie wymienione w klasie jako ciągi statyczne:

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..
}

I często w kodzie widzimy kod taki jak ten ( 1. formularz ):

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
} 

Co jest trochę okropne do przeczytania, ponieważ nie pokazuje, jaką wspólną własność tych stron skłonił autor kodu do złożenia ich tutaj. ifAby zrozumieć, musimy przeczytać kod w oddziale.

Aktualne rozwiązanie

Te ifzostały częściowo uproszczone dzięki listom stron zadeklarowanych przez różne osoby z różnych klas. To sprawia, że ​​kod wygląda ( 2. forma ):

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
} 

... co wyraża zamiar znacznie lepiej. Ale...

Aktualny problem

Problem polega na tym, że jeśli dodamy stronę, teoretycznie musielibyśmy przejrzeć całą bazę kodu, aby dowiedzieć się, czy musimy dodać naszą stronę do takiej if()listy lub do takiej listy.

Nawet jeśli przenieśliśmy wszystkie te listy do PageCodesklasy jako stałe statyczne, nadal wymaga dyscypliny od programistów, aby sprawdzić, czy ich nowa strona pasuje do którejkolwiek z tych list i odpowiednio ją dodać.

Nowe rozwiązanie

Moim rozwiązaniem było utworzenie wyliczenia (ponieważ istnieje skończona dobrze znana lista kodów stron), w których każda strona zawiera pewne właściwości, które musimy ustawić:

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
}

Następnie kod warunkowy wygląda teraz tak ( trzeci formularz ):

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

Co jest bardzo czytelne. Ponadto, jeśli ktoś musi dodać stronę, jest zmuszony pomyśleć o każdej wartości logicznej i czy jest to prawda, czy fałsz dla nowej strony.

Nowy problem

Jeden z problemów, jaki widzę, polega na tym, że może być 10 boolanów takich jak ten, co czyni konstruktora naprawdę dużym i może być trudno uzyskać prawidłową deklarację podczas dodawania strony. Czy ktoś ma lepsze rozwiązanie?

Joffrey
źródło

Odpowiedzi:

13

Możesz posunąć pomysł o krok dalej i zdefiniować wyliczenie dla funkcji strony zamiast używania logicznych wartości.

Ułatwia to dodawanie / usuwanie funkcji na stronie i sprawia, że ​​definicje stron są natychmiast czytelne, nawet jeśli istnieje 30-40 potencjalnych funkcji.

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
źródło
Myślałem o tym, ale tutaj deweloper nie jest zmuszony udzielić odpowiedzi na wszystkie pytania „czy to strona pomocy?”, „Czy ma wózek na zakupy?” itp. Gdy jest ich dużo, łatwo zapomnisz o jednym.
Joffrey,
5
@Joffrey Kiedy się nad tym zastanowić, posiadanie dziesięciu boolean nie zmusza ich do udzielenia odpowiedzi, a przynajmniej takiej, o której myślą. Najbardziej prawdopodobny scenariusz polega na tym, że po prostu skopiują definicję innej strony lub pozwolą IDE wypełnić wszystkie parametry false, a następnie zmodyfikują jeden lub dwa (prawdopodobnie niewłaściwy, ponieważ tak trudno jest śledzić). Nie ma doskonałej ochrony przed głupotami i przychodzi moment, w którym musisz zaufać swoim programistom, aby postępowali właściwie.
biziclop
Racja, nie myślałem o tym w ten sposób :) I nie sądzę, że mały dodatkowy „głupek” podany przez wartość logiczną jest wart pogorszenia wiarygodności, więc prawdopodobnie wybiorę rozwiązanie vararg. Dzięki za wgląd!
Joffrey,
1
Dobre rozwiązanie, pozytywnie ocenione. Chociaż część mnie, która zakodowała na 6502, chce wszystko zepsuć na strzępy. :-)
user949300 15.01.2016
@ user949300 pierwsze rozwiązanie podobne do vararg, o którym myślałem, to tak naprawdę maski bitowe :) ale prawdziwy vararg z typem enum jest czystszy
Joffrey