Notre domaine de connaissance implique des personnes marchant sur une plaque d'enregistrement de pression avec leurs pieds nus. Nous faisons la reconnaissance d'image qui se traduit par des objets de la classe «Pied», si un pied humain est reconnu dans les données du capteur.
Plusieurs calculs doivent être effectués sur les données du pied.
Maintenant, quelle API serait la meilleure:
class Foot : public RecognizedObject {
MaxPressureFrame getMaxPressureFrame();
FootAxis getFootAxis();
AnatomicalZones getAnatomicalZones();
// + similar getters for other calculations
// ...
}
Ou:
class Foot : public RecognizedObject {
virtual CalculationBase getCalculation(QString aName);
// ...
}
Maintenant, il y a beaucoup de pour et de contre que je peux trouver, mais je ne peux pas vraiment décider lesquels sont les plus importants. Remarque, il s'agit d'une application utilisateur final, et non d'une bibliothèque de logiciels que nous vendons.
Aucun conseil?
Certains avantages de la première approche pourraient être:
- KISS - tout est très concret. L'API, mais aussi l'implémentation.
- valeurs de retour fortement typées.
- l'héritage de cette classe est infaillible. Rien ne peut être annulé, seulement ajouté.
- L'API est très fermée, rien ne rentre, rien ne peut être outrepassé, donc moins peut mal tourner.
Certains con:
- Le nombre de getters augmentera, à mesure que chaque nouveau calcul que nous inventons sera ajouté à la liste
- L'API est plus susceptible de changer, et si des changements de rupture sont introduits, nous avons besoin d'une nouvelle version d'API, un Foot2.
- en cas de réutilisation de la classe dans d'autres projets, nous pourrions ne pas avoir besoin de tous les calculs
Quelques avantages pour la deuxième approche:
- plus flexible
- l'api est moins susceptible de changer, (en supposant que nous avons obtenu l'abstraction correcte, sinon, changer coûtera plus cher)
Certains con:
- vaguement tapé. Besoins lancés à chaque appel.
- le paramètre de chaîne - j'ai de mauvais sentiments à ce sujet (ramification sur les valeurs de chaîne ...)
- Il n'y a pas de cas d'utilisation actuel / exigence qui rend obligatoire la flexibilité supplémentaire, mais il pourrait y en avoir à l'avenir.
- l'API impose des restrictions: chaque calcul doit dériver d'une classe de base. Obtenir un calcul sera forcé par cette méthode 1, et le passage de paramètres supplémentaires sera impossible, à moins que nous ne concevions un moyen encore plus dynamique et super flexible de passer des paramètres qui augmente encore plus la complexité.
enum
et activer ses valeurs. Pourtant, je pense que la deuxième option est mauvaise, car elle s'écarte de KISS.getCalculation()
.Réponses:
Je pense que la première approche sera payante. Les chaînes magiques peuvent créer les problèmes suivants: erreurs de frappe, mauvaise utilisation, sécurité du type de retour non trivial, manque de complétion de code, code peu clair (cette version avait-elle cette fonctionnalité? Je suppose que nous le saurons au moment de l'exécution). L'utilisation d'énumérations résoudra certains de ces problèmes, mais regardons les inconvénients que vous avez soulevés:
Certes, cela peut être ennuyeux, mais cela garde les choses agréables et strictes, et vous permet de compléter le code n'importe où dans votre projet dans n'importe quel IDE moderne, avec de bons commentaires de titre qui sont beaucoup plus utiles que les énumérations.
Certes, mais c'est en fait un énorme pro;) vous pouvez définir des interfaces pour des API partielles, puis vous n'avez pas besoin de recompiler la classe dépendante qui n'est pas affectée par les API plus récentes (donc pas besoin de Foot2). Cela permet un meilleur découplage, la dépendance est désormais de l'interface et non de l'implémentation. De plus, si une interface existante change, vous aurez une erreur de compilation dans les classes dépendantes, ce qui est idéal pour empêcher le code périmé.
Je ne vois pas comment l'utilisation de chaînes magiques ou d'énumérations aidera à cela ... Si je comprends bien, soit vous incluez le code dans la classe Foot, soit vous le décomposez en quelques classes plus petites, et cela vaut pour les deux options.
la source
IEnumerable<T>.Count
], une telle approche peut permettre au code de profiter des avantages de performance des nouveaux fonctionnalités d'interface lors de l'utilisation d'implémentations qui les prennent en charge, mais restent compatibles avec les anciennes implémentations.Je recommanderais l'option 3: préciser que les calculs ne font pas partie intrinsèque de l'abstraction de a
Foot
, mais opèrent dessus. Ensuite, vous pouvez diviserFoot
et les calculs en classes distinctes, comme ceci:De cette façon, vous avez toujours un typage fort de votre calcul et vous pouvez en ajouter de nouveaux sans affecter le code existant (sauf si vous avez fait une grave erreur dans le montant des informations exposées par
Foot
).la source