Quel est le meilleur: un tas de getters ou 1 méthode avec un paramètre de chaîne de sélection?


15

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

5
Vous pouvez créer un enumet activer ses valeurs. Pourtant, je pense que la deuxième option est mauvaise, car elle s'écarte de KISS.
Vorac

Il est plus difficile de trouver toutes les utilisations d'un calcul spécifique si vous utilisez un paramètre de chaîne pour le déclencher. Difficile d'être à 100%, vous les avez tous trouvés.
Konrad Morawski

Prenez le meilleur de deux mondes et écrivez une façade avec un tas de getters invoquant getCalculation().
2014

1
Les énumérations sont définitivement meilleures que les cordes! Je n'y avais pas pensé. Ils restreignent l'API et empêchent l'abus du paramètre de chaîne (comme la concaténation de jetons et autres conneries). Je suppose donc que la comparaison est entre l'option 1 et l'option 2 avec enum au lieu de chaîne.
Bgie

Réponses:


6

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:

  • Le nombre de getters augmentera, à mesure que chaque nouveau calcul que nous inventons sera ajouté à la liste

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.

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

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

  • en cas de réutilisation de la classe dans d'autres projets, nous pourrions ne pas avoir besoin de tous les calculs

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.


J'aime l'idée d'API partielles avec interfaces, elle semble pérenne. J'irai avec celui-là. Je vous remercie. Si cela devenait trop encombré (pied implémentant trop d'interfaces), l'utilisation de plusieurs petites classes d'adaptateur serait encore plus flexible: si plusieurs variantes de pied existent avec différentes API (comme foot, humanfoot, dogfoot, humanfootVersion2), il pourrait y avoir un petit adaptateur pour chacun pour permettre à un widget GUI de fonctionner avec tous ...
Bgie

Un avantage d'utiliser un sélecteur de commandes est que l'on peut avoir une implémentation qui reçoit une commande qu'il ne comprend pas appeler une méthode d'assistance statique fournie avec l'interface. Si la commande représente quelque chose qui peut être fait avec presque toutes les implémentations en utilisant une approche à usage général, mais que certaines implémentations pourraient être capables de faire via de meilleurs moyens [considérez par exemple 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.
supercat

12

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 diviser Footet les calculs en classes distinctes, comme ceci:

class Foot : public RecognizedObject {
public:
    // Rather low-level API to access all characteristics that might be needed by a calculation
};

class MaxPressureFrame {
public:
    MaxPressureFrame(const Foot& aFoot); // Performs the calculation based on the information in aFoot
    //API for accessing the results of the calculation
};

// Similar classes for other calculations

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


Certainement plus agréable que les options 1 et 2. Ce n'est qu'à une courte étape de l'utilisation du modèle de conception de stratégie.
Brian

4
Cela pourrait être approprié. Cela pourrait être exagéré. Dépend de la complexité des calculs. Vous allez ajouter un MaxPressureFrameStrategy et un MaxPressureFrameStrategyFactory? Et il a tendance à transformer le pied en un objet anémique.
user949300

Bien que ce soit une bonne alternative, inverser les dépendances ne fonctionnerait pas dans notre cas. Le pied doit également agir comme une sorte de médiateur, car certains calculs doivent être recalculés si un autre change (en raison de la modification d'un paramètre par l'utilisateur).
Bgie

@Bgie: Avec de telles dépendances entre les calculs, je suis d'accord avec @ user116462 que la première option est la meilleure. »
Bart van Ingen Schenau
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.