Le numéro de version de ce document est composé d’un numéro de version majeure, d’un numéro de version mineure et d’un numéro de révision :
<version majeure>.<version mineure>-r<révision>
Un changement de numéro de version majeure intervient lors d’une modification du contrat cassant la rétrocompatibilité avec les versions précédentes.
Un changement de numéro de version mineure intervient lors d’une modification du contrat qui ne casse pas la rétrocompatibilité.
Un changement de numéro de version de révison intervient à tout autre modification de ce document sans effet sur le contrat ou relevant de corrections d’erreurs flagrantes.
Les changements de version majeure, mineure et de révision interviennent chacune par incrémentation d’une valeur entière. Le numéro de révision est réinitialisé à 0 lors d’un changement de version mineure ou majeure. Le numéro de version mineure est réinitialisé à 0 lors d’un changement de version majeure.
On recommande que les implémentations se référant à cette spécification suivent elles-mêmes un versionnement à trois valeurs (majeure, mineure et révision) les versions majeure et mineure se référant respectivement aux versions majeure et mineure de la présente spécification et la version de révision étant réservée aux correctifs internes de l’implémentation ou aux modifications qui ne relèvent pas de critères impératifs de la présente spécification (par exemple, si une implémentation simplement suggérée par la présente spécification vient à disparaître d’une implémentation, quoique cela crée ainsi une rupture de rétrocompatibilité). On recommande aussi dans ce cas que l’implémentation indique de manière explicite que son numéro de version se réfère au numéro de version de la présente spécification. Dans le cas contraire, toute implémentation se référant à la présente spécification sans s’aligner sur ses numéros de version majeure et de version mineure doit indiquer explicitement le numéro de version complet de la spécification.
La présente spécification abstraite s’inspire de la JSR 363 de la communauté Java mais en restreint le périmètre.
Elle est destinée à guider l’implémentation de bibliothèques de construction et de conversion d’unités et non pas à fournir des collections d’unités courantes déjà construites. Il ne s’agit pas non plus d’une bibliothèque d’interprétation de chaines de caractères comme unités ni d’écriture d’unités sous forme de chaînes de caractères.
En définissant les unes par rapport aux autres les unités qui lui sont nécessaires, la présente spécification vise à permettre à l’utilisateur d’une implémentation de s’affranchir de la question rébarbative de l’implémentation des convertisseurs d’unité à unité.
En adoptant un périmètre limité, la présente spécification s’assigne également l’objectif de s’abstraire des spécificités des langages de programmation de manière à ce que la représentation conceptuelle qu’elle propose puisse être aisément implémentée en plusieurs d’entre eux en y consacrant un minimum d’effort. Toutefois, la spécification est explicitement orientée objet.
La présente spécification n’inclut pas les contrôles d’homogénéité de conversion d’unités ni de cohérence des dimension ni des grandeurs. La cohérence des conversions est laissée à l’utilisateur.
On se restreint à la représentation des conversions et des unités communes de manière à n’implémenter que des cas relativement simples de conversions affines ou linéaires.
La conceptualisation des bases, des systèmes d’unités et des considérations relatives aux mesures n’entrent pas dans le champ de cette spécification.
Sauf mention contraire relative aux éléments optionnels ou recommandés, le respect de la spécification abstraite est impératif.
Dans les diagrammes de cette section, les méthodes en italique sont optionnelles. Leur implémentation est recommandée pour qui souhaite s’aligner sur la spécification d’implémentation qui les utilise.
Un convertisseur représente une conversion affine ou linéaire d’une unité à une autre. La méthode convert() prend en paramètre une valeur exprimée dans l’unité source et retourne son expression dans l’unité cible. L’unité source est celle à partir de laquelle s’effectue l’appel de la méthode de création de convertisseur, l’unité cible étant passée en paramètre à cette méthode.
Les méthodes scale() et offset() retournent respectivement la pente et l’ordonnée à l’origine de la formule de conversion affine et la méthode inverse() retourne le convertisseur inverse de l’unité cible vers l’unité source du convertisseur.
On recommande qu’un convertisseur soit identique, au sens de l’instance, au convertisseur inverse de son convertisseur inverse.
La méthode linear() est optionnelle. Elle construit un convertisseur qui ne garde que la partie linéaire du convertisseur sur lequel la méthode est appelée. Lorsque cette méthode est implémentée, on recommande que si le convertisseur sur lequel elle est appelé est déjà linéaire, alors la méthode renvoie ce convertisseur lui-même au sens de l’instance.
La méthode linearPow() est optionnelle. Elle construit un convertisseur qui ne garde que la partie linéaire du convertisseur sur lequel la méthode est appelé, mais en l’élevant à la puissance indiquée en paramètre. Lorsque cette méthode est implémentée, on recommande que si le convertisseur sur lequel elle est appelée est déjà linéaire et que la puissance indiquée en paramètre est égale à 1, alors la méthode renvoie ce convertisseur lui-même au sens de l’instance.
La méthode concatenate() produit un convertisseur dont la méthode convert() est équivalente à l’évaluation en premier lieu de la méthode convert() du convertisseur indiqué en paramètre et en second lieu de la méthode convert() du convertisseur d’appel sur la valeur qui en résulte.
On recommande que les implémentations fournissent des convertisseurs immuables et indiquent le cas échéant cette caractéristique de manière explicite.
Le type abstrait Unit représente une unité de manière générale. Les unités concrètes sont de trois types : les unités fondamentales (FundamentalUnit) ne sont définies à partir d’aucune autre unité. Les unités transformées (TransformedUnit) sont définies par une unité de référence et un convertisseur qui représente la formule linéaire ou affine qui permet de passer de l’unité transformée à son unité de référence. Les unités dérivées (DerivedUnit) sont définies par une collection d’unités chacune élevée à une puissance rationnelle, l’association d’une unité et d’une puissance rationnelle constitutant un facteur (Factor) de l’unité dérivée. Il n’est pas requis que les facteurs soient eux-mêmes des unités.
Les unités transformées et dérivées sont ainsi définies à partir d’autres unités et donc, directement ou indirectement, de manière ultime à partir d’un jeu d’unités qui sont toutes fondamentales.
Toute unité est capable de fournir d’une part avec la méthode getConverterTo(), un convertisseur vers une unité cible (sous réserve, dont l’appréciation est laissée à l’utilisateur, de la cohérence entre l’unité de départ et l’unité cible du point de vue des unités fondamentales qui la composent) et d’autre part avec la méthode toBase(), un convertisseur vers le jeu d’unités fondamentales qui la composent.
Les unités sont des instances immuables (impératif).
Un facteur représente l’association d’une unité à un exposant rationnel. La combinaison des facteurs permet de construire les unités dérivées.
On suggère que le concept d’unité spécialise celui de facteur de manière à considérer par polymorphisme qu’une unité est un facteur d’elle-même élevée à la puissance 1. Cette représentation permet d’éviter la construction de facteurs d’unités à la puissance 1 dans la définition de nombreuses unités dérivées courantes.
On suggère l’implémentation de méthodes de construction d’unités filles à partir d’une unité parente à la manière des méthodes spécifiées par la JSR 363, parmi lesquelles les méthodes utilitaires suivantes :
La méthode shift() construit une unité transformée dont l’unité de référence est l’unité d’appel de la méthode et le convertisseur à l’unité de référence est une translation dont la valeur est indiquée en paramètre.
La méthode scaleMultiply() construit une unité transformée dont l’unité de référence est l’unité d’appel de la méthode et le convertisseur à l’unité de référence est un agrandissement dont la valeur est indiquée en paramètre.
La méthode scaleDivide() construit une unité transformée correspondant à un appel à scaleMultiply() avec un argument donc la valeur est inversée.
La méthode factor() construit un facteur dont l’unité est l’unité d’appel et la puissance est un rationnel tel que le numérateur corresponde au premier paramètre de la méthode et le dénominateur au second.
La spécification d’implémentation est indicative. La spécification dans son ensemble peut être respectée par une implémentation sans qu’elle s’aligne pour autant sur les choix d’implémentation exposés par cette section.
Les convertisseurs sont construits par concaténation de la conversion de l’unité de départ vers les unités fondamentales qui la composent et de la conversion inverse de l’unité cible vers ces mêmes unités fondamentales (Algorithme 1).
Pour cela, chaque type d’unité doit être capable de définir la conversion par laquelle exprimer une valeur dans les unités fondamentales qui la composent en implémentant la méthode toBase() de manière spécifique chacune à sa nature.
La conversion d’une unité fondamentale vers le jeu d’unités fondamentales qui la constitue est l’identité. On recommande d’implémenter cette identité comme un convertisseur singleton.
La conversion d’une unité transformée vers le jeu d’unités fondamentales qui la composent consite à concaténer la conversion vers son unité de référence à la conversion vers le jeu d’unités fondamentales qui composent cette dernière.
Enfin, la conversion d’une unité dérivée vers le jeu d’unités fondamentales qui la composent consiste à parcourir les facteurs de définition de l’unité, d’en extraire la conversion vers le jeu d’unités fondamentales de l’unité du facteur, de n’en garder que la part linéaire et de l’élever à la puissance du facteur. Chaque facteur fournissant ainsi un convertisseur vers son jeu d’unités fondamentales, tous sont concaténés de manière à produire un convertisseur global.
L’algorithme ne garde que la partie linéaire des convertisseurs résultants des unités référencées par chaque facteur car on considère que les translations doivent disparaître ainsi qu’on s’y attend, par exemple en convertissant des degrés Celcius par mètre en Kelvin par mètre.
La conformité aux critères de validation est impérative au respect de la spécification. On ne donne cependant pas de critère relatif à la précision des conversions au-delà des valeurs indiquées.