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:
Important!
Dans la page Produits, le navigateur peut choisir le filtre qu'il souhaite.
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?
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.
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.
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.
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.