J'ai regardé quelques réponses et cherché sur Google, mais je n'ai rien trouvé d'utile (c'est-à-dire que cela n'aurait pas d'effets secondaires gênants).
Mon problème, en résumé, est que j'ai un objet et que je dois y effectuer une longue séquence d'opérations; Je le vois comme une sorte de chaîne de montage, comme construire une voiture.
Je crois que ces objets s'appelleraient des objets de méthode .
Donc, dans cet exemple, à un moment donné, j'aurais un CarWithoutUpholstery sur lequel j'aurais alors besoin d'exécuter installBackSeat, installFrontSeat, installWoodenInserts (les opérations n'interfèrent pas entre elles et pourraient même être effectuées en parallèle). Ces opérations sont effectuées par CarWithoutUpholstery.worker () et produisent un nouvel objet qui serait un CarWithUpholstery, sur lequel j'exécuterais peut-être alors cleanInsides (), verifyNoUpholsteryDefects (), etc.
Les opérations en une seule phase sont déjà indépendantes, c'est-à-dire que je suis déjà aux prises avec un sous-ensemble d'entre elles qui peuvent être exécutées dans n'importe quel ordre (les sièges avant et arrière peuvent être installés dans n'importe quel ordre).
Ma logique utilise actuellement Reflection pour la simplicité de mise en œuvre.
Autrement dit, une fois que j'ai un CarWithoutUpholstery, l'objet se inspecte pour les méthodes appelées performSomething (). À ce stade, il exécute toutes ces méthodes:
myObject.perform001SomeOperation();
myObject.perform002SomeOtherOperation();
...
tout en vérifiant les erreurs et les trucs. Bien que l'ordre des opérations soit sans importance, j'ai assigné un ordre lexicographique au cas où je découvre que l'ordre est important après tout. Cela contredit YAGNI , mais cela coûte très peu - un simple tri () - et cela pourrait économiser un changement de nom massif de la méthode (ou l'introduction d'une autre méthode pour effectuer des tests, par exemple un tableau de méthodes) sur toute la ligne.
Un exemple différent
Disons qu'au lieu de construire une voiture, je dois compiler un rapport de police secrète sur quelqu'un et le soumettre à mon Evil Overlord . Mon dernier objet sera un ReadyReport. Pour le construire je commence par rassembler des informations de base (nom, prénom, conjoint ...). Il s'agit de ma phase A. Selon qu'il y a un conjoint ou non, je devrai peut-être ensuite passer aux phases B1 ou B2 et recueillir des données sur la sexualité d'une ou deux personnes. Il s'agit de plusieurs requêtes différentes adressées à différents Evil Minions contrôlant la vie nocturne, les caméras de rue, les reçus de vente des sex-shops et autres. Et ainsi de suite.
Si la victime n'a pas de famille, je n'entrerai même pas dans la phase GetInformationAboutFamily, mais si je le fais, alors ce n'est pas pertinent si je cible d'abord le père ou la mère ou les frères et sœurs (le cas échéant). Mais je ne peux pas le faire si je n'ai pas effectué un FamilyStatusCheck, qui appartient donc à une phase antérieure.
Tout fonctionne à merveille ...
- si j'ai besoin d'une opération supplémentaire, j'ai seulement besoin d'ajouter une méthode privée,
- si l'opération est commune à plusieurs phases je peux la faire hériter d'une superclasse,
- les opérations sont simples et autonomes. Aucune valeur d'une opération n'est jamais requise par aucune des autres (les opérations qui le sont sont effectuées dans une phase différente),
- les objets en aval n'ont pas besoin d'effectuer de nombreux tests car ils ne pourraient même pas exister si leurs objets créateurs n'avaient pas vérifié ces conditions en premier lieu. C'est-à-dire, lorsque vous placez des insertions dans le tableau de bord, nettoyez le tableau de bord et vérifiez le tableau de bord, je n'ai pas besoin de vérifier qu'un tableau de bord est réellement là .
- il permet des tests faciles. Je peux facilement simuler un objet partiel et exécuter n'importe quelle méthode dessus, et toutes les opérations sont des boîtes noires déterministes.
...mais...
Le problème est survenu lorsque j'ai ajouté une dernière opération dans l'un de mes objets de méthode, ce qui a fait que le module global dépassait un indice de complexité obligatoire ("moins de N méthodes privées").
J'ai déjà pris la question à l'étage et suggéré que dans ce cas, la richesse des méthodes privées n'est pas révélatrice d'un désastre. La complexité est là, mais elle est là parce que l'opération est complexe, et en fait ce n'est pas si complexe - c'est juste long .
En utilisant l'exemple de Evil Overlord, mon problème est que Evil Overlord (alias He Who Shall Not Be Denied ) ayant demandé toutes les informations diététiques, mes Dietary Minions me disant que je dois interroger les restaurants, les kitchenettes, les vendeurs de rue, les vendeurs de rue sans licence, les serres propriétaires, etc., et le (sub) Overlord maléfique - connu sous le nom de celui qui ne doit pas non plus être refusé - se plaignant que j'exécute trop de requêtes dans la phase GetDietaryInformation.
Remarque : je suis conscient que de plusieurs points de vue, ce n'est pas du tout un problème (en ignorant les éventuels problèmes de performances, etc.). Tout ce qui se passe, c'est qu'une mesure spécifique n'est pas satisfaisante, et cela se justifie.
Ce que je pense pouvoir faire
Hormis la première, toutes ces options sont réalisables et, je pense, défendables.
- J'ai vérifié que je peux être sournois et déclarer la moitié de mes méthodes
protected
. Mais j'exploiterais une faiblesse dans la procédure de test, et à part me justifier quand je suis attrapé, je n'aime pas ça. C'est aussi une mesure provisoire. Et si le nombre d'opérations requises double? Peu probable, mais quoi alors? - Je peux diviser arbitrairement cette phase en AnnealedObjectAlpha, AnnealedObjectBravo et AnnealedObjectCharlie, et avoir un tiers des opérations effectuées à chaque étape. J'ai l'impression que cela ajoute en fait de la complexité (N-1 classes supplémentaires), sans aucun avantage à part réussir un test. Je peux bien sûr considérer qu'un CarWithFrontSeatsInstalled et un CarWithAllSeatsInstalled sont logiquement des étapes successives . Le risque qu'une méthode Bravo soit plus tard requise par Alpha est petit, et encore plus petit si je la joue bien. Mais reste.
- Je peux regrouper différentes opérations, à distance similaires, en une seule.
performAllSeatsInstallation()
. Il ne s'agit que d'une mesure provisoire et cela augmente la complexité de l'opération unique. Si jamais je dois faire les opérations A et B dans un ordre différent, et que je les ai regroupées dans E = (A + C) et F (B + D), je devrai dégrouper E et F et mélanger le code autour . - Je peux utiliser un tableau de fonctions lambda et contourner complètement le contrôle, mais je trouve cela maladroit. C'est cependant la meilleure alternative à ce jour. Cela éliminerait la réflexion. Les deux problèmes que j'ai sont que l'on me demanderait probablement de réécrire tous les objets de la méthode, pas seulement l'hypothétique
CarWithEngineInstalled
, et bien que ce soit une très bonne sécurité d'emploi, cela n'attire vraiment pas beaucoup; et que le vérificateur de couverture de code a des problèmes avec les lambdas (qui sont résolubles, mais quand même ).
Donc...
- Quelle est, selon vous, ma meilleure option?
- Y a-t-il une meilleure façon que je n'ai pas envisagée? ( peut - être que je ferais mieux de venir net et de demander directement de quoi il s'agit? )
- Cette conception est-elle désespérément imparfaite, et je ferais mieux d'admettre la défaite et le fossé - cette architecture tout à fait? Pas bon pour ma carrière, mais écrire un code mal conçu serait-il meilleur à long terme?
- Mon choix actuel est-il en fait le One True Way, et je dois me battre pour obtenir des métriques (et / ou des instruments) de meilleure qualité installés? Pour cette dernière option, j'aurais besoin de références ... Je ne peux pas simplement agiter la main au @PHB tout en murmurant Ce ne sont pas les mesures que vous recherchez . Peu importe combien j'aimerais pouvoir