J'écris un programme qui implique de travailler à la fois avec des coordonnées polaires et cartésiennes.
Est-il judicieux de créer deux structures différentes pour chaque type de points, l’un avec X
et les Y
membres et l’autre avec R
et les Theta
membres.
Ou est-ce trop et il vaut mieux avoir une seule structure avec first
et en second
tant que membres.
Ce que j'écris est simple et cela ne changera pas beaucoup. Mais je suis curieux de savoir ce qui est mieux du point de vue de la conception.
Je pense que la première option est meilleure. Cela semble plus lisible et je bénéficierai d’une vérification de type.
Réponses:
J'ai vu les deux solutions, donc cela dépend vraiment du contexte.
Pour des raisons de lisibilité, avoir plusieurs structures comme vous le suggérez est très efficace. Toutefois, dans certains environnements, vous souhaitez effectuer des manipulations courantes sur ces structures et vous vous retrouvez en train de dupliquer du code, tel que des opérations matrix * vector. Cela peut être frustrant lorsque l'opération en question n'est pas disponible dans votre type de vecteur car personne ne l'a transférée ici.
La solution extrême (que nous avons finalement retenue) consiste à avoir une classe de base basée sur un modèle CRTP dont les fonctions obtiennent <0> (), obtenant <1> () et obtenant <2> (), pour obtenir les éléments de manière générique. Ces fonctions sont ensuite définies dans la structure cartésienne ou polaire dérivée de cette classe de base. Cela résout tous les problèmes, mais a un prix assez dérisoire: avoir à apprendre la métaprogrammation des templates. Cependant, si la métaprogrammation des modèles est déjà un jeu équitable pour votre projet, cela pourrait être un bon match.
la source
Oui, cela fait beaucoup de sens.
La valeur d'une structure ne réside pas simplement dans le fait qu'elle encapsule les données sous un nom pratique. La valeur est que cela codifie vos intentions afin que le compilateur puisse vous aider à vérifier que vous ne les violez pas un jour (par exemple, confondez un jeu de coordonnées polaires avec un jeu de coordonnées cartésiennes).
Les gens ont de la peine à se souvenir de ces détails insidieux, mais ils sont bons à créer des projets audacieux et inventifs. Les ordinateurs sont bons pour les détails et mauvais pour les projets créatifs. Par conséquent, il est toujours judicieux de confier autant de détails de maintenance insignifiants à l'ordinateur, tout en laissant l'esprit libre de travailler sur le grand projet.
la source
Oui, bien que cartésien et polaire soient (à leur place) des schémas de représentation coordonnée éminemment sensés, ils ne devraient idéalement jamais être mélangés (si vous avez un point cartésien {1,1}, il est très différent de Polar {1,1 }).
En fonction de vos besoins, il peut également être utile de mettre en œuvre une Coordonnée interface, avec des méthodes comme
X()
,Y()
,Displacement()
etAngle()
(ou peut - êtreRadius()
etTheta()
, en fonction).la source
En fin de compte, le but de la programmation est de basculer les bits de transistor pour effectuer un travail utile. Mais penser à un niveau aussi bas conduirait à une folie ingérable, ce qui explique pourquoi il existe des langages de programmation de niveau supérieur pour vous aider à masquer cette complexité.
Si vous ne faites qu'une structure avec des membres nommés
first
etsecond
, alors les noms ne veulent rien dire; vous les traiteriez essentiellement comme des adresses de mémoire. Cela va à l'encontre de l'objectif du langage de programmation de haut niveau.En outre, le fait qu’ils soient tous représentables
double
ne signifie pas que vous pouvez les utiliser de façon interchangeable. Par exemple, θ est un angle sans dimension, alors que y a des unités de longueur. Comme les types ne sont pas logiquement substituables, ils doivent constituer deux structures incompatibles.Si vous avez vraiment besoin de jouer à des astuces de réutilisation de la mémoire - et vous ne devriez certainement pas le faire - vous pouvez utiliser un
union
type en C pour préciser votre intention.la source
Premièrement, ayez les deux explicitement, selon la réponse tout à fait saine de @ Kilian-foth.
Cependant, j'aimerais ajouter:
Posez la question suivante: Avez-vous vraiment des opérations qui sont génériques aux deux quand on les considère par paires
double
? Notez que ceci ne signifie pas que vous avez des opérations qui s’appliquent aux deux dans leurs propres termes. Par exemple, 'plot (Coord)' se soucie de savoir s'ilCoord
est polaire ou cartésien. De l'autre côté, continuer à archiver ne traite que les données telles quelles. Si vous avez vraiment des opérations génériques, envisager de définir soit une classe de base, ou la définition d' un convertisseur à unstd::pair<double, double>
outuple
ou tout ce que vous avez dans votre langue.En outre, une approche pourrait consister à traiter un type de coordonnées comme plus fondamental et l’autre comme un simple support pour une interaction utilisateur ou externe.
Ainsi , vous pouvez assurer que toutes les opérations de base sont codés
Cartesian
et fournir un soutien pour la conversionPolar
àCartesian
. Cela évite d'implémenter différentes versions de nombreuses opérations.la source
Une solution possible, en fonction du langage et si vous savez que les deux classes auront des méthodes et des opérations similaires, serait de définir la classe une fois et d’utiliser des alias de types afin de nommer explicitement les types différemment.
Cela a également l’avantage que tant que les classes sont exactement les mêmes, vous ne pouvez en conserver qu’une, mais dès que vous devez les modifier, vous n’avez pas besoin de modifier le code en les utilisant, car les types ont déjà été utilisés. Distincly.
Une autre option, qui dépend encore une fois de l'utilisation des classes (si vous avez besoin de polymorphisme ou autre), consiste à utiliser l'héritage public vers les deux nouveaux types, afin qu'ils aient la même interface publique que le type commun qu'ils représentent. Cela permet également une évolution séparée des types.
la source
Je pense qu'avoir le même nom de membre est une mauvaise idée dans ce cas, car cela rend le code plus sujet aux erreurs.
Imaginez le scénario: vous avez deux points cartésiens: pntA et pntB. Ensuite, vous décidez, pour une raison quelconque, de les représenter mieux en coordonnées polaires, puis de modifier la déclaration et le constructeur.
Maintenant, si toutes vos opérations étaient juste des appels de méthodes comme:
alors tu vas bien. Mais si vous utilisiez les membres explicitement? Comparer
Dans le premier cas, le code ne sera pas compilé. Vous verrez immédiatement l'erreur et pourrez y remédier. Mais si vous avez les mêmes noms de membres, l'erreur ne sera que sur le plan logique, beaucoup plus difficile à détecter.
Si vous écrivez dans un langage non orienté objet, il est encore plus facile de transmettre une mauvaise structure à la fonction. Qu'est-ce qui vous empêche d'écrire le code suivant?
D'autre part, différents types de données vous permettraient de rechercher l'erreur lors de la compilation.
la source