Enum avec beaucoup de propriétés booléennes

11

Je travaille actuellement sur une webapp où nous devons souvent conditionner une logique de serveur basée sur la page qui va être retournée à l'utilisateur.

Chaque page reçoit un code de page à 4 lettres, et ces codes de page sont actuellement répertoriés dans une classe en tant que chaînes statiques:

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

Et souvent dans le code, nous voyons un code comme celui-ci ( 1ère forme ):

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
} 

Ce qui est un peu horrible à lire car il ne montre pas quelle propriété commune de ces pages a conduit l'auteur du code à les rassembler ici. Nous devons lire le code dans la ifbranche pour comprendre.

Solution actuelle

Ces ifs ont été en partie simplifiés en utilisant des listes de pages qui sont déclarées par différentes personnes dans différentes classes. Cela fait ressembler le code ( 2e forme ):

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
} 

... qui exprime bien mieux l'intention. Mais...

Problème actuel

Le problème ici est que si nous ajoutons une page, nous aurions en théorie besoin de parcourir toute la base de code pour savoir si nous devons ajouter notre page à une if()ou à une liste comme celle-ci.

Même si nous avons déplacé toutes ces listes dans la PageCodesclasse en tant que constantes statiques, les développeurs ont toujours besoin de discipline pour vérifier si leur nouvelle page tient dans l'une de ces listes et l'ajouter en conséquence.

Nouvelle solution

Ma solution a été de créer une énumération (car il existe une liste finie bien connue de codes de page) où chaque page contient des propriétés que nous devons définir:

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
}

Ensuite, le code conditionnel ressemble maintenant à ceci ( 3e forme ):

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

Ce qui est très lisible. De plus, si quelqu'un a besoin d'ajouter une page, il est obligé de penser à chaque booléen et si c'est vrai ou faux pour sa nouvelle page.

Nouveau problème

Un problème que je vois, c'est qu'il y aura peut-être 10 booléens comme celui-ci, ce qui rend le constructeur très gros et il pourrait être difficile d'obtenir la bonne déclaration lorsque vous ajoutez une page. Quelqu'un at-il une meilleure solution?

Joffrey
la source

Réponses:

13

Vous pouvez pousser l'idée un peu plus loin et définir une énumération pour les fonctionnalités de votre page au lieu d'utiliser des booléens.

Cela facilite l'ajout / la suppression d'une fonctionnalité à une page et rend les définitions de page immédiatement lisibles même lorsqu'il existe 30 à 40 fonctionnalités potentielles.

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
la source
J'y ai pensé, mais ici, le développeur n'est pas obligé de répondre à toutes les questions "est-ce une page de disponibilité?", "A-t-il un panier?" etc. Quand il y en a beaucoup, vous en oubliez facilement un.
Joffrey
5
@Joffrey Quand on y pense, avoir dix booléens ne les oblige pas à donner une réponse non plus, du moins pas une réponse à laquelle ils pensent. Le scénario le plus probable est qu'ils vont simplement copier la définition d'une autre page ou simplement laisser l'EDI remplir tous les paramètres false, puis en modifier un ou deux (probablement le mauvais, car il est si difficile de garder une trace). Il n'y a pas de protection parfaite contre les stupides, et il arrive un moment où vous devez faire confiance à vos développeurs pour faire la bonne chose.
biziclop
Bon, je n'y ai pas pensé de cette façon :) Et je ne pense pas que le petit extra "infaillible" donné par le booléen vaille la peine d'être dégradé en lisibilité, donc j'irai probablement avec la solution vararg. Merci pour votre perspicacité!
Joffrey
1
Bonne solution, voté positivement. Bien que la partie de moi qui a codé sur 6502s veut tout entasser en bits. :-)
user949300
@ user949300 la première solution de type vararg à laquelle j'ai pensé était en fait des masques de bits :) mais un vrai vararg avec un type enum est plus propre
Joffrey