Enum com muitas propriedades booleanas

11

Atualmente, estou trabalhando em um aplicativo da Web em que geralmente precisamos condicionar alguma lógica do servidor com base na página que será devolvida ao usuário.

Cada página recebe um código de página de 4 letras e, atualmente, esses códigos de página estão listados em uma classe como Strings estáticas:

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

E, muitas vezes, no código, vemos códigos como este ( 1º formulário ):

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
} 

O que é meio horrível de ler porque não mostra que propriedade comum dessas páginas levou o autor do código a reuni-las aqui. Temos que ler o código no iframo para entender.

Solução atual

Estes ifs foram parcialmente simplificados usando listas de páginas que são declaradas por pessoas diferentes em classes diferentes. Isso faz com que o código pareça ( 2º formulário ):

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
} 

... que expressa a intenção muito melhor. Mas...

Problema atual

O problema aqui é que, se adicionarmos uma página, em teoria, precisaremos percorrer toda a base de código para descobrir se precisamos adicionar nossa página a uma if()ou a uma lista como essa.

Mesmo se movermos todas essas listas para a PageCodesclasse como constantes estáticas, ainda será necessário disciplina dos desenvolvedores para verificar se a nova página se encaixa em alguma dessas listas e incluí-la de acordo.

Nova solução

Minha solução foi criar uma enumeração (porque existe uma lista bem conhecida finita de códigos de página) em que cada página contém algumas propriedades que precisamos definir:

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
}

Então o código condicional agora se parece com este ( terceiro formulário ):

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

O que é muito legível. Além disso, se alguém precisar adicionar uma página, ele será forçado a pensar em cada booleano e se isso é verdadeiro ou falso para a nova página.

Novo Problema

Um problema que vejo é que haverá talvez 10 booleanos como este, o que torna o construtor realmente grande e pode ser difícil obter a declaração correta ao adicionar uma página. Alguém tem uma solução melhor?

Joffrey
fonte

Respostas:

13

Você pode levar a idéia um passo adiante e definir uma enumeração para os recursos da sua página em vez de usar booleanos.

Isso facilita a adição / remoção de um recurso a uma página e torna as definições de página legíveis instantaneamente, mesmo quando há 30 a 40 recursos em potencial.

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
fonte
Pensei sobre isso, mas aqui, o desenvolvedor não é obrigado a responder a todas as perguntas "é uma página disponível?", "Tem um carrinho de compras?" etc. Quando existem muitos deles, você esquece facilmente um.
precisa saber é
5
@Joffrey Quando você pensa sobre isso, ter dez booleanos também não os força a dar uma resposta, pelo menos não uma resposta em que eles pensam. O cenário mais provável é que eles apenas copiem a definição de outra página ou deixem o IDE preencher todos os parâmetros falsee modifiquem um ou dois (provavelmente o errado, pois é tão difícil acompanhar isso). Não existe uma proteção perfeita contra o estúpido, e chega um momento em que você deve confiar nos seus desenvolvedores para fazer a coisa certa.
biziclop
Certo, eu não pensei dessa maneira :) E eu não acho que a pequena "prova de bobo" dada pelo booleano valha a degradação da lisibilidade, então provavelmente vou usar a solução vararg. Obrigado pela sua compreensão!
Joffrey
1
Boa solução, votada. Embora a parte de mim que codifiquei o 6502s queira compactar tudo em bits. :-)
user949300
@ user949300 o primeiro vararg-like solução que eu pensava era realmente mordeu máscaras :) mas um verdadeiro vararg com um tipo de enumeração é mais limpo
Joffrey