web-dev-qa-db-fra.com

Si sinon - Logique de code répété

Mon patron m'a donné un projet avec une logique particulière. Je dois développer une page Web qui doit amener le navigateur à travers de nombreux cas jusqu'à ce qu'il arrive sur le produit.

C'est le schéma de chemin de la navigation sur le site:

Path Scheme

Important!

Dans la page Produits, le navigateur peut choisir le filtre qu'il souhaite.

  • Si un, il/elle [~ # ~] doit [~ # ~] traverser le b (puis c bien sûr) ou c et atteindre les produits.
  • Si b, il/elle [~ # ~] doit [~ # ~] passer par le C et atteindre les produits.
  • Si c, il/elle atteint directement les produits.

Bien sûr, si je commence à partir d'un voyage le plus long et lorsque j'atteigne mes produits, j'ai 3 filtres actifs.

Jusqu'à présent, j'ai développé le code suivant qui fonctionne bien.

if filter_A
  if filter_B
     filter_C()
     .. else ..
  else
     filter_C
    .. else ..
else
   if filter_B
      filter_C()
     .. else ..
   else
     filter_C()
     .. else ..

Je suis ici pour demander ce qu'un programmeur plus expertise a fait dans cette situation. Je ne respecte pas les DRY principe, je ne l'aime pas et je voudrais savoir une autre façon de développer ce genre de logique.

J'ai pensé à fractiver chaque section du code dans les fonctions, mais c'est une bonne idée dans ce cas?

15
Kevin Cittadini

Vous n'avez pas dit si les filtres prennent des paramètres. Par exemple, filter_A Peut-être un filtre de catégorie, de sorte que ce ne soit pas seulement une question de "Dois-je appliquer filter_A ", cela pourrait être" J'ai besoin d'appliquer filter_A et retourner tous les enregistrements dans le champ de catégorie = fooCategory ".

Le moyen le plus simple de mettre en œuvre exactement ce que vous avez décrit (mais assurez-vous de lire la seconde moitié de la réponse ci-dessous) est similaire aux autres réponses, mais je n'aurais pas eu de chèques booléens du tout. Je définirais des interfaces: FilterA, FilterB, FilterC. Ensuite, vous pouvez avoir quelque chose comme (je suis un Java programmeur, donc ce sera une syntaxe Java-Esque):

class RequestFilters {
    FilterA filterA;
    FilterB filterB;
    FilterC filterC;
}

Ensuite, vous pourriez avoir quelque chose comme celui-ci (à l'aide du modèle Enum singleton modèle de Java efficace ):

enum NoOpFilterA implements FilterA {
    INSTANCE;

    public List<Item> applyFilter(List<Item> input) {
       return input;
    }
}

Mais si vous voulez réellement que certains articles soient filtrés, vous pouvez plutôt fournir une instance d'une mise en œuvre FilterA qui fait en réalité quelque chose. Votre méthode de filtration sera la très simple

List<Item> filterItems(List<Item> data, RequestFilters filters) {
    List<Item> returnedList = data;
    returnedList = filters.filterA.filter(data);
    returnedList = filters.filterB.filter(data);
    returnedList = filters.filterC.filter(data);
    return returnedList;
}

mais je viens de commencer.

Je soupçonne que l'appel applyFilter sera effectivement similaire pour les trois types de filtres. Si c'est le cas, je ne le ferais même pas la voie décrite ci-dessus. Vous pouvez obtenir un code encore plus propre par une seule interface, puis ce faire:

class ChainedFilter implements Filter {
     List<Filter> filterList;

     void addFilter(Filter filter) {
          filterList.add(filter);
     }

     List<Item> applyFilter(List<Item> input) {
         List<Item> returnedList = input;
         for(Filter f : filterList) {
             returnedList = f.applyFilter(returnedList);
         }
         return returnedList;
     }
}

Ensuite, lorsque votre utilisateur navigue dans les pages, vous devez simplement ajouter une nouvelle instance de tout filtre dont vous avez besoin le cas échéant. Cela vous permettra de pouvoir appliquer plusieurs instances du même filtre avec différents arguments si vous avez besoin de ce comportement à l'avenir et d'ajouter des filtres supplémentaires à l'avenir sans avoir à changer votre conception .

De plus, vous pouvez ajouter quelque chose comme le NoOpFilter ci-dessus ou vous ne pouvez tout simplement pas ajouter un filtre particulier à la liste, tout ce qui est plus facile pour votre code.

20
durron597

Dans ce cas, il est important de séparer la logique de filtrage et le flux de contrôle de la manière dont les filtres sont exécutés. La logique du filtre doit être séparée en fonctions individuelles, pouvant fonctionner indépendamment de l'autre.

ApplyFilterA();
ApplyFilterB();
ApplyFilterC();

Dans le code d'échantillon affiché, il y a 3 booléens filter_A, filter_B, et filter_C. Cependant, du diagramme, filter_C est toujours exécuté, afin que cela puisse être changé à un inconditionnel.

Remarque : Je suppose que le diagramme de flux de contrôle est correct. Il y a une divergence entre le code d'échantillon posté et le diagramme de flux de contrôle.

Une pièce séparée de contrôles de code que les filtres sont exécutés

ApplyFilters(bool filter_A, bool filter_B)
{
    listOfProducts tmp;
    if (filter_A)
        ApplyFilterA();
    if (filter_B)
        ApplyFilterB();
    ApplyFilterC();
}

Il existe une séparation distincte entre le contrôle des filtres exécutant et ce que font les filtres. Casser ces deux morceaux de logique à part.

3
CurtisHx

Je suppose que vous voulez l'algorithme le plus simple et le plus clair.
[ Comme il se ressemble dans votre organigramme, chaque filtre avant le C, est facultatif, car chacun d'entre eux peut être soit appliqué, ou non. Dans ce cas, je vivrais IFS séparé de chaque filtre, sans nidification ni enchaînement:

if filter_a
  do_filter_a()

if filter_b
  do_filter_b()

do_filter_c()

si vous avez un organigramme avec un nombre variable de filtres, avant le responsable, je voudrais plutôt enregistrer tous les filtres à un tableau, dans une ordonnance à laquelle ils doivent apparaître. Processez ensuite les filtres optionnels de la boucle et appliquez-la obligatoire à la fin, en dehors de la boucle:

optional_filters_array = (a, b, c, d, e, f, g, h, etc)

for current_filter in optional_filters_array
  do_filter(current_filter)

do_required_filter()

ou:

optional_filters_array = (a, b, c, d, e, f, g, h, etc)
required_filter = last_filter


for current_filter in optional_filters_array
  do_filter(current_filter)

do_filter(required_filter)

de Cource, vous devriez définir le sous-programme de traitement du filtre.

2
igoryonya

Je me demande si la modélisation de vos filtres est une sorte d'objet dans un graphique aurait un sens. Au moins c'est ce que je pense à voir le diagramme.

Si vous modélisez la dépendance des filtres comme un graphique d'objet, le code qui gère les chemins d'écoulement possibles est à peu près tout droit sans aucune logique poilue. De plus, le graphique (logique commerciale) peut changer, tandis que le code qui interprète le graphique reste le même.

0
enum