Programmez 249
Programmez 249
.NET 6
JAVA 17
N°249
11/12
2021
Le
développeur
va sauver
© source : yogysic
Contenus
⊦≰ Agenda ⊦≺⊔ Coder Fibonacci en Java avec des tests
Les événements pour les développeurs Codons le calcul des termes de la suite de Fibonacci en Java
La rédaction grâce au TDD.
Thierry Leriche
⊦≷⊗ Java 17
Java 17 vient de sortir. ⊦≻⊘ Interopérabilité Kotlin et Java partie 2
Quelles nouveautés pour les développeurs ? Revenons sur l’interopérabilité entre Kotlin et Java.
Loïc Mathieu Sallah Kokaina
⊦≷⊙ Le développeur va sauver la Terre ! ⊦≼⊓ Par les deux bouts de la lorgnette
Partie 1 : posons les fondamentaux Question sur l’observabilité !
L’éco-conception doit permettre de mieux utiliser les ressources Jean-Baptiste Bron
technologiques, réduire l’impact de l’informatique et optimiser les
codes, le binaire, le poids des pages web, etc; Dossier complet
sur les bonnes pratiques et pourquoi les utiliser.
Mathieu Touchard, Pierre Lagarde, Raphaël Lemaire,
Benoit Petit, Vincent Frattaroli, François Rézenthel
Divers
Approval Tests
Quoi ? Vous ne connaissez les Approval Tests ?
Il est temps de combler vos lacunes.
Sepehr Namdar
⊦≮ Edito
⊦≹⊚ Superviser votre Linux avec un écran LCD !
Le développeur est comme Chuck Norris,
il va nous sauver (ou nous défoncer)
Un peu de code, des libs, un écran LCD. Ce projet permet
monitorer son système Linux très simplement avec lcd4linux.
Sébastien Colas
≸⊔ ≸⊕ Abonnement & Boutique
ÉDITO
HORS SÉRIE #5
AUTOMNE
PROGRAMMEZ!
N°250
100% Red Hat
Disponible
Disponible le 3 janvier 2022
dès le 26 novembre 2021
4 ÉDITO [Link]
005_promoHS05.qxp_249 20/10/2021 08:59 Page43
i b l e bre
p o n em
Di 6 nov
s
l e 2
dè s
Couverture
100 % Développeur
006.qxp_249 18/10/2021 18:52 Page6
AGENDA
Meetups Programmez!
1 2 3 4 5 6 7
A partir de 18h30
Open Source
Experience (Paris)
DevFest
DevCon
16 décembre : conférence Cybersécurité
Strasbourg
janvier 2022
Publicité
1 2 Nefer-IT : Tél. : 09 86 73 61 08 - ftonic@[Link]
Impression : SIB Imprimerie, France
3 4 5 6 7 8 9
Dépôt légal : A parution
10 11 12 13 14 15 16 Commission paritaire : 1225K78366
17 18 19 20 21 22 23 ISSN : 1627-0908
Abonnement
Abonnement (tarifs France) : 49 € pour 1 an, 79 € pour 2 ans.
Touraine
Etudiants : 39 €. Europe et Suisse : 55,82 € - Algérie, Maroc, Tunisie : 59,89 € -
Tech
Canada : 68,36 € - Tom : 83,65 € - Dom : 66,82 €.
(Tours)
24 25 26 27 28 29 30 Autres pays : consultez les tarifs sur [Link].
31 Pour toute question sur l’abonnement :
abonnements@[Link]
février 2022 Abonnement PDF
monde entier : 39 € pour 1 an.
1 2 3 4 5 6 Accès aux archives : 19 €.
Nefer-IT
SnowCamp (Grenoble)
57 rue de Gisors, 95300 Pontoise France
7 8 9 10 11 12 13 redaction@[Link]
14 15 16 17 18 19 20 Tél. : 09 86 73 61 08
Toute reproduction intégrale ou partielle est interdite sans accord des auteurs et du
21 22 23 24 25 26 27 directeur de la publication. © Nefer-IT / Programmez!, octobre 2021.
28 29 30 1 juil. 2 juil.
Merci à Aurélie Vache pour la liste 2021, consultable sur son GitHub : [Link]
6 AGENDA [Link]
007.qxp_249 18/10/2021 18:59 Page7
N°7+8
60 pages
NOUVEAU !
Abonnement 1 an : 29 €
008.qxp_249 18/10/2021 16:05 Page8
BRÈVES
par la rédaction de
Twitch, en Pénurie de semi-conducteurs : l’industrie Let’s Encrypt avait pourtant averti les
open automobile trinque utilisateurs concernés et les avait invi-
source… La pénurie de semi-conducteurs se poursuit et les fondeurs ont du mal à
tés à renouveler les certificats
contre faire face à la demande croissante de microprocesseurs et de semi-
potentiellement affectés avant la date
sa volonté conducteurs. Ils ont visiblement décidé de privilégier leurs clients
fatidique, mais ça n’a pas suffi. Le 30
Une autre histoire de serveur mal septembre, de nombreux services ont
historiques au détriment des nouveaux venus, notamment l’industrie auto-
configuré a eu des conséquences donc connu des problèmes liés à
mobile qui est devenue au cours des dernières années particulièrement
désagréables pour Twitch, la plate- leurs certificats : on peut ainsi citer
gourmande en semi-conducteurs. Résultat, plusieurs constructeurs ont été
forme de streaming live détenue par Bluecoat, Ovhcloud, Shopify, Ledger
contraints d’annoncer un ralentissement sur leurs usines de productions, à
Amazon. Dans la nuit du 6 octobre, ou encore Cloudflare parmi de nom-
l’instar de Stellantis, issu de la fusion de PSA et FCA, qui a annoncé la fer-
un internaute a publié sur 4chan un breux autres. La rançon du succès
meture de plusieurs de ses usines européennes jusqu’à la fin de l’année.
lien menant vers une archive d’envi- pour Let’s Encrypt, qui est devenu en
ron 128Go de données, l’espace de dix ans un acteur central
apparemment dérobées sur les ser- de l’écosystème des autorités de cer-
veurs de Twitch. On y retrouvait tification et par la même un SPOF
notamment le code source de plu- (Single Point of Failure, point de dé-
sieurs applications développées par faillance unique en français).
la plateforme, mais aussi des projets
internes tels que Vapor, un potentiel Braquage à l’APHP
concurrent de Steam développé par Les hôpitaux de Paris ont annoncé
les équipes de Twitch. Et pour ajou- début septembre avoir été victime
ter au drame, les attaquants ont d’un vol de données record : les don-
également publié une liste des reve- nées de test Covid appartenant à 1,4
nus perçus par les streamers les plus ment propose de labelliser des solu- Let’s Encrypt, un million de Franciliens ont été déro-
en vue de la plateforme. Rude se- tions étrangères opérées par des certificat vous manque bées dans le courant de l’été.
maine pour Twitch donc, qui acteurs français. Depuis, les an- et SPOF, tout est cassé L’attaquant a exploité une faille zero
continue ses investigations sur l’ori- nonces se multiplient : Capgemini, Quand vous devenez un acteur cen- day dans un logiciel utilisé par l’AP-
gine exacte de la fuite. Orange et Microsoft s’acoquinent tral de l’écosystème web, le moindre HP pour transférer ces données vers
sur « bleu », OvhCloud propose un dysfonctionnement peut avoir des les serveurs de l’Assurance Maladie.
Cloud de confiance : partenariat avec Whaller et Thales conséquences en cascades. C’est ce Un suspect a rapidement été arrêté
le temps presse et les avancera de son côté avec Google qui est arrivé aux utilisateurs de Let’s par les enquêteurs : il s’agit d’un étu-
acteurs s’organisent Cloud. Pour l’instant, ce ne sont que Encrypt à la fin du mois de sep- diant en informatique de 22 ans, qui
Les annonces de partenariats entre des annonces, mais les offres sont tembre : un certificat racine utilisé reconnaît être à l’origine du vol et
acteurs américains et européens se attendues avec une certaine impa- pour valider des certificats SSL fournis clame avoir voulu prouver les failles
multiplient dans le cadre du label tience, surtout du côté de par l’ONG est arrivé à expiration. de la sécurité du système de santé
Cloud de Confiance. Petit rappel l’administration et des ministères, à français. Des failles démontrées de
pour ceux qui s’y perdraient : afin de qui on a gentiment rappelé que manière un peu trop convaincante,
permettre aux entreprises de bénéfi- souscrire à des offres Office 365 hé- qui lui valent donc aujourd’hui d’at-
cier de solutions sans s’exposer aux bergées sur Azure n’était pas tendre un procès suite aux plaintes
risques juridiques liés à des lois vraiment en accord avec la politique déposées par l’AP-HP et les diffé-
comme le Cloud Act, le gouverne- de cloud de l’Etat. rentes victimes du vol de données.
8 [Link]
007.qxp_249 18/10/2021 17:38 Page9
Niveau padawan
[Link] 9
010_013.qxp_249 18/10/2021 16:08 Page10
À la rencontre de .NET 6
Après 1 an d’attente, plusieurs previews et les multiples posts des équipes dévelop-
peurs, Microsoft lance .Net 6. Pierre Gascoin (expert technique, SQLI) nous présente la
Pierre Gascoin nouvelle version. Bonne découverte. La Rédaction.
Expert technique, SQLI
10 [Link]
010_013.qxp_249 18/10/2021 16:08 Page11
Hot reload
Un des grands piliers poussés par Microsoft est l'optimisation
du cycle de développement des applications autour de son
framework. C'est dans cette optique qu'une nouvelle fonction-
nalité appelée « Hot reload » fait son apparition (ou « rechar-
gement à chaud » pour les puristes de la langue française).
Initialement mis en place par les équipes en charge de
Xamarin pour le rechargement à chaud du XAML, il a été
décidé de généraliser ce concept à l'ensemble du framework.
Avec cette fonctionnalité, le développeur peut modifier le
code de ses applications (code managé uniquement) pen-
dant que celles-ci sont en cours d'exécution. Les modifica-
tions sont alors directement prises en compte et propagées
sur l'application déjà lancée.
Pour cela, il suffit de réaliser une modification dans le code
et de cliquer sur le nouveau bouton « Apply Code Changes »
(« appliquer les changements ») dans Visual Studio afin d’ap- Figure 1
pliquer ces modifications. • Aux déclarations top-level dans le [Link] (pas d’espace Initialisation en [Link]
Microsoft mentionne que le Hot reload est supporté pour de de nom, de classe ou de déclaration de méthode) Core WebAPI .NET 5
Source : Microsoft
nombreux types de projets tels que les applications WPF, • Au nouveau modèle d’hébergement [Link]
[Link] Core (code-behind), console, WinUI3… Il est aussi • Et à la mise en place des usings implicites
disponible sur l’ensemble des applications qui sont basées sur Ci-dessous, voici un exemple (provenant du devblogs de
les runtimes du Framework .NET et CoreCLR. Microsoft) qui compare la quantité de code à écrire afin de
Avec .NET 6, le rechargement à chaud est disponible à partir mettre en place un service avec l’initialisation de
de la ligne de commande dotnet watch. Pour rappel, l’utilitaire Swagger/OpenAPI entre les versions [Link] Core web API
dotnet watch permet de surveiller les fichiers du projet pendant en .NET 5 et [Link] Core web API en .NET 6 : Figure1
l’exécution afin de redémarrer celui-ci si une modification est
apportée à un fichier. Désormais, les modifications seront Initialisation en [Link] Core WebAPI en .NET 6
rechargées à chaud sans redémarrage : à noter que dans le
var builder = [Link](args);
cadre de modifications nécessitant obligatoirement un redé-
marrage dotnet watch devrait demander une confirmation préa-
[Link]();
lable du développeur.
[Link](c =>
{
Minimal web API & native-cloud [Link]("v1", new() { Title = "WebApplication22", Version = "v1" });
Microsoft souhaite faire du framework .NET un produit de
});
premier choix lorsqu’il s’agit de produire une application
cloud-native. Et cela passe forcément par une méthode de
var app = [Link]();
création simple et rapide de service. Jusqu’ici, la création des
services nécessitait un certain nombre de codes d’initialisa-
if ([Link]())
tion : même pour la plus simple des API. Avec .NET 6, c’est
{
désormais du passé avec l’arrivée des “minimal web API”. Le
[Link]();
concept est simple : avoir la possibilité de créer des services
[Link](c => [Link]("/swagger/v1/[Link]", "Web
rapidement avec un minimum de code. Cela est particulière-
Application22 v1"));
ment pratique pour les applications de type micro-services
}
qui exposent de nombreuses API au scope limité.
Désormais la création d’un projet web :
[Link]();
dotnet new web
[Link]();
Génère le fichier unique suivant :
[Link] 11
010_013.qxp_249 18/10/2021 16:08 Page12
12 [Link]
010_013.qxp_249 18/10/2021 16:08 Page13
[Link] 13
014_016.qxp_249 18/10/2021 16:07 Page14
14 [Link]
014_016.qxp_249 18/10/2021 16:07 Page15
blie l’existence de fonctionnalités dévelop- lée avant la méthode Close(). Ce problè- Gestion des dépendances
pées quelques années auparavant. me était indétectable si je n’avais pas eu La gestion des dépendances est une étape
Pour ne rien oublier, l’idéal serait d’avoir un un test qui vérifiait le contenu du fichier critique de la migration. Il existe deux ma-
cahier de recettes couvrant la totalité des généré. nières de référencer des librairies externes :
cas. • Lors d’une migration vers ASP .net core 3, • L’approche manuelle, qui consiste à ajou-
Malheureusement, ce type de document il est devenu impossible de manipuler les ter une référence en allant chercher une
évolue souvent moins vite que nos applica- Request et Response dans un DLL sur son PC.
tions et rares sont les développeurs qui y Middlerware sans utiliser leurs méthodes • L’approche automatisée via un gestion-
prêtent attention. Ce qui se traduit souvent asynchrones. Les méthodes synchrones naire de paquets comme NuGet.
par de nombreux aller-retour entre les existant toujours, la compilation se pro- La première approche est celle qui présente
équipes de développement et les testeurs. duisait sans problème. Les tests unitaires le plus d’inconvénients :
Ceux-ci conduisent inévitablement à une d’intégration étaient la seule solution • Les DLL doivent être déployées sur le PC
perte de temps et d’énergie. Avec le temps, pour anticiper les problèmes et leur trou- de développement (via le repository,
ce phénomène ne fera que s’accentuer et ver une solution. XCopy, ou MSI)
amènera vos équipes à résister face au • La mise à jour d’une même DLL pour plu-
changement. La migration pouvant impli- Réduire la voilure sieurs projets est plus laborieuse.
quer de grands changements, il faut trouver Parmi les nombreuses fonctionnalités de nos • Les dépendances indirectes ne sont pas
une réponse au problème dès aujourd’hui. applications, il n’est pas rare que certaines facilement identifiables.
Par chance, il existe une solution spéciale- ne soient plus utilisées. Il n’est pas rare non • Les dépendances indirectes ne sont pas
ment adaptée pour les développeurs. Il plus que l’on garde du code mort (des mé- référencées automatiquement.
s’agit des tests unitaires. Mais là encore, il thodes, ou classes qui ne servent plus). Ces • Il n’est pas possible de référencer les va-
est rare qu’une application soit intégrale- situations peuvent sembler bénignes. Mais riantes x86 et x64 d’une même DLL dans
ment couverte par des tests unitaires. que se passera-t-il si après le passage à .net un projet.
Pour éviter des drames lors de la migration, 6 on découvre qu’elles produisent une er- A contrario, NuGet :
il faut trouver le moyen d’optimiser l’usage reur de compilation. Faudra-t-il les • Télécharge, référence automatiquement
du cahier de recette et des tests unitaires corriger ou les abandonner ? Que se passe- les DLL, et leurs références indirectes.
codés. ra-t-il si l’on découvre au dernier moment • Permet la mise à jour en un clic de tous
Présenter de la sorte, le chantier peut sem- que ces codes dépendent de librairies qui ne les projets d’une solution.
bler colossal. Si vous partez de zéro, il ne supportent pas .net 6 ? • Permet de gérer facilement des profils de
faut pas s’inquiéter. L’approche la plus effi- Pour ne pas avoir à prendre des décisions à compilation x86, x64 ou autres.
cace consiste à procéder par itérations. la hâte, et ne pas perdre de temps, il De plus, Nuget a un grand intérêt dans le
Chaque itération consistant en : convient d’identifier très vite ces fonctionna- contexte d’une migration : il permet de
• L’identification d'un petit nombre de fonc- lités et codes morts. S’ils ne sont plus utiles, connaître les dépendances indirectes d’un
tionnalités importantes pour les autant les supprimer tout de suite. paquet en fonction de la version de .net ci-
utilisateurs. Bien évidemment, je ne vous encourage pas blée. La liste de dépendances d’un paquet
• La documentation des scénarios d’usages à supprimer votre code de manière barbare. est consultable via l’interface de gestion
de ces fonctionnalités pour le cahier de L’idée ici est de vous faire prendre conscien- NuGet de Visual Studio.
recette. ce qu’il faut savoir réduire la voilure de son Même si l’interface de Visual Studio est
• Le codage de tests unitaires relatifs à ces application plutôt que de subir. Parmi ces agréable, je ne la trouve pas adaptée dans
fonctionnalités. fonctionnalités, il est aussi probable que le cadre d’une migration. Je vous conseille
Progressivement, vos applications seront certaines n’auront plus de raison d’être plutôt d’aller sur le site [Link]
davantage couvertes par des tests unitaires. après la migration. Si la suppression ne et d’y rechercher les librairies que vous utili-
Lors des publications pour les testeurs, peut pas avoir lieu actuellement, il faut pen- sez.
ceux-ci vous remontrent moins de bugs. Ce ser à parquer le code concerné avec Exemple : Pour Open-XML-SDK Figure 1
que l’on appelle couramment le chemin cri- l'attribut [Obsolete]. Le grand intérêt du site Nuget par rapport à
tique finira par être entièrement sécurisé. Exemple : je maintiens seul depuis 2011 Visual Studio est qu’il permet de consulter
Avant même d’avoir commencé la migra- une application qui a débuté sur Windows la page du paquet d’une dépendance indi-
tion, votre application aura gagné en en WPF, puis Windows Phone 7 en recte pour valider ses propres dépendances.
qualité. Quand le projet de migration débu- SilverLight, et Windows 8, pour finir sur De la sorte, il est possible de vérifier l’en-
tera, vous pourrez rapidement vérifier son Windows 10 avec UWP. Dans le temps, j’ai semble des dépendances directes ou
impact et éviter tout effet de bord avant été obligé de supprimer des fonctionnali- indirectes, et de valider leur compatibilité
même de publier vos premières versions en tés telles que le support de l’API de avec .net 6. Dans le cas contraire, il faudra
tests. recherche de Windows 8. Pour supporter rechercher une librairie de substitution com-
Par le passé, cette démarche m’a permis Windows 11, il est évident que je vais me sé- patible.
d’éviter de nombreux problèmes. Voici parer du code dédié à la gestion des tuiles En lisant ces quelques lignes, je pense que
quelques cas concrets : dynamiques. vous aurez compris que l’adoption de
• Lors du passage de .net 1.0 à 1.1 j’ai été NuGet est indispensable. Continuer à réfé-
confronté à des erreurs du fait de Stream rencer manuellement ses dépendances
dont la méthode Flush() n’était pas appe- n’est pas raisonnable et s’avérera extrême-
[Link] 15
014_016.qxp_249 18/10/2021 16:07 Page16
16 [Link]
017_020.qxp_249 20/10/2021 18:52 Page17
Les nouveautés de C# 10
Depuis la version .NET Core 3.1 en 2019, Microsoft a planifié de mettre à jour la ver-
sion de son framework .NET chaque mois de novembre. 2020 a marqué la sortie de
.NET 5, ainsi que de son langage phare C# en version 9. Cette année, Microsoft conti- Baptiste Bazureau
Expert technique
nue sur sa lancée et annonce la sortie de .NET 6 accompagnée de C# 10 dont nous SQLI
vous proposons de décortiquer les nouveautés et améliorations.
Nous vous précisons qu’au moment où nous rédigeons ces public Point(int x, int y)
lignes, C# 10 n’est pas encore sorti dans sa version définitive. {
Certaines fonctionnalités pourraient encore évoluer, voire ne X = x;
pas être embarquées. Y = y;
}
Les structures d’enregistrement François Lefebvre
En 2020, C# 9 a introduit le mot clé record qui permet de Expert technique
public int X { get; init; }
définir un type référence fournissant des fonctionnalités inté- SQLI
grées pour l’encapsulation des données. Il était jusqu’ici public int Y { get; init; }
possible de l’affecter uniquement à des classes, ce que C# }
10 étend désormais aux structures. Les structures d’enregis-
trement sont de type valeur tout comme le sont les structures Ensuite, nous remarquons la présence du mot clé struct qui
classiques. Cela signifie qu’elles répondent à des règles com- spécifie qu’il s’agit bien d’une structure d’enregistrement, et
munes. À noter que C# 10 va lever certaines restrictions sur non une classe d’enregistrement. En C# 9 les classes d’enre-
les structures. Il rend possible la création d’un constructeur gistrement étaient déclarées sans le mot clé class:
sans paramètre, ainsi que l’initialisation des propriétés dès
public record Person(string FirstName, string LastName);
leur déclaration, comme le montre l’exemple suivant :
Cela reste possible, mais C# 10 a rendu faisable l’ajout
public struct Point
optionnel du mot clé class afin d’éviter les confusions.
{
public Point() // constructeur sans paramètre public record class Person(string FirstName, string LastName);
{
Les expressions with
X = 5;
Tout comme avec les classes d’enregistrement, les structures
}
d’enregistrement peuvent être instanciées à l’aide des
expressions with. Celles-ci permettent de créer une copie
public int X { get; set; }
d’une structure d’enregistrement tout en lui spécifiant une ou
plusieurs données différentes. Prenons l’exemple suivant :
public int Y { get; set; } = 3; // initialisation à la déclaration
} var firstPoint = new Point(1, 2);
var secondPoint = firstPoint with { Y = 3 };
La déclaration d’une structure d’enregistrement
Il devient dès lors possible de déclarer une structure d’enre-
[Link](secondPoint);
gistrement sur une seule ligne :
// sortie : Point { X = 1, Y = 3 }
public readonly record struct Point(int X, int Y);
La propriété X de secondPoint est issue de l’instance de
Notons plusieurs points dans cette déclaration : tout d’abord firstPoint tandis que la propriété Y est définie grâce à l’utilisa-
la présence du mot clé readonly qui n’est pas disponible pour tion de l’expression with.
les classes d’enregistrement. Son ajout optionnel permet de
spécifier que les propriétés de la structure d’enregistrement La comparaison d’égalité
sont immuables. En effet, elles ne le sont pas par défaut, ce L’égalité entre deux structures d’enregistrement s’effectue en
qui constitue l’une des différences majeures avec les classes fonction de leurs valeurs. Nous pouvons donc utiliser le mot clé
d’enregistrement. De plus, les différents paramètres déclarés Equals afin de les comparer comme nous le ferions pour les struc-
seront convertis en propriétés par le compilateur, et le tures classiques, mais également les opérateurs == et != qui
constructeur ainsi généré les renseignera à l’initialisation sont utilisables uniquement pour les structures d’enregistrement :
d’une nouvelle instance. Cette syntaxe est donc équivalente
var firstPoint = new Point(1, 2);
à celle ci-dessous, qui utilise le mot clé init introduit en C# 9
var secondPoint = new Point(1, 2);
rendant les propriétés immuables :
[Link] 17
017_020.qxp_249 20/10/2021 18:52 Page18
public record Employee(string FirstName, string LastName, int Grade) string firstName = "Jane";
: Person(FirstName, LastName);
(firstName, string LastName) = person;
18 [Link]
017_020.qxp_249 20/10/2021 18:52 Page19
class Employee { }
Les directives global using
}
Dans un projet C#, il est fréquent d’utiliser les mêmes types
issus des mêmes espaces de noms, ce qui provoque la multi-
namespace [Link]
plication de directives using similaires à travers les fichiers.
{
Pour éviter cette répétition, C# 10 permet de rendre dispo-
class Bill { }
nible des namespaces à l’ensemble des fichiers de code d’un
}
projet. Pour cela, il suffit d’ajouter le mot clé global précé-
dant une instruction using et l’espace de noms devient dispo- Dans la plupart des projets, les fichiers de code ne contiennent
nible globalement. qu’un seul namespace. Une nouvelle syntaxe introduite avec
Par exemple, si l’on crée une application web à partir du C# 10 permet de déclarer un espace de nom en haut de fichier,
modèle [Link] Core avec authentification, le et dont la portée s’étend à l’ensemble du fichier. Pour cela il
HomeController généré ressemble à cela, sachant que le suffit d’utiliser le mot clé namespace sans les accolades :
contenu de la classe est omis :
namespace [Link];
using [Link];
Cette syntaxe améliore la lisibilité horizontale du code en
using [Link];
réduisant l’indentation d’un niveau.
using [Link];
using [Link];
Patterns de propriétés étendus
Historiquement, le mot clé “is” était un opérateur pour tester
namespace [Link];
uniquement le type et convertir une donnée. C# 7 a introduit
[Authorize]
le pattern matching – se traduisant par « critères spéciaux » –
public class HomeController : Controller {}
qui permet de tester si une donnée correspond à un pattern.
// Le contenu de la classe est omis
Le pattern matching a systématiquement continué à être
Il y a de fortes chances pour que les autres contrôleurs du pro- amélioré depuis, et cette version ne déroge pas à la règle.
jet utilisent aussi les namespaces [Link] Jusqu’alors, pour faire un test de correspondance envers des
et [Link]. propriétés imbriquées, il nous fallait utiliser un modèle avec
Avec C# 10, nous pouvons créer un fichier [Link] des objets imbriqués. Prenons l’exemple suivant : une voiture
à la racine du projet avec le code suivant : est composée d’une roue, et la roue possède une taille. Nous
devons effectuer une action spéciale si l’on traite une voiture
global using [Link];
avec une roue de 17 pouces. Voici l’utilisation d’un pattern
global using [Link];
matching en C# 9 :
Nous pouvons ainsi retirer ces usings dans le fichier
record Car(Wheel Wheel);
[Link], qui devient donc :
record Wheel(int Size);
using [Link]; if (vehicule is Car { Wheel: { Size: 17 } })
using [Link]; DoSomething();
[Link] 19
017_020.qxp_249 20/10/2021 18:52 Page20
const string PRODUCT_VERSION = "Hirsute Hippo"; // Equivalent avec la fonctionnalité de vérification de la nullité
void Foo(string bar!!) { }
[Osolete($"This method will be removed after version {PRODUCT_VERSION}")]
L’opérateur !! permettra de s’assurer que le paramètre de la
void Foo() { }
fonction est non null. Attention toutefois à ne pas confondre
Les améliorations cette fonctionnalité avec la notion de types références nul-
des fonctions lambda lables introduite avec C# 8. Pour rester concis, les types réfé-
C# 10 apporte quelques améliorations autour des expres- rences nullables donnent des avertissements à la compilation
sions lambda. L’inférence de type a été améliorée. Il n’est sur des déréférencements qui pourraient potentiellement pro-
dorénavant plus nécessaire de préciser le type d’une variable voquer des NullRerefenceException. Ici l’opérateur !! n’a
contenant une fonction lambda. Nous pouvons simplement aucune incidence à la compilation, mais uniquement à l’exé-
la déclarer avec le mot clé var. cution. Il simplifie la vérification de la nullité des paramètres,
ce qui allège le code et facilite le travail des adeptes de la
var returnOne = () => 1;
programmation défensive.
Jusqu’à C# 9, cela aurait produit une erreur de compilation La fonctionnalité de propriétés obligatoires – propriétés pré-
(CS0815: Cannot assign 'expression' to an implicitly typed fixées par le mot clé required – a également pu être annon-
local). Pour faciliter le travail du compilateur ou modifier le cée comme faisant partie des nouveautés de C#. Or, il n’en
type de retour d’une expression lambda, celui-ci peut être est rien. Cette fonctionnalité est toujours envisagée, mais il
précisé. Pour se faire, il faut préfixer la fonction lambda par n’y a pas encore de spécifications abouties sur les usages de
le type de retour souhaité. cette fonctionnalité.
Une autre fonctionnalité évoquée, et finalement absente est
Func<int, float> incrementAsFloat = float (int x) => x + 1;
le mot clé field pour accéder au champ privé d’une propriété
Dans l’exemple précédent, la fonction lambda prend en sans avoir besoin de le déclarer :
paramètre un entier qu’elle additionne avec la valeur 1 qui
public Datetime Birthday { get; init => field = [Link](); }
est aussi un entier. Le résultat de la fonction lambda est donc
lui-même un entier. Mais comme le type de retour est précisé, C# 10 nous apporte un lot de nouveautés intéressantes. Il
le résultat du calcul est retourné après avoir été converti en continue en effet d’évoluer en intégrant de nouvelles fonc-
float. Il faut également noter que si la fonction lambda n’a tionnalités utiles comme l’extension des types d’enregistre-
qu’un seul paramètre, il est quand même obligatoire d’utili- ments aux structures tout en s’efforçant d’améliorer conti-
ser les parenthèses. nuellement sa syntaxe grâce notamment à la déclaration
Les attributs peuvent désormais être ajoutés aux fonctions d’espace de nom de portée de fichier. Cela s’intègre dans la
lambda et à leurs paramètres. Comme précédemment, l’uti- continuité des mises à jour .NET récentes qui diminuent la
lisation d’attributs imposera l’utilisation des parenthèses pour quantité de code nécessaire et améliorent sa lisibilité.
les paramètres. Sans cela, il y aurait une confusion pour D’autres nouveautés n’ont pas été présentées dans cet article
savoir si l’attribut s’applique sur la fonction ou sur un para- - comme l’amélioration de l’analyse de nullabilité et du déter-
mètre. L’exemple suivant illustre la création d’une fonction minisme des assignations, la directive #line, ou les améliora-
lambda avec l’attribut CustomDiagnostic appliqué au niveau tions de générations de code - car nous trouvions qu’elles
de la fonction, et l’attribut CustomFormatter appliqué au apportent trop peu de changements ou couvrent des cas
paramètre msg. d’utilisations trop spécifiques.
Microsoft continue donc d’enrichir son langage d’année en
var lambda = [CustomDiagnostic] ([CustomFormatter]string msg) => Console.
année et nous sommes enthousiastes à l’idée de pouvoir l’uti-
WriteLine(msg);
liser dans nos développements futurs.
20 [Link]
021_027.qxp_249 20/10/2021 08:54 Page21
[Link] 21
021_027.qxp_249 20/10/2021 08:54 Page22
<ErrorBoundary>
WebAssembly. Le code .NET étant interprété, cela signifie
<ChildContent>
qu’il s’exécute plus lentement que lors d’une exécution .NET
<MyComponent />
classique. La compilation .NET WebAssembly AOT corrige
</ChildContent>
ces problèmes de performance en compilant directement le
<ErrorContent>
code .NET en WebAssembly.
<p class="my-error">Voici une erreur personnalisée.</p>
Pour profiter de la compilation .NET WebAssembly AOT, il est
</ErrorContent>
nécessaire d’installer un outil de compilation additionnel dis-
</ErrorBoundary>
ponible en option dans le SDK .NET. Pour l’installer, il suffit
de taper dans une invite de commande : Il s’agit d’un moyen efficace pour gérer et personnaliser l’af-
fichage des erreurs au sein de Blazor.
dotnet workload install microsoft-net-sdk-blazorwebassembly-aot
22 [Link]
021_027.qxp_249 20/10/2021 08:54 Page23
Support du SVG Cela est utile pour notamment transférer des fichiers comme
Il est maintenant possible d’utiliser la syntaxe Razor, dont les détaillé dans la section suivante.
composants Blazor, dans un élément SVG foreignObject.
Téléversement plus rapide de fichiers
<svg width="200" height="200" xmlns="[Link]
plus lourds
<rect x="0" y="0" rx="10" ry="10" width="200" height="200" stroke="black"
En utilisant la nouvelle façon de diffuser des données entre
fill="none" />
JavaScript et .NET, il est désormais possible de téléverser des
<foreignObject x="20" y="20" width="160" height="160">
fichiers d’une taille supérieure à 2GB avec le composant InputFile.
<p>@message</p>
Le transfert est également plus rapide grâce à l’utilisation d’un flux
</foreignObject>
de byte[] directement sans utiliser un encodage Base64.
</svg>
[Link] 23
021_027.qxp_249 20/10/2021 08:54 Page24
Cela dira au navigateur que chaque fois qu’un évènement d’objets de météo, une liste de personnes, etc. Cependant, il
“coller” a lieu, il doit aussi déclencher un évènement custom- peut arriver que l’on souhaite définir un type plus précis pour
paste et passer les arguments définis. notre paramètre. Une contrainte sur un type d’objet permet
à la classe générique d’utiliser des paramètres ou méthodes
Génération automatique du type contraint.
de composant Blazor Avec .NET 6, il est maintenant possible d’ajouter une
Il est possible de générer un composant Blazor en créant le contrainte en utilisant la syntaxe C# classique :
tag dans une page puis faire un clic droit et générer le com-
@typeparam TEntity where TEntity : IEntity
posant.
Rendu de composant dynamique
.NET6 permet de créer des composants Blazor dynamiques
de la même manière que l’on utilise une variable de type
dynamique en .NET.
@code {
Type someType = ...
IDictionary<string, object> myDictionaryOfParameters = ...
}
24 [Link]
021_027.qxp_249 20/10/2021 08:54 Page25
• Réutilisation du développement web (code et compé- en standalone de manière native et nécessite Edge Canary et
tences) le SDK .NET Core 3.1. De même, il faut installer le template
• Accès à toutes les fonctionnalités natives de l’appareil grâce à une commande dotnet.
• Mélange de d’interface native et de web La solution se présente sous cette forme, on y retrouve bien
• Réduit le temps de développement des applications les différents projets selon la plateforme visée.
Le code .Net est exécuté directement dans l’application nati-
ve et exécute des composants Blazor localement. Le rendu
du DOM est envoyé dans un contrôleur de vue web. Tous les
évènements qui se déclenchent dans cette vue sont envoyés
dans le code .NET puis les modifications sont retournées
dans le contrôleur qui affiche la vue. Tout s’exécute dans
l’application.
.NET Multi-platform App UI (MAUI) est une évolution de
Xamarin Form, mais étendue à plus de plateformes (comme
le desktop par exemple).
Ces principales caractéristiques sont :
• Cross platform
• Utilisation des interfaces natives de chaque plateforme
• Un seul projet système, une seule base de code Cependant, on constate la présence d’un projet Blazor.
• Déploiement sur plusieurs appareils, mobiles et desktop Voyons ce que contient la page principale : Figure 6
• Disponibilité avec .NET 6 C’est bien un fichier Razor classique qui contient un counter !
C’est le principe des Blazor hybrid apps qui sont utilisées au Connaissant l’engouement et la montée en puissance de
sein de .NET MAUI. Blazor, ce projet reste donc à surveiller de près !
Le contrôleur BlazorWebView est intégré dans MAUI. On
retrouve donc : Micro FrontEnd avec Blazor
• Réutilisation des composants UI entre native et web Tout d’abord, définissons ensemble ce que nous appellerons
• Mix & match web and native UI le micro-frontend. Le micro-frontend est une architecture
• Accès direct aux fonctionnalités natives des appareils dans laquelle plusieurs composants coopèrent tout en étant
• Applications cross-platform mobile & desktop (à la sortie hautement indépendants. Il est facile de faire le parallèle
de .NET 6, le focus sera mis sur le desktop) avec l’architecture micro-services. Chacun des composants a
On peut donc utiliser des composants Blazor tels quels au sa propre logique, ils sont tous isolés les uns des autres et
sein d’une application .NET MAUI. Tous les composants déjà peuvent être développés par des équipes différentes.
développés dans le cadre d’autres projets peuvent donc faci- Pour que cette architecture fonctionne, il faut envisager une
lement être réutilisés. application principale, nommée la “App Shell” qui appellera
.NET MAUI possède le composant natif BlazorWebView qui tous les composants indépendants. Ainsi, pour récupérer les
permet de faire le rendu des composants Blazor. Cela repré- composants faisant partie de l’application, il faut utiliser ce
sente un vrai gain de productivité. que l’on nomme un compositeur. En .NET, il est par exemple
BlazorWebView est également disponible pour Winforms & possible d’utiliser la bibliothèque Piral.
WPF, ce qui permettra d’utiliser aussi les composants Blazor Dans notre exemple, nous utiliserons des composants Razor
avec ces technologies. sous forme de Razor Class Library (RCL) qui représenteront
les composantes du micro-frontend, l’app shell sera un projet
Mobile Blazor Bindings Blazor wasm, et le compositeur sera fait grâce aux dépen-
Vous connaissez certainement Blazor pour la création d’ap- dances internes de C#. Puis nous mettrons en place le lazy
plications web, avec toute la puissance qui lui incombe. Mais loading, afin de rendre l’expérience plus réaliste.
Blazor ne s’arrête pas là ; en effet, il existe un projet expéri-
mental dans le dépôt dotnet, nommé MobileBlazorBindings. Figure 6
Mais alors, késako ?
Il s’agit tout simplement de donner la possibilité aux dévelop-
peurs de créer des applications lourdes ou mobiles en utilisant
C# et .NET. Ça doit vous parler ça, C# et .NET pour des appli-
cations lourdes ou mobiles ? C’est normal puisque c’est ce que
fait Xamarin, et c’est là toute la subtilité : Mobile Blazor
Bindings se présente comme une abstraction de haut niveau
de Xamarin. Il utilise la syntaxe des pages Razor pour définir
les composants, autant sur le front, que sur le back-end.
On y retrouve ainsi tous les composants de [Link],
que l’on peut utiliser à sa guise, le rendu étant lui toujours
laissé à la charge de Xamarin.
En tant que projet expérimental, il n’est pas encore disponible
[Link] 25
021_027.qxp_249 20/10/2021 08:54 Page26
26 [Link]
021_027.qxp_249 20/10/2021 08:54 Page27
Le fait d’étendre la classe TestContext de bUnit nous donne essentiellement dans l’utilisation de bibliothèques JavaScript,
accès au contexte de test de bUnit. ou bien de frameworks CSS tels que jQuery, Bootstrap ou
Maintenant, voyons si l’on ajoute un peu de logique à un encore Materialize. Mais on peut en retrouver une certaine
composant. Pour cela nous utiliserons le composant Counter utilité pour l’utilisation plus poussée de framework JavaScript,
qui est automatiquement généré lors de la création d’un pro- comme React.
jet Blazor.
On constate que l’on a cliqué une fois sur le bouton et qu’en Conclusion
effet le composant affiche bien la bonne valeur. .NET 6 apporte son lot de nouveautés en améliorant et sim-
Bien entendu, vous pourrez aller bien plus loin dans vos tests, plifiant l’utilisation de Blazor. L’optimisation est toujours un
comme précisé plus haut dans l’article grâce à notre intro- fer de lance des nouvelles versions de .NET et nous consta-
duction sur le sujet. tons une nouvelle fois son efficacité tant par la réduction des
Vous pourrez par exemple donner une valeur précise à un données à télécharger que par l’amélioration du rendu et des
paramètre, injecter des services, mocker vos données ou vitesses de traitements. Ces nouveaux ajouts sont les bienve-
encore gérer les retours asynchrones. nus et raviront à la fois des utilisateurs finaux comme les
En bref, cette bibliothèque est très complète, et son utilisation développeurs. Si Blazor continue sur cette même lancée, il est
est sans appel bénéfique à nos développements ! à prévoir qu’il s’impose relativement vite au sein des techno-
logies web, et en premier lieu chez les développeurs .NET. Si
JSInterop demain vous voyez Blazor truster le haut du classement des
Une fonctionnalité de .NET (arrivée avec .NET Core 3.1) très technos web, vous ne pourrez pas dire qu’on ne vous
intéressante dans l’utilisation de Blazor est le JSInterop. aura pas prévenus ! Figure 8
JSinterop est le mécanisme permettant d’interagir entre C#
et JavaScript. En effet, il offre la possibilité d’appeler des
fonctions C# dans le JavaScript et vice-versa.
Attention toutefois à ne pas commettre deux erreurs impor-
tantes :
• Utiliser la balise <script> dans un composant Razor
(.razor), car cette balise ne peut pas être mise à jour dyna-
miquement par Blazor.
• Modifier un composant dont le rendu a été fait par Blazor
avec du JavaScript. Blazor garde en mémoire un arbre du
DOM de ce qu’il a rendu, et si ce dernier est modifié sans
passer par Blazor, cela peut poser des problèmes à l’usage.
En ce qui concerne l’isolation JavaScript, Blazor intègre le
standard des modules JavaScript. On retrouve donc nos
fichiers JavaScript dans la partie wwwroot du projet Blazor.
L’intérêt de cette fonctionnalité au sein de Blazor se retrouve
Figure 9
La recherche de performance peut s’acquérir de deux ma- En .Net 6, de nombreuses améliorations ont été ajoutées par
nières. D’abord, il y a la recherche de la performance pure la communauté. L’une d’elles porte sur la gestion des mé-
du code. On essaye de trouver des raisonnements et une lo- thodes dites inline. L’Inlinning est le processus d’optimisation
gique différente permettant d’augmenter significativement d’un compilateur permettant de remplacer l’appel d’une
les traitements. Puis, il y a les avancées du langage et les per- fonction par son code. Bien que par définition, une méthode
formances qu'elles peuvent apporter. Ce que j’apprécie avec inline augmente la taille du programme, son principal avan-
cette version de .Net 6, c’est que les développeurs ont utilisé tage est d’offrir la possibilité d’améliorations qui ne peuvent
tantôt l’une et tantôt l’autre, et parfois même les deux. être accessibles par l’appel de la fonction (code mort, optimi-
sation d’invariant, élimination de variables d’induction, etc.).
Les tests de performance avec Benchmark .Net Pour certaines raisons, l’inlinning est à double tranchant côté
Avant toute chose, l’utilisation d’une librairie de benchmark performance : utilisé à mauvais escient, il peut réduire drasti-
est nécessaire. Benchmark .Net est une librairie destinée aux quement les performances. Toutefois, utilisé correctement, il
benchmarks et à la mesure de performance. Sa modularité lui est peut-être extrêmement puissant.
octroie la possibilité de mesurer différents frameworks à la fois Finalement, c’est un ensemble d’améliorations qui ont été ef-
du .Net Core ou du Framework. Ainsi c’est le partenaire idéal fectuées sur la partie JIT afin de mieux comprendre le code
pour tester les gains de performance entre .Net 5 et .Net 6, et faisant appel à la fonction inline. Pour tester les gains de per-
c’est ce que nous utiliserons tout au long de l’article. formance, prenons l’exemple de la classe Utf8Formatter. Si
nous regardons plus en détail le code, voici la signature de la
JIT méthode TryFormatInt64 : Figure 1
Le code source écrit en C# est compilé dans un langage in- Nous pouvons observer que la méthode est taguée pour être
termédiaire (il) conforme à la spécification CLI. Le code de en mode inline. En effectuant un test avec Benchmark .NET,
langage intermédiaire et les ressources, telles que les bit- nous remarquons d’après le ratio que les performances ont
maps et les chaînes, sont stockés dans une assembly, en plus que doublé par rapport à .Net Core 3 .1, et que la diffé-
général, avec une extension .dll. rence est aussi significative avec .NET 5.0. Figure 2
Lorsque le programme C# est exécuté, l’assembly est chargé La communauté a travaillé sur des améliorations concernant
dans le CLR. Le CLR effectue une compilation just-in-time la dévirtualisation, le PGO Dynamique ou encore la vérifica-
(JIT) pour convertir le code de langage intermédiaire en ins- tion des limites.
tructions machine natives. C’est donc naturellement que si
l’on veut augmenter les performances du runtime .Net, la Crossgen et AOT
gestion de la partie Jit est un bon candidat. Crossgen est arrivé très tôt dans le runtime de .Net
Framework. Au fur et à mesure de l’avancée du framework
Figure 1 .Net Core, le besoin s’est fait sentir de réécrire l’outil.
Crossgen est un outil permettant la compilation AOT (ahead-
of-time). Son but est de réduire les besoins de la compilation
JIT au moment de l’exécution. La compilation AOT s’ap-
plique au moment de publication de l’application, Crossgen
applique alors une pré-compilation JIT sur l’ensemble des dll
et stocke le code ainsi créé dans une nouvelle section pou-
vant être récupérée rapidement par le runtime.
Crossgen 2 a été complètement réécrit avec une nouvelle ar-
chitecture pour coller aux usages de .Net 6. En effet, ceux-ci
Figure 2 diffèrent totalement suivant les besoins. Il peut être utilisé sur
28 [Link]
028_031.qxp_249 20/10/2021 08:52 Page29
System Types
System Types étant utilisé à tout moment et dans chaque ap- Figure 3
plication .NET, on ne peut augmenter les performances de
.Net sans passer par l’optimisation des types de la librairie
« System ». Des modifications ont été apportées à certains
types moins habituels comme le type version, et d’autres re-
touches concernent des types plus « couramment utilisés »,
comme Random qui a été totalement refondu. L’histoire est
simple, l’algorithme utilisé jusqu’à .Net 6 était le même de-
puis 20 ans. Il a donc été réécrit pour améliorer les Figure 4
performances de la génération d’une chaîne pseudo-aléatoi-
re sans empiéter sur la qualité dont nous avons besoin en
tant que développeur. Je pense que cette refonte de l’algo-
rithme devrait sûrement avoir un article à lui tout seul ; c’est
pour cela que j’ai préféré me pencher sur un autre type que
l’on utilise tous les jours et qui au cœur des applications
.NET : le Guid. Ce type est présent pour fournir un identifiant Figure 5
unique et universel dans une application.
Étant donné son caractère unique et universel, l’utilisation est
répandue dans les applications et l’une de ses méthodes les
plus utilisées reste le « parsing » (utilisé dans la désérialisation
de Json par exemple). En .Net il existe plusieurs formats de
Guid, avec ou sans parenthèse, accolade, séparée par un Figure 6
hypen. L’algorithme a été simplifié et l’utilisation de méthode
inline permet de mieux gérer les cas d’extraction d’un Guid [Link]. D’abord optimisé pour les
d’une chaîne de caractères. byte[], une deuxième pull request est venue compléter le
Si on lance un test en comparant à partir de la version .Net code pour n’importe quel type. Au lieu de comparer les sé-
Framework 4.8, voici ce que nous obtenons : Figure 3 quences directement, celle-ci se base sur la « value type »
Un bond en avant a été fait entre .Net Framework 4.8 et .Net [Link]<T>. Ainsi le traitement est délégué à la mé-
Core 3.1, puis de petites avancées jusqu’à .Net 6. Toutefois, thode span ajoutant une vectorisation de la comparaison
quand on pense que le parsing de string en Guid est inévi- sans utiliser plus de ressource. Le seul inconvénient est que le
table dans la réception de Json par une API, nous nous type T doit s’y prêter correctement :
rendons compte qu’après 1 million de parsing la maigre dif- Code complet sur [Link] & github
férence entre .Net 5 et 6 devient bien plus importante :
Figure 4 Le résultat est bluffant et nous rappelle le bienfait de la librai-
rie [Link] sur les performances lors des traitements :
String, Collections et Linq Figure 6
Les tableaux, les collections et Linq ont une place importante Les autres améliorations concernent la partie Distinct, Min,
au cœur de .Net. La communauté a donc travaillé à réduire Max ainsi que l’ajout de nouvelles API telles que
l’impact des traitements sur les performances du code. [Link] (acceptation de 3 sources au lieu de 2).
Prenons le cas d’une copie d’un dictionnaire. Étant utilisé à Enfin, intéressons-nous au String. Une optimisation intéres-
longueur de temps, rien de plus banal que de cloner un dic- sante a été réalisée au niveau de la méthode
tionnaire pour effectuer certains traitements. Si le « [Link](String, String) ». Certes, ce n’est pas la mé-
dictionnaire source et le dictionnaire de destination partagent thode en elle-même qui a été optimisée, mais plutôt le
le même comparateur de clé, l’astuce a consisté à copier les traitement des différents cas de Distinct. En effet, trois cas
objets sans les hacher par la suite : ont tiré leur épingle du jeu afin d’être optimisés dans cette
Code complet sur [Link] & github version de .Net 6 :
Le cas le plus évident est lorsque nous souhaitons remplacer
En vérifiant par un test, nous remarquons un gain de perfor- un caractère par un autre, souvent des caractères spéciaux
mance depuis .Net Framework 4.8 : Figure 5 comme “\n”. Ainsi la rapidité d’un [Link]("\n", " ") est ac-
Sur les collections, d’autres changements ont été effectués crue pour la simple et bonne raison qu’elle fait directement
pour améliorer un peu plus les performances. appel à la méthode [Link](char, char) :
Côté Linq, une amélioration m’a interpellé concernant les Code complet sur [Link] & github
tests d’équivalence entre deux énumérables avec Le deuxième cas réside lors du remplacement d’un caractère
[Link] 29
028_031.qxp_249 20/10/2021 08:52 Page30
unique par une valeur (unique ou non). Dans ce cas, c’est d’ailleurs une problématique connue depuis de nombreuses
indexOf(char) qui est utilisée : années.
Code complet sur [Link] & github Les modifications apportées dans .Net 6 contourne ce pro-
Le dernier cas survient lors du remplacement de plusieurs ca- blème en suivant l’offset plutôt en mémoire quand cela est
ractères avec l’utilisation d’un équivalent de IndexOf(string, possible ce qui accélère le traitement. Par la même occasion,
[Link]). les appels système ne sont effectués que lorsqu’ils sont expli-
Si nous testons les performances de chaque cas, nous retrou- citement requis.
vons une augmentation de performance significative dans le Voici les résultats de ce long travail : Figure 8
cas 1. L’augmentation reste tout de même honorable dans le D’autres améliorations de performance ont été introduites
cas 2 et 3 : Figure 7 dans cette réécriture, nous avons par exemple un travail sur
Les autres modifications de la librairie String ont porté sur la fonction de Lenght, avec pour elle aussi un accès en mé-
l’API [Link] avec là aussi l’introduction de la fonctionna- moire tant qu’il n’y a pas d’accès Write sur le fichier. Les
lité « ReadOnlySpan<string ?> », mais également parties Aync file IO, comme nous l’avons vu, ont bénéficié
[Link] avec des changements surtout issues de C#10 des avancées de FileStream, mais également d’autres amé-
et la string interpolation. liorations comme une meilleure gestion des allocations
mémoire et de la libération.
IO
La librairie FileStream est l’une des plus vieilles librairies de EF core 6.0 sur le banc des gains de
.Net. Elle a donc connu de nombreuses modifications année performance
après année et était donc l’une des candidates les plus im- C’est au tour de EF Core 6.0 de passer sur le banc des gains
portantes à une mise à jour, sachant que l’accès au fichier est de performance. Comme évoqué, l’ambition était grande
utilisé dans d'innombrables scénarios. Pour cette version pour cette nouvelle version, à savoir rattraper le « micro
.NET 6 la librairie FileStream a été complètement réécrite ORM » Dapper dans les scores de performance Tech
pour d’une part séparer les fonctionnalités en donnant plus Empower Fortunes. Les équipes d’EF Core ont annoncé être
de visibilité au code, et d’autre part faciliter les changements passées de 55% à environ 5% d’écart de performance. Si on
affectant la performance. considère les différences de feature entre Dapper et EF Core,
Deux méthodes ont été principalement revues pour offrir des c’est une avancée remarquable. Toutefois, il faut bien garder
gains de performance : [Link] et FileStream. à l’esprit que les tests de performance concernent un scéna-
Position. Le constat de départ, fait par la communauté, porte rio particulier utilisant du « no-tracking » et sans update. Il est
sur l’observation que les méthodes [Link] et donc possible que dans nos applications de production les
[Link] synchronisent l’offset du ficher de ma- performances soient différentes. Regardons, néanmoins en-
nière récurrente à chaque opération asynchrone. C’est semble, les résultats et quelques modifications qui ont été
apportées à EF Core.
30 [Link]
028_031.qxp_249 20/10/2021 08:52 Page31
des optimisations soient pris en compte. Figure 9 listner » ou la journalisation activé(e), le code vérifie toujours
Je vous invite donc à guetter les annonces du prochain round à chaque instant l’activation ou non de ces derniers.
pour vérifier les gains. Afin d’offrir de meilleure performance et garder toujours cette
grande flexibilité, l’astuce a été de vérifier si la journalisation
Regroupement et recyclage des DbContext ou l’interception n’était pas activé et dans ce cas supprimer la
Arrivé avec EF Core 2.0, le regroupement de contextes offre journalisation pendant 1 seconde. Par conséquent, l’activa-
au développeur la possibilité de réutiliser un DbContext en le tion de la journalisation peut prendre jusqu’à 1 seconde, là où
réinitialisant plutôt que de le supprimer purement et simple- elle était instantanée. L’équipe a calculé que ce processus per-
ment. Dans cette gestion, un pool de contexte trop grand ou mettait d’améliorer le débit de référence de 7%.
illimité aurait tendance à créer des objets DbContext au fur et
à mesure des besoins, sans jamais les supprimer. La consé- Désactivation des contrôles
quence serait une gestion des ressources catastrophique. de sécurité des threads
Ainsi, jusqu’à la version EF Core 6.0, le pool par défaut était En espérant ne rien vous apprendre, EF Core n’est pas
de 128, ce qui est déjà un nombre important. Pour les be- « thread safe ». Une des raisons est qu’il encapsule une
soins du benchmark Tech Empower, la valeur a été mise par connexion à une base de données qui n’autorise presque ja-
défaut à 1024. D’après les tests réalisés par l’équipe EF Core, mais l’utilisation simultanée. Étant donné que les accès
les performances seraient augmentées de 23%. concurrents sont surtout dus à un problème de développe-
Il faut toutefois relativiser cette amélioration, d’une part car il ment, EF Core inclut un mécanisme de sécurité interne
était déjà possible de spécifier une valeur pour la taille du essayant de détecter tant bien que mal (en mode best effort)
pool, et d’autre part, car les scénarios qui auraient besoin les accès concurrents et lève une exception informative.
d’autant de Dbcontext sont limités. Il s’est avéré que ce mécanisme n’est pas aussi performant que
Une autre amélioration touche quant à elle la manière dont EF l’équipe le souhaite notamment lors des requêtes asynchrones.
Core interagit avec les objets [Link] (par exemple La problématique réside dans le fait qu’il n’y a pas réellement
DbConnection, DBCommand, DBDatareader, etc.). De base, le d’amélioration à apporter à ce mécanisme à part le désactiver
profilage de la mémoire a révélé un nombre élevé d’instances totalement. Étant donné les erreurs que cela provoquera,
de ces objets. L’une des améliorations a donc consisté à réécrire l’équipe a donc opté pour un indicateur de désactivation of-
les interactions pour que chaque DbContext dispose de ses frant à ceux qui le souhaitent la possibilité d’augmenter les
propres instances dédiées qu’il réutilisera à chaque fois. performances (d’environ 7% d’après les tests) s’ils sont
convaincus qu’aucun bug de concurrence n’existe.
Suppression de la journalisation
Les logs peuvent être importants pour comprendre d’éven- Que retenir ?
tuelles problématiques sur nos applications. Dans EF Core, il Que ce soit pour .Net 6 ou EF Core 6, les performances ont
est possible de voir les instructions SQL avant leur exécution été au centre des actions de la communauté, et je dois dire
ainsi que leur temps d’exécution. L’utilisateur a la possibilité que j’ai été séduit par celles-ci. Même si pour EfCore 6 cer-
de s’appuyer sur les événements grâce à classe taines améliorations sont difficilement transposables pour
DiagnosticSource et la gestion des instructions avec un inter- une application en production, les gains de performance sont
cepteur. Bien que cela puisse être puissant, des pertes de appréciables. Personnellement, j’ai hâte de pouvoir migrer
performance peuvent apparaître, car une fois le « diagnostic mes applications et voir les gains que je peux tirer de .Net 6 !
[Link]/francoistonicD
[Link] 31
032_034.qxp_249 18/10/2021 16:11 Page32
32 [Link]
032_034.qxp_249 18/10/2021 16:11 Page33
[Link] 33
032_034.qxp_249 18/10/2021 16:11 Page34
De son côté, IntelliCode, la version assistée par l’IA patibilité Windows on Windows64 (WoW64). Rassurez-vous,
d’IntelliSense, s’est déjà démarquée depuis son intégration même si l’EDI fonctionne désormais sur 64-bits, vos applica-
dans les précédentes éditions de Visual Studio par sa capaci- tions resteront, malgré tout, compilables pour des plate-
té à prédire l’intention des développeurs à travers son systè- formes 32-bits.
me d’auto-complétion. Pour la version 2022 de l’EDI made Microsoft a annoncé des temps de chargement 2,5 fois plus
in Microsoft, IntelliCode s’est encore amélioré afin de propo- rapides. Pour valider le gain annoncé bien que la solution uti-
ser, à partir d’un simple mot-clé, de construire le reste de la lisée par l’éditeur pour sa propre démonstration ne soit pas
ligne de code. A terme, l’objectif pour IntelliCode est de sug- disponible, nous avons réalisé quelques essais avec le code
gérer, à l’instar de GitHub Copilot, des implémentations source du framework aspnetcore disponible sur GitHub.
complètes. La finalité pour Microsoft est multiple : proposer Composé de 518 projets pour un poids total de 2Go, l’am-
une expérience dans laquelle moins de temps est perdu sur pleur de la solution devrait nous permettre de mettre en
des particularité syntaxiques propres à chaque langage de exergue le gain potentiel. Pour cela, nous avons procédé
programmation au profit de la conception générale de vos ainsi : sur une même machine, nous avons tout d’abord
algorithmes, et guider les profils les moins expérimentés en lancé la dernière version en date de Visual Studio 2019, puis
leur proposant des extraits de code “propre” pour in fine éra- chronométré plusieurs actions comme le chargement com-
diquer les code smells. Figure 7 plet de la solution et son déchargement. Après un redémar-
Si toutefois IntelliCode ne suffit pas et que vous cherchez à rage complet, nous avons répété la séquence cette fois-ci
solliciter l’aide de quelqu’un, vous aurez sans doute recours avec VS 2022 (preview 3.1 au moment du test).
à Live Share. Cet outil, intégré à Visual Studio, sert à créer Quelques précisions sur l’environnement de test : il s’agit
des sessions de développement collaboratif en temps réel. d’un ordinateur exécutant Windows 10 Pro en 64 bits qui est
Avec Visual Studio 2022, Live Share se dote de nouvelles équipé de 16Go de RAM dont au moins 10 de libres, et les
fonctionnalités parmi lesquelles un chat textuel afin d’échan- différentes versions de Visual Studio ainsi que la solution ont
ger au sein de la session et éviter d’avoir à changer de été installées sur un disque dur type NVMe pour limiter le
contexte en basculant sur une application tierce, ou encore risque de goulot d’étranglement.
la possibilité de mettre en place des sessions récurrentes
grâce à la génération de liens réutilisables. Opération Visual Studio
Enfin, les organisations pourront mettre en place leur 2019 2022 (pre 3.1)
Chargement 67s 30s
propre politique de sécurité sur les sessions de partage,
Déchargement 18s 7s
notamment pour bloquer le partage du terminal avec accès
Compilation* 5.450s 5.650s*
d’écriture sur des machines dites sensibles.
*fera office de témoin, car géré par le SDK et non pas par VS
Devenv passe au 64-bits A l’issue de ces quelques tests, nous validons d’ores et déjà
Visual Studio 2022 fait un bond en avant, puisque son pro- la démonstration de Microsoft puisque lors du chargement
cessus principal ([Link]) devient un processus 64-bits. de la solution, le processus [Link] a volontiers utilisé plus
Ainsi, il pourra exploiter plus de 4Go de RAM, et éviter les de 5.1Go de RAM, prouvant l’intérêt pour Visual Studio de
erreurs Out-Of-Memory lors du chargement de solutions passer au 64-bits. De plus, certains chargements ont effec-
volumineuses. tivement été réduits drastiquement avec Visual Studio 2022
De plus, les performances devraient s’accroître pour les ordi- (~2.3x plus rapide pour le chargement, ~2.5x plus rapide au
nateurs possédant une architecture 64-bits puisque VS 2022 déchargement). Figure 8
n’aura plus besoin d’être exécuté à travers la couche de com-
Côté pomme
Enfin, les utilisateurs de Visual Studio for Mac ne sont pas en
reste, puisque Microsoft a réservé plusieurs nouveautés à
cette déclinaison. La nouveauté majeure est la migration
d’une bonne partie du code de l’interface de Visual Studio
for Mac sur du code natif. Ce changement permettra notam-
ment de prendre en compte les options d’accessibilité sélec-
tionnées au niveau du système d’exploitation, et globalement
d’améliorer les performances et la stabilité de l’EDI. L’autre
priorité est de réduire le delta entre l’expérience Visual Studio
sur Mac par rapport à Windows. En ce sens, la terminologie
et certains menus ont déjà été unifiés, et l’intégration de Git
Figure 7 serait à terme entièrement disponible dans Visual Studio for
Mac. Dans cette preview, nous pouvons d’ores et déjà utiliser
la fenêtre « Git Changes » pour inclure / exclure des modifi-
cations avant de commit, et d’autres fonctionnalités
devraient apparaître au fil des versions.
Figure 8
34 [Link]
035_036.qxp_249 18/10/2021 16:12 Page35
JEP 406: Pattern Matching for switch (Preview) Le grand intérêt est que cela évite de devoir faire un test défen-
C’est sans doute la plus importante nouveauté de cette relea- sif avant le switch, et permet d’inclure dans celui-ci la valeur
se, le pattern matching arrive dans les switchs, en preview. nulle comme toute autre valeur possible de notre variable. Et
On peut désormais faire un switch sur le type d’une variable ça ne s’arrête pas là, le switch a été enrichi de guards qui per-
(y compris enum, record et tableau), et en extraire une mettent d’inclure une condition au case. L’exemple suivant issu
variable locale au case qui sera du type correspondant. La de la JEP montre son utilisation. En ajoutant un guard au case
JEP donne l’exemple suivant avec une chaîne de if/else : Triangle : case Triangle t && ([Link]() > 100), on peut créer deux
cases : un pour les grands triangles, et un autre pour les petits.
static String formatter(Object o) {
String formatted = "unknown"; static void testTriangle(Shape s) {
if (o instanceof Integer i) { switch (s) {
formatted = [Link]("int %d", i); case Triangle t && ([Link]() > 100) -> {
} else if (o instanceof Long l) { [Link]("Large triangle");
formatted = [Link]("long %d", l); }
} else if (o instanceof Double d) { case Triangle t -> [Link]("Small triangle");
formatted = [Link]("double %f", d); default -> [Link]("Non-triangle");
} else if (o instanceof String s) { }
formatted = [Link]("String %s", s); }
}
Plus d’informations dans la JEP-406 : [Link]
return formatted;
}
JEP 356: Enhanced Pseudo-Random
Qui peut maintenant être ré-écrit avec une switch expression : Number Generators
La JEP-356 fournit une nouvelle interface RandomGenerator, et une
static String formatterPatternSwitch(Object o) {
factory RandomGeneratorFactory, qui permettent d’accéder à une implé-
return switch (o) {
mentation de générateur de nombre aléatoire. Les générateurs
case Integer i -> [Link]("int %d", i);
existant : Random, SecureRandom, SplittableRandom et ThreadLocalRandom;
case Long l -> [Link]("long %d", l);
implémentent maintenant cette interface qui ajoute, entre autre,
case Double d -> [Link]("double %f", d);
l’accès à un stream de nombre aléatoire (RandomGenerator::doubles(),
case String s -> [Link]("String %s", s);
RandomGenerator::ints(), …). De nouveaux algorithmes de génération
default -> [Link]();
de nombre aléatoires ont été implémentés, plus sécurisés et plus
};
performant (mais ils ne sont plus thread-safe), ils ont vocation à
}
remplacer les anciens. Voici quelques exemples d’instanciation de
En plus de supporter du pattern matching, le switch permet générateur de nombres aléatoires :
maintenant de définir un case spécial null (dans ses deux
// the old Random generator
formes : statement ou expression). Auparavant, une variable
RandomGenerator rng1 = [Link]("Random").create(42);
de switch nulle entraînait une NullPointerException. Dans sa nou-
// the default random generator, currently L32X64MixRandom
velle forme, on peut ajouter un case null pour gérer les nulls au
// but this can change
sein du switch. Sans case null, l’ancien comportement est
RandomGenerator rng2 = [Link]().create(42);
gardé, et une NullPointerException sera levée.
// shortcut for the default
Si on reprend l’exemple précédent, cela nous donne :
RandomGenerator rng3 = [Link]();
static String formatterPatternSwitch(Object o) { // stream all available generators and display their names
return switch (o) { [Link]().forEach(generator ->
case null -> "null"; [Link]([Link]());
case Integer i -> [Link]("int %d", i);
Plus d’informations dans la JEP-356 : [Link]
case Long l -> [Link]("long %d", l);
case Double d -> [Link]("double %f", d);
Les fonctionnalités qui passent de
case String s -> [Link]("String %s", s);
preview à standard
default -> [Link]();
Dans Java 17, une seule fonctionnalité passe de preview à
};
standard : les Sealed Classes qui permettent de limiter le
}
[Link] 35
035_036.qxp_249 18/10/2021 16:12 Page36
nombre d’implémentations d’une classe ou d’une interface à [Link](), et en test via [Link](LocaDateTime.
une liste prédéfinie. La hiérarchie de cette classe/interface est of(//the hardcoded date time)).
donc close (scellée / sealed). Celles-ci existent depuis Java 15.
Plus d’informations sur la JEP-409 : [Link] Divers
Divers ajouts au JDK :
Les fonctionnalités qui restent en preview • [Link]([Link]) : permet de créer une copie d’une
Les fonctionnalités suivantes restent en preview (ou en incu- entrée de Map qui ne soit pas connectée à la Map existante.
bator module) : • [Link](), [Link](), [Link]() : per-
• Vector API : seconde incubation de la fonctionnalité. Vector met d’accéder aux entrée/sortie standard et sortie erreur
API est une nouvelle API qui permet d’exprimer des calculs d’un process via un Reader ou un Writer.
de vecteur (calcul matriciel entre autres), qui seront exécutés
via des instructions machines optimales en fonction de la pla- Dépréciation et encapsulation
teforme d’exécution. Java 17 voit la dépréciation pour suppression de l’API des
Plus d’informations dans la JEP-414 : [Link] Applets via la JEP-398, celle-ci n’étant plus utilisée depuis de
• Foreign Function & Memory API : nouvel incubator pour nombreuses années, elle n’a pas fait couler beaucoup d’encre.
ces deux fonctionnalités qui sont maintenant liées (l’une uti- Java 17 voit aussi la dépréciation du Security Manager pour
lisation l’autre) au sein d’un même incubator. suppression via la JEP-411. Il y a eu beaucoup de débat
• Foreign Memory API permet de gérer des segments autour de cette annonce, avec, il faut l’avouer un peu de
mémoire (on heap ou off heap) tandis que Foreign Function drama, comme quand Apache Netbeans a annoncé qu’il ne
l’interconnexion de la JVM avec du code natif (en C par pourrait pas supporter Java 17 alors qu’il fallait juste changer
exemple) de façon facile et performante. Ces deux API sont quelques lignes de code…
les bases du projet Panama. La suppression du Security Manager a été annoncée, car
Plus d’informations dans la JEP-412 [Link] celui-ci est complexe à maintenir, coûteux en termes de per-
formance, et n’apporte pas la sécurité nécessaire face aux
Un nouveau port de la JVM enjeux actuels. Il a été créé pour sécuriser les applets qui, par
Java 17 ajoute le support de l’architecture macOS/AArch64 définition, exécutent du code untrusted, et n’a donc plus de
(aka Apple Silicon). Plus d’informations dans la JEP-412 sens dans une JVM qui ne contiendrait plus l’API Applet.
[Link] Pour finir, la JEP 403: Strongly Encapsulate JDK Internals a
basculé le JDK vers une encapsulation stricte de ses classes
HexFormat internes. C’est la fin de ce qui avait été commencé avec Java
La class [Link] permet la conversion de type primitif, 9 et la modularisation du JDK.
tableau de byte, ou tableau de char en chaîne de caractère Concrètement, le mode d’encapsulation était passé de --ille-
hexadécimal et vice versa. gal-access=permit en Java 15 à --illegal-access=deny en Java 16 avec
la possibilité de changer l’option de configuration. Avec Java
[Link]().toHexDigits(127); // "7f"
17, --illegal-access disparaît et l’accès aux classes internes du
[Link]().fromHexDigits("7f"); // 127
JDK (hors Unsafe) n’est plus possible.
InstantSource
Tester du code contenant de la manipulation de date a tou- Conclusion
jours été un challenge, surtout si celui-ci use et abuse de Même si cette version de Java ne comporte pas beaucoup de
[Link](), [Link](), et autre initialisation de JEP, elle propose un grand nombre de nouveautés, et semble
date avec la date en cours. paver le chemin pour l’aboutissement du projet Panama dans
Pour faciliter la testabilité de ce genre de code, une nouvelle une future version.
interface a été ajoutée au JDK : InstantSource, avec une seule En dépréciant les Applets et le Security Manager, le JDK va
implémentation : Clock. Le but de l’interface InstantSource est aussi se séparer de pas mal de code historique plus utilisé, ou
d’être une fabrique d’Instant. Au lieu de créer un Instant avec la plus adapté au monde actuel. Même si cela induit quelques
date du jour, vous le créez depuis l’InstantSource. Un test pou- petits désagréments évidents, et complexifie le support de
vant alors utiliser une InstantSource à une date fixe au lieu de la cette version dans de nombreux framework, c’est un mal
date du jour. Imaginez le code suivant : pour un bien. Cela permettra de faciliter la maintenance du
JDK, et aux équipes de celui-ci à se focaliser sur l’apport de
public class MyBean {
nouveautés, le Security Manager étant une source d’impor-
private InstantSource source; // dependency inject
tante complexité au sein du JDK.
...
Pour finir, le statut Long Term Support (LTS) de cette release
public void process(Instant endInstant) {
va en faire la release de choix pour de nombreux dévelop-
if ([Link]().isAfter(endInstant) {
peurs, et il faut s’attendre à ce que beaucoup de dévelop-
...
peurs migrent directement de Java 11 à Java 17 (plus que de
}
migration de Java 16 à Java 17). De plus, l’annonce faite à
}
Spring One de baser sur Java 17 minimum les futurs Spring
}
Framework 6 et Spring Boot 3 (GA fin 2022) va définitive-
En fonctionnement normal, l’InstanSource est initialisé via ment entériner Java 17 comme version à privilégier en 2022.
36 [Link]
037_039.qxp_249 20/10/2021 08:49 Page37
Le développeur
va sauver la Terre !
PARTIE 1 : POSONS LES FONDAMENTAUX
Le développeur est le Chuck Norris de l’éco- pourrions plusieurs Mo en optimisant les ressources et
conception. Sans lui, on ne peut pas réduire la taille le poids de chaque page. Il faut revenir à un principe
des applications, réduire l’usage des ressources IT ou simple : 1 Ko est un 1 Ko utile. Dans les années 1980,
encore améliorer l’utilisation réseau. On peut chaque Ko était une ressource précieuse qu’il fallait
appliquer l’éco-responsabilité dans les apps mobiles, utiliser avec rigueur. Nous avons perdu cette obsession
le cloud, le desktop. Le développeur peut cibler le des contraintes matérielles. Comme le sujet est
code, les piles techniques, tous les assets techniques énorme, nous avons décidé de publier un dossier sur
et graphiques. Rien que sur les sites web, nous deux numéros. La partie 2 sera publié dans le n°251.
Figure 1
Dans un software, travailler l’obésité du code permet de ga- intelligent destiné à l’industrie, ces gains sont directement liés
gner « facilement » 30% de performance, qui se traduisent au coût total de possession du capteur, car la maintenance
souvent par des temps de chargement plus courts, et donc destinée à remplacer la batterie est souvent coûteuse.
une expérience utilisateur améliorée. Lorsqu’enfin on passe à l’échelle d’un système complexe as-
Si ce logiciel est embarqué sur un smartphone, ces 30% sont semblant de multiples logiciels & intelligences, le coût
autant d’autonomie, et donc de temps d’utilisation de l’appli- énergétique de la performance devient crucial. Ainsi qui vou-
cation avant une recharge sur le secteur, ou avant un drait d’une voiture électrique et autonome dont la batterie ne
changement de batterie. Lors de la conception d’un capteur permettrait pas de parcourir plus de 13 km entre 2 bornes ?
Figure 1
[Link] 37
037_039.qxp_249 20/10/2021 08:49 Page38
Nous découpons nos travaux en trois projets, d’ampleur et de nome, ou encore l’avion de chasse. Dans ces systèmes, les
complexité croissante : intelligences sont en réseaux communicants, et la perfor-
• Le projet Green Code porte sur le code, notamment dans mance du système global repose non seulement sur les
les usages mobiles & web, et a pour objectifs d’en maîtriser performances des sous-systèmes, mais aussi sur l’intelli-
les outils de mesure de l’impact environnemental et de la gence de la communication et les choix d’architectures
consommation énergétique, ainsi que les leviers d’actions. (distribuée, centralisée, hétérarchique). L’enjeu « Green »
Ici le TRL est élevé, des outils existent, et notre effort se devient alors un enjeu de performances de systèmes intelli-
situe dans l’agrégation de ces outils et l’analyse des résul- gents. Au travers de ce projet, notre objectif est de
tats pour en établir des leviers d’amélioration. s’inspirer de la consommation énergétique du cerveau hu-
• Le projet Green Smart Objects s’intéresse aux objets intelli- main à 40 watts. En comparaison, le système d'intelligence
gents, objets connectés et autres IOT & IIOT, dans lequel artificielle Watson d'IBM qui a remporté le jeu « Jeopardy! »
on doit mesurer et maîtriser à la fois la consommation du en 2011 avait besoin de 80.000 watts et AlphaGo de
code, celle du hardware, et celle de la communication DeepMind qui a battu le meilleur joueur de go en 2016 fai-
(wifi, Bluetooth, Lora,…). Dans ce projet, notre démarche sait à peine mieux avec 20.000 watts. Nous nous
de mesure de la performance « green » des objets intelli- attaquons ainsi à l’efficacité énergétique d’une intelligence
gents et communicants devra nous permettre d’aller vers artificielle en inventant une nouvelle échelle de mesure de
une modélisation green MBSE de ces systèmes en phase de l’efficacité d’une IA pour permettre de mieux les comparer
conception. et espérer ainsi s’approcher de l’efficacité énergétique de
• Enfin, le projet Green Smart System passe à l’échelle des l’intelligence humaine…
systèmes complexes tels l’usine du futur, le véhicule auto-
Qu’est-ce que la
Pierre LAGARDE
« Green Software Foundation » ?
Principal Program La « Green Software Foundation » est une organisation à but non lucratif créée en 2021
Manager
Windows & Devices - basée sur la Linux Foundation. Elle est née d'un désir et d'un besoin mutuel de collabo-
Microsoft Corp ration au sein des équipes de développement. Elle regroupe des organisations qui par-
tagent un engagement envers le développement « green » autour de principes qui ont
pris naissance il y a un an ici : [Link]
38 [Link]
037_039.qxp_249 20/10/2021 08:49 Page39
LES PRINCIPES
DE DÉVELOPPEMENT
« GREEN »
Écrire des principes de développement « green » est un exer-
cice qui nécessite des connaissances scientifiques, sur le
climat, sur la production électrique, le matériel électronique
et bien sûr les principes de fonctionnement des data centers Figure A
qui sont avec l’Intelligence Artificielle au centre des débats du
développement « Green », car ils représentent une large par-
tie des émissions de carbone pour le secteur du numérique.
Pour prendre ces 2 exemples, les premiers datacenters ont
été conçus pour répondre à une forte demande en termes de
ressources et de haute disponibilité, quant aux architectures
d’IA, elles étaient basées sur de très grandes quantités de
données et des algorithmes extrêmement gourmands en
puissance de calcul. Dans l’élaboration de ces architectures,
l’environnement n’entrait pas dans l’équation. Le défi qu’il
faut relever maintenant c’est de redéfinir ces architectures lo-
gicielles en prenant en compte les différents aspects
environnementaux liés au domaine du numérique. Figure B
C’est pourquoi une initiative lancée par Asim Hussain a vu le
jour pour mettre par écrit ces « grands » principes de dévelop-
pement qui permettront de définir ce qu’est une application Valoriser l’impact CO2 global : en créant des applica-
ou un service numérique « green ». Aussi bien au niveau du tions qui sont capables de s’exécuter sur du matériel
code source, mais aussi pendant l’exécution des applications plus ancien pour allonger la durée de vie du matériel,
et des services, et ce, indépendamment du domaine d’appli- ordinateurs, tablettes, téléphones ou serveurs.
cation, de l’industrie, de la taille ou du type de l’organisation, Proportionnalité énergétique : utiliser des serveurs avec
du fournisseur de cloud ou même du langage. un taux d’utilisation élevé, car la relation entre la puis-
sance et l'utilisation n'est pas exactement proportionnel-
Ces principes de développement sont au nombre de 8 aujour- le, car un ordinateur inactif n’a pas une puissance à 0.
d’hui, mais ils sont amenés à évoluer à travers la Green Figure B
Software Foundation et ses contributions open source. Mise en réseau : réduire la quantité de données et la
Voici un résumé de ces 8 principes que vous pouvez retrouver distance à parcourir sur le réseau. Les différents fac-
en français sur le site : [Link] teurs d’émissions sont par exemple : la distance, le
Minimiser l’empreinte carbone : cela paraît simple, nombre d’appareils réseau traversés, ou encore le pro-
mais penser son application pour avoir une empreinte tocole réseau utilisé.
carbone faible doit être prise en compte dès la phase Formuler au mieux la demande : au lieu de façonner
de conception et en tenant compte du maximum de l’offre pour répondre à la demande, essayez de façon-
paramètres : l’usage, le nombre d’utilisateurs, le temps ner la demande pour qu’elle corresponde à l’offre.
d’utilisation, etc. Pour donner un exemple, les logiciels de vidéoconféren-
Économiser l’électricité : la consommation électrique ce vont réduire la qualité vidéo pour donner la priorité
des composants d’un ordinateur n’est pas égale et doit à l'audio en cas de forte affluence.
être prise en compte pour venir optimiser sa consom- Mesure et optimisation : concentrez-vous sur les optimi-
mation électrique. Par exemple, un GPU pourra faire le sations de bout en bout qui augmente l’efficacité glo-
même travail qu’un CPU, mais avec beaucoup moins bale en empreinte carbone et utilisez des outils de
de cycles. Par exemple, le décodage d’un codex vidéo. mesure précis qui vous permettront de concentrer vos
[Link] efforts au bon endroit.
Prendre en compte l’intensité en CO2 : l'intensité carbo- - Sous Windows il y a la ligne de commande : [Link]
ne de l'électricité est une mesure de la quantité d'émis- /srumutil qui vous donne le détail exact de la consommation
sions de carbone (CO2éq) produite par kilowatt-heure électrique par process au format CSV ou XML si vous ajou-
d'électricité consommée. Chaque pays ou région va avoir tez le paramètre /XML.
une intensité carbone différente à un instant t en fonc- - Il y a aussi un projet Open source [Link]
tion de son type de production. Cette information peut
être prise en compte dans une application pour venir Je suis convaincu que respecter ces 8 principes peut aussi
réduire son émission de carbone et aussi éviter les émis- bien sensibiliser les développeurs que les utilisateurs dans
sions marginales en déplaçant la consommation élec- cette transition vers une meilleure sobriété numérique.
trique, par exemple, en différant une sauvegarde ou une
indexation de données. Figure A
[Link] 39
040_046.qxp_249 18/10/2021 16:13 Page40
40 [Link]
040_046.qxp_249 18/10/2021 16:13 Page41
[Link] 41
042.qxp_249 18/10/2021 16:15 Page42
Offres 2021
Profitez dès aujourd’hui de nos offres spéciales !*
1Programmez!
an + Pack maker/IoT 59€
Nos classiques
49€* 1Programmez!
an + Tous les numéros de Technosaures
79€
1 an D 10 numéros
(6 numéros + 4 hors séries)
79€*
+ accès aux archives + Pack maker/IoT
2 ans D 20 numéros
2 ans 89€
(12 numéros + 8 hors séries)
39€*
2 ans
Etudiant Programmez! + Pack maker/IoT
99€
1 an D 10 numéros
(6 numéros + 4 hors séries)
19€
Programmez! + Tous les numéros de Technosaures
Option : accès aux archives + accès aux archives + Pack maker/IoT
Contenu du
* Tarifs France métropolitaine
n Abonnement 1 an Etudiant : 39 € Programmez! + Tous les numéros Programmez! + Tous les numéros
Adresse : I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I
n Je joins mon règlement par chèque à l’ordre de Programmez ! n Je souhaite régler à réception de facture * Tarifs France métropolitaine
043.qxp_249 18/10/2021 16:17 Page43
N°1
N°2
N°3
235
236
238
239
HS 01 été 2020
N°4
N°5
240
241
242
N°6
N°7
+
HS 04 été 2021
N°8
Commandez
directement sur
246
247
248
€ = I___I___I___I___I € .............................I___I___I___I___I € €
Offres pouvant s'arrêter à tout moment, sans préavis
Adresse : I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I___I
Analysis propose des pistes d’améliorations à travers la chose. Dans cette optique, séparer les fichiers js et css du
fonction analyse des bonnes pratiques. Voyons par exemple html permet de mettre ceux-ci en cache. Optimiser en taille
la liste pour [Link] : Figure 3 les fichiers, images, pdf ou autres, et également minifier et
Ce type de checklist est utile. Car si tout le monde bien sûr compresser les fichiers qui peuvent l’être (css, js, html),
veut des pages performantes et teste que celles-ci s’affichent permet d’avoir moins de données à transférer.
bien, on ne parcourt pas systématiquement une liste d’idées Pour les cookies : comme ils sont transférés à chaque
d’améliorations. requête, leur poids s’ajoute à chaque fois. Il faut donc faire
Si on regarde dans le détail, on peut les classer selon trois attention à les conserver de petite taille. De plus les cookies,
axes, qui correspondent aux variables qui font la note. prévus pour conserver l'identité d’un utilisateur et gérer une
session de connexion, ne sont pas utiles pour récupérer des
Réduire le nombre de requêtes http fichiers statiques (images, css, js, documents PDF, …), il
Il y a une ligne dédiée « Limiter le nombre de requêtes » qui serait donc contre productif de les ajouter aux requêtes pour
s'affiche en rouge au-delà d’un certain seuil. Mais l’extension ceux-ci. On peut l’éviter en créant un sous-domaine différent,
nous suggère également d’éviter les redirections, de limiter le par exemple [Link]. En revanche, un grand nombre
nombre fichiers CSS, de ne pas télécharger des images non de noms de domaines est un signe que le site est
affichées ou d’utiliser des polices de caractères standard (déjà probablement trop complexe.
présentes sur la machine de l’utilisateur). L’utilisation des
mécanismes de cache permet également d’éviter des Économiser du temps de calcul
requêtes, en fixant une date de validité du fichier par exemple. sur le navigateur
On pourrait rétorquer qu’avec la généralisation d’http2, Enfin, certaines pratiques visent directement à limiter la
limiter le nombre de requêtes devient moins pertinent, car charge de calcul imposée au navigateur pour l’affichage de
avec cette mise à jour, il n’y a pas de surcoût à faire une la page. Des fichiers html, css ou js non valides nécessitent
requête http supplémentaire plutôt que regrouper les fichiers. plus de calcul pour que le navigateur devine ce qu’il doit
Mais un trop grand nombre de requêtes reste un indice qu’il faire. Retraiter les images côté client nécessite également des
y a peut être des améliorations possibles sur la page. ressources. Les plugins comme Flash ou Java sont aussi
plutôt lents et gourmands, mais dans le web d’aujourd’hui,
Réduire la bande passante utilisée leur utilisation est devenue très rare.
Réduire la quantité d’octets transmis permet de réduire la Cette liste est très incomplète et ne liste pas tout ce qui est
charge serveur et réseau. On pourrait se dire qu’un site web possible pour améliorer une page ou un site. Il s’agit d’un
est très petit par rapport à d’autres choses qui transitent sous ensemble des 115 bonnes pratiques d’éco conception
aujourd’hui sur internet, en particulier la vidéo ou les jeux en web6 fournies par le collectif numérique responsable, parmi
streaming. C’est vrai, mais comme on visite beaucoup de celles que l’on peut tester automatiquement. N’hésitez pas à
pages web, cela constitue tout de même une part importante aller lire le référentiel en entier, ainsi que d’autres comme la
du trafic et contribue à augmenter les besoins en checklist d’opquast ou les conseils de Google PageSpeed.
infrastructures. De plus, les actions réduisant la bande Notez aussi que satisfaire l’extension pour tout avoir en vert
passante utilisée vont aussi permettre d’avoir un site plus n’est pas le but. Il s’agit d’un outil alliant un objectif de
léger et rapide, ce qui réduira la pression au renouvellement sensibilisation et la fourniture de quelques idées
des appareils. Les mécanismes de cache du navigateur d’améliorations. Par exemple, si votre page ne sera jamais
permettent d’éviter de télécharger plusieurs fois la même imprimée par personne, passer du temps sur une CSS print
Figure 3
est sans doute inutile. Ce sera en revanche apprécié dans le
cas d’un contenu textuel.
Mobile first
Il existe une stratégie de conception et de test permettant de
valider qu’un site se comporte bien sur tous les appareils :
mobile first. L’idée est de concevoir d’abord les pages pour
des téléphones mobiles les plus contraignants en taille
d’écran ou en puissance, et aussi de concevoir pour un
réseau mobile faible, 3G voire 2G. Dans ces conditions, on
est obligé de travailler sur le poids et les performances du site
pour qu’il puisse être utilisable. On doit également se poser
des questions difficiles sur les contenus qui sont importants à
mettre en valeur et ceux qui le sont moins.
Une fois qu’on a une application qui fonctionne bien dans
ces conditions extrêmes, elle sera forcément confortable à
utiliser sur des machines plus puissantes, des écrans plus
grands, des réseaux plus performants.
(6) [Link]
44 [Link]
040_046.qxp_249 18/10/2021 16:14 Page45
[Link] 45
040_046.qxp_249 18/10/2021 16:14 Page46
Figure 5
Hacker News -
site de partage implique toute l’équipe : les designers qui vont penser les
de liens centrés écrans et leurs enchaînements, les graphistes qui vont réaliser
autour de la
technologie peut les maquettes et surtout les décideurs qui choisiront les
être considéré fonctionnalités, lesquelles inclure, leur nature, les modes
comme minima- d’interactions.
liste et low-tech
avec ses 7 N’hésitons pas à parler avec le reste de notre équipe de ces
requêtes http et problématiques : l’impact du numérique sur le monde,
moins de 20ko l’intérêt de faire léger et simple.
transférés.
Exemples de sites éco conçus
Avec la popularité croissante de l’écoconception web depuis
quelques années, des développeurs et designers ont déjà
construit des sites explicitement éco conçus. On y trouve le
site du Low Tech Magazine déjà évoqué,ou encore le portail
des services du gouvernement du royaume uni,
[Link] , volontairement minimaliste, mais aussi des
sites plus esthétiques, où rien ne laisse penser qu’ils sont low
tech, comme celui de la fabrik à vrac, épicerie bio. Figure 6
On peut donc parfaitement construire un site léger, beau et
très fréquenté.
46 [Link]
047_051.qxp_249 18/10/2021 16:38 Page47
Mesurer la consommation
d’énergie d’une application en
développement avec Scaphandre Benoit Petit
bpetit@[Link]
J’ai travaillé comme
Ingénieur systèmes et
Les conséquences du réchauffement climatique sont de plus en plus perceptibles cloud avant de créer
(inondations en Allemagne, records de chaleurs…). Plusieurs secteurs de l’économie Hubblo : pour aider les
entreprises à mesurer,
entament leur introspection afin de réduire leurs émissions de gaz à effet de serre. L’IT comprendre et réduire
ne fait pas exception, ce qui est heureux. On s’entend assez facilement l’impact de leurs services
numériques sur le
sur le fait qu’il faut mesurer son impact et le comprendre pour pouvoir le climat. Hubblo est à la
fois un éditeur de
réduire. Mais comment mesurer l’impact de ses applications et services, logiciels libres d’aide à la
quand on est impliqué dans une équipe de développement ? Comment mesure d’impact et un
intégrateur. Je suis
être plus sobre dans nos métiers ? Nous allons vous donner des élé- membre de Boavizta, un
ments de réponses avec Scaphandre. groupe de professionnels
qui œuvrent pour des
Scaphandre est un agent de monitoring dédié aux métriques Documentation : [Link] méthodes et des
données ouvertes pour
de consommation d’énergie. Il permet de mesurer la Site web d’Hubblo : [Link] aider à la mesure de
consommation d’électricité d’un serveur et des services qui y Projet Vegeta : [Link] l’impact des services IT
sont hébergés. La consommation de chaque processus systè- sur l’environnement. Je
me est mesurée, ce qui permet d’identifier des axes Le cas d’usage suis également
d’amélioration au niveau applicatif. Prenons l’exemple d’une API qui est sollicitée par un grand formateur pour la
sobriété numérique des
L’outil fonctionne pour le moment sur les plateformes sui- nombre d’utilisateurs sur des périodes clés. Le code serveur de infrastructures chez The
vantes : cette API sera vraisemblablement distribué sur plusieurs Green Compagnon.
• les serveurs bare metal sous GNU/Linux (le test pour cet ar- machines, voire dans des conteneurs répartis sur plusieurs
ticle est lancé sur un laptop sous GNU/Linux) machines à l’aide d’un scheduler, de manière à pouvoir
• des machines virtuelles lorsque l’agent est également ins- déployer ou retirer des conteneurs et éteindre ou allumer des
tallé sur l’hyperviseur (Qemu/KVM pour le moment) machines en fonction des pics de charge. On peut donc absor-
• Docker/Kubernetes ber des pics de charge et limiter la consommation de ressource
Figure 1 lorsque les besoins sont moindres (ce qui est une excellente pra-
L’outil, sous licence Apache 2.0, est extensible. Son architec- tique). Cependant, la consommation de ressources peut être
ture permet d’y ajouter des modules pour s’adapter à de nou- importante lors d’un pic de charge, ce qui n’est ni bon pour le
velles solutions de monitoring ou de traitement de données. porte-monnaie (surtout dans le cloud) ni pour la planète.
La philosophie de l’outil est de s’adapter à l’existant et non Quoi de mieux pour s’assurer que l’impact de l’application
l’inverse. On peut aujourd'hui associer scaphandre à une lors des pics de charge ne sera pas de plus en plus important
base de données orientée temps de type Prometheus ou de version en version, que de mesurer sa consommation
Warp10, ou une solution de stream comme Riemann. Il est d’énergie pendant un test de charge, avant le déploiement ?
également possible de récupérer les données dans des
fichiers JSON pour un post-traitement ou pour utiliser les Outillage
données de Scaphandre pour une application avancée Nous allons prendre le cas d’une application Python/Flask
(qualscan en est un exemple en permettant de mesurer la (très basique), que nous allons solliciter à l’aide de l’outil de
consommation d’énergie de l’installation des dépendances test de charge Vegeta. Nous mesurerons les effets de ces tirs
d’un projet NodeJS). Figure 2 de charge avec Scaphandre, stockerons les données dans
Code : [Link] Prometheus et afficherons les données avec Grafana pour
Figure 2
Figure 1
[Link] 47
047_051.qxp_249 18/10/2021 16:38 Page48
analyse. Pour une plus grande simplicité nous utiliserons doc- [Link] :
ker-compose pour piloter ces outils et jouer le scénario. apiVersion: 1
Le setup providers:
Voici à quoi ressemble le dossier courant :
├── app
. # <string> an unique provider name. Required
│ ├── [Link]
- name: 'Scaphandre examples'
│ └── Dockerfile
# <int> Org id. Default to 1
├── [Link]
orgId: 1
├── grafana
# <string> name of the dashboard folder.
│ ├── [Link]
folder: 'scaphandre'
│ ├── [Link]
# <string> provider type. Default to 'file'
│ ├── Dockerfile
type: file
│ └── [Link]
# <bool> disable dashboard deletion
├── prom
disableDeletion: true
│ ├── Dockerfile
# <bool> allow updating provisioned dashboards from the UI
│ └── [Link]
allowUiUpdates: false
├── [Link]
options:
└── vegeta
# <string, required> path to dashboard files on disk. Required when
└── Dockerfile
using the 'file' type
path: /var/lib/grafana/dashboards
Le dossier app contient l’application [Link] # <bool> use folder names from filesystem to create folders in Grafana
foldersFromFilesStructure: true
# save this as [Link]
from flask import Flask [Link]
# config file version
app = Flask(__name__) apiVersion: 1
@[Link]("/")
def fibo(): datasources:
response = "\n la suite fibonacci est : " # <string, required> name of the datasource. Required
response += str(fibonacci(30)) - name: Prometheus-scaph
return response # <string, required> datasource type. Required
type: prometheus
def fibonacci(n): # <string, required> access mode. direct or proxy. Required
if(n <= 1): access: proxy
return n # <int> org id. will default to orgId 1 if not specified
else: orgId: 1
return (fibonacci(n-1) + fibonacci(n-2)) # <string> url
response = "" url: [Link]
for i in range(n): isDefault: true
response += str(fibonacci(i)) version: 1
return response+", " # <bool> allows users to edit datasources from the UI.
et un Dockerfile, que voici : editable: true
FROM python
Dockerfile :
FROM grafana/grafana
RUN pip3 install -U Flask
COPY . .
COPY /[Link] /etc/grafana/provisioning/datasources/
COPY /[Link] /etc/grafana/provisioning/dashboards/
CMD ["flask", "run", "-h", "[Link]"]
COPY /[Link] /var/lib/grafana/dashboards/
On affiche donc la suite de Fibonacci à chaque nouvelle
Le dossier prom contient la configuration prometheus, pro-
requête, jusqu’à 30. Rien de plus simple, on se contente d’une
[Link] :
application web sans connexion à une base de données ou
# my global config
autre (mais l’exercice peut très bien se répéter avec quelque
global:
chose de plus complexe). Le dossier grafana contient un
scrape_interval: 10s # Set the scrape interval to every 15 seconds.
Dockerfile et des configurations de base pour installer le dash-
Default is every 1 minute.
board par défaut et se connecter à la datasource Prometheus.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default
48 [Link]
047_051.qxp_249 18/10/2021 16:38 Page49
is every 1 minute. l’on souhaite utiliser, ici prometheus (Une section dédiée aux
# scrape_timeout is set to the global default (10s). sensors et aux exporters scaphandre est présente dans la
documentation).
# Attach these labels to any time series or alerts when communicating with L’option --containers indique à scaphandre que l’on veut sur-
# external systems (federation, remote storage, Alertmanager). veiller les conteneurs présents sur la machine et appliquer des
external_labels: labels en rapport sur les métriques de consommation d’éner-
monitor: 'scaphandre-monitor' gie. Nous y reviendrons. Comme nous l’avons évoqué plus
haut, scaphandre peut adopter un comportement différent
# Load rules once and periodically evaluate them according to the global en fonction de la base de données de série temporelle ou de
'evaluation_interval'. la solution de monitoring que l’on adresse. Ici, on utilise l’ex-
rule_files: porter prometheus. Tous les conteneurs peuvent discuter à
# - "[Link]" travers le réseau “lab-network”. Tous les fichiers pour ce test
# - "[Link]" sont disponibles sur github : [Link]
tions/tree/master/article-programmez-09-2021
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself. Premier test
scrape_configs: Grâce au docker-compose, nous allons pouvoir jouer le scé-
# The job name is added as a label `job=<job_name>` to any timeseries nario de test très simplement :
scraped from this config. docker-compose up -d
- job_name: 'prometheus'
Une fois lancé nous devrions avoir les conteneurs suivants :
# metrics_path defaults to '/metrics' CONTAINER ID IMAGE COMMAND
# scheme defaults to 'http'. CREATED STATUS PORTS NAMES
0de3ec310881 article-programmez-09-2021_vegeta
static_configs: "/bin/bash -c 'sleep…" 2 seconds ago Up Less than a second
- targets: ['localhost:9090'] article-programmez-09-2021_vegeta_1
3afaa56604dc article-programmez-09-2021_app "flask run -h
- job_name: 'scaphandre' [Link]" About an hour ago Up About an hour [Link]:5000-
# metrics_path defaults to '/metrics' >5000/tcp article-programmez-09-2021_app_1
# scheme defaults to 'http'. 71ada46d0bcf article-programmez-09-2021_prometheus
"/bin/prometheus --c…" 2 hours ago Up 2 hours
static_configs: [Link]:9090->9090/tcp article-programmez-09-2021_prometheus_1
- targets: ['scaphandre:8080'] 57573fa6e388 hubblo/scaphandre:build-PR_84-docker-labels-9
"/usr/local/bin/scap…" 2 hours ago Up 2 hours
Nous voyons à la dernière ligne de la configuration que sca-
[Link]:8080->8080/tcp article-programmez-09-2021_scaphandre_1
phandre fournit les données à prometheus en pull mode
1cf7a95368e2 article-programmez-09-2021_grafana "/[Link]"
(c’est prometheus qui vient “scraper” les données pour les
8 hours ago Up 8 hours [Link]:3000->3000/tcp
stocker en base), soit le mode classique. Dans le langage
article-programmez-09-2021_grafana_1
Prometheus, scaphandre est un exporter.
Le dossier prom contient aussi le Dockerfile suivant : Grafana est alors accessible sur [Link] Les identi-
fiants sont admin / secret. Figure 3
FROM prom/prometheus
Le premier graphe en haut à gauche représente la consom-
mation d’énergie instantanée de puissance de la machine (en
Watts), celui à sa droite représente la consommation d’éner-
COPY /[Link] /etc/prometheus/[Link]
gie sur la période de temps sélectionnée (en Watts-heure). Le
Pour finir, voici le docker-compose qui nous permet de mettre graphe du dessous est la consommation par socket CPU. Figure 3
tout ce petit monde à l’unisson :
Code complet sur [Link] & github
Attardons-nous sur la ligne de commande du conteneur
vegeta : /bin/bash -c 'sleep 10 && echo "GET [Link] |
vegeta attack -duration=200s -rate=500 | tee [Link] | vegeta report'
On demande à “vegeta” de jouer la requête GET pendant
200 secondes, à un rythme de 500 requêtes par seconde,
puis de nous afficher le résultat du test. La ligne de comman-
de du conteneur scaphandre (["prometheus", "--containers"])
ressemblerait à celle-ci si l’on lançait scaphandre directe-
ment dans le terminal : scaphandre prometheus --containers
Le premier verbe est l’exporter choisi, soit le connecteur spé-
cifique à la solution de stockage et d’analyse de données que
[Link] 49
047_051.qxp_249 18/10/2021 16:38 Page50
Puisque je n’ai qu’une socket CPU sur la machine qui me per- Voyons ce que change en termes de consommation d’éner-
met de lancer le test, c’est exactement le même graphe qu'au- gie le fait de passer à une boucle simple :
dessus. Le dernier étage donne la consommation d’énergie Code complet sur [Link] & github
par processus, avec à gauche, un top des processus les plus Une fois le code remplacé, on peut reconstruire l’image du
gourmands en énergie en ce moment et à droite l’historique conteneur de l’app :
de consommation de puissance par processus, sur la fenêtre
docker-compose build app
de temps. Lorsque l’on arrive la première fois sur le dash-
board, le graphe en bas à droite donne la consommation de
Puis relancer le test :
tous les processus collectés. On peut alors filtrer pour obtenir
docker-compose up -d
uniquement la consommation de notre application en saisis-
sant un mot clef dans la case “process_filter” en haut. En sai- Et constater l’effet de ce nouveau tir de charge : Figure 5
sissant “flask”, on va avoir la consommation des processus On relève maintenant comme maximum 4,34W et en valeur
qui contiennent “flask” dans leur ligne de commande et ainsi moyenne 2,27W. Cette version du code est donc à priori
isoler la consommation de notre application durant le test. moins énergivore que la précédente. Notons tout de même
Une fois le test terminé on peut sélectionner la fenêtre de que ce test est extrêmement rudimentaire et qu’il faudrait
temps exact du test en cliquant et en faisant glisser le curseur valider cette affirmation avec des scénarios de tir de charge
au niveau du graphe de consommation. Figure 4 différents (et valider ça avec un serveur d’application bon
Grafana nous donne gentiment quelques stats sur cette pour de la production, comme gunicorn).
fenêtre. L’application a consommé au maximum 5,16W et en
moyenne 3,01W. Ces chiffres permettent un premier exemple Relever la consommation de conte-
de comparaison avec des versions ultérieures de l’application. neurs appartenant à un même service
Nous avons vu dans cet article l’option --containers. Voyons
Mise à jour de l’application et second test dans la pratique à quoi elle sert.
Vous aurez certainement reconnu dans le code de notre Lorsque notre application passera en production, nous aurons
application une recursion : certainement un nombre important de conteneurs, derrière
un load balancer, pour tenir la charge. Si l’on souhaite mesu-
# save this as [Link]
rer la consommation d’énergie de l’application dans ce
from flask import Flask
contexte, il faudra accéder à cette information pour chaque
conteneur participant au service. C’est là que l’option est
utile, Scaphandre va alors appliquer des labels spécifiques aux
app = Flask(__name__)
métriques de consommation de chaque processus, lorsque
celui-ci est conteneurisé. Voici à quoi ressemble la métrique
@[Link]("/")
de consommation de notre application vu par prometheus (et
def fibo():
visible sur [Link] dans notre exemple) :
response = "\n la suite fibonacci est : "
response += str(fibonacci(30)) scaph_process_power_consumption_microwatts{container_docker_version="20.
return response 10.2",container_label_com_docker_compose_service="app",container_lab
el_com_docker_compose_config_hash="a2cc6462a0ba2ab59f230d5ea16fe54c38
def fibonacci(n): 666124dfc27f56f5789f0cf314a2bf",container_names="article-programmez-09-
if(n <= 1): 2021_app_1",container_label_com_docker_compose_oneoff="False",container
return n _id="27f3159ee70707d74f5da09468c1076abba719ca3b97bb1f60907f184
else: 8569df9",container_label_com_docker_compose_version="1.25.0",exe="flask",
return (fibonacci(n-1) + fibonacci(n-2)) container_scheduler="docker",pid="851283",container_label_com_docker
response = "" _compose_container_number="1",container_label_com_docker_compose_project
for i in range(n): _working_dir="/home/bpetit/git/hubblo/formations/article-programmez-09-
response += str(fibonacci(i)) 2021",container_label_com_docker_compose_project_config_files="docker-
return response+", " [Link]",container_label_com_docker_compose_project="article-programmez
Figure 4
-09-2021",cmdline="/usr/local/bin/python/usr/local/bin/flaskrun-h0.0.0.0"} 0
50 [Link]
047_051.qxp_249 18/10/2021 16:38 Page51
té le démon docker et donc est capable d’aller chercher des d’autres éléments qui parfois pèsent encore plus lourd dans
informations supplémentaires sur chaque processus associé le bilan carbone. La consommation d’électricité est indirecte-
à un conteneur. Kubernetes devrait être également supporté ment responsable d’émissions de Gaz à Effet de Serre (GES),
à la fin de l’été 2021. Ajoutons un panel dans le dashboard car c’est une énergie dite secondaire, ou finale, par opposi-
grafana pour illustrer, soit en cliquant sur le bouton “Add tion aux énergies primaires (charbon, vent, rayons du soleil,
Panel”, soit en dupliquant le panel qui montre la consomma- pétrole, éléments radioactifs…) qui sont consommées pour la
tion de flask, avec “Duplicate” : Figure 6 fabriquer. On peut effectuer la conversion en émissions de
Une fois fait, il faut changer la formule utilisée avec Edit, sur GES, grâce aux données présentées sur un service comme
le nouveau panel. Figure 7 ElectricityMap. Ces émissions sont classées dans le “scope 2”
Notre requête est constituée de : des émissions, celui des émissions indirectes.
• scaph_process_power_consumption_microwatts : le nom Le “scope 1” représente les émissions directes. Dans le cas
de la métrique qui nous intéresse d’un service IT, peu de choses sont classées dans le scope 1.
• container_label_com_docker_compose_service : le label Une exception, dans le cas d’un exploitant de datacenter, serait
qui nous permet d’identifier les données qui nous intéres- le fioul brûlé lors d’une coupure générale d’électricité malgré la
sent, ici ce sont celles avec la valeur “app” redondance des arrivées électriques (ou lorsque ce fioul est
Notons que l’on divise le résultat par 1 000 000, car la considéré périmé et qu’il est brûlé pour être remplacé…).
métrique nous retourne des microwatts. Une valeur en micro- Le troisième et dernier scope, le “scope 3”, représente les
watts n’est pas très explicite pour ma part, je préfère lire les autres émissions, notamment celles qui sont liées à la fabri-
données en watts pour mieux me représenter les choses. cation des équipements, à leur transport et à leur fin de vie.
Les informations nécessaires pour calculer ce dernier scope
Conclusion sont souvent plus compliquées à obtenir et même parfois dé-
Cet article est une première approche de l’utilisation de libérément écartées (ce qui arrange bien les entreprises qui
Scaphandre, pour mesurer la consommation d’énergie d’un affirment être “neutre en carbone”).
service. Il est possible de décliner l’usage de cet outil à de Mesurer la consommation d’énergie est donc une bonne
nombreux autres cas. Les seuls pré-requis au moment où chose pour un service plutôt gourmand sur sa phase de run
j’écris ces lignes, sont d’ tre sur une machine physique, sous (web à haute volumétrie, machine learning, recherche,
GNU/Linux, ou bien sur une machine virtuelle, si Scaphandre média, etc.). C’est un indicateur qui permet d’orienter ses
est également installé sur l’hyperviseur et que l’on utilise l’op- efforts de sobriété dans le développement d’une application
tion --vm (voir la documentation). ou l’évolution d’une infrastructure IT. Il reste cependant indis-
Notre exemple est imparfait, car on lance les tirs de charge pensable, si l’on souhaite réduire efficacement et durable-
depuis la même machine que celle qui fait tourner l’applica- ment les émissions d’une entreprise, d’adopter une
tion. Pour obtenir des métriques plus “propres”, il serait démarche plus globale et de prendre en compte le scope 3
mieux d’utiliser deux machines différentes. Il serait égale- des émissions dans la stratégie et dans les calculs.
ment intéressant d’intégrer cette suite dans la chaîne de CI,
pour garantir un suivi de l'appétit du service en énergie et ça
de version en version, systématiquement, pour chaque contri-
bution de l’équipe de développement.
Enfin il faut prendre un peu de recul pour préciser que ces
métriques de consommation d’énergie ne sont qu’une partie
des informations nécessaires pour connaître l’impact d’un
service numérique sur le climat. C’est certainement la partie
la plus accessible pour un profil technique en termes de res-
ponsabilités, mais une analyse d’impact complète comprend
Figure 6
Figure 7
[Link] 51
052_053.qxp_249 18/10/2021 16:38 Page52
52 [Link]
052_053.qxp_249 18/10/2021 16:38 Page53
[Link] 53
054_057.qxp_249 18/10/2021 16:39 Page54
54 [Link]
054_057.qxp_249 18/10/2021 16:39 Page55
Figure 1
La première étape sera pour nous de trouver les inputs et les public static <IN1, IN2, OUT> void verifyAllCombinations(Function2<IN1, IN2, OUT>
séparer du code. C’est facile : call, IN1[] parameters1, IN2[] parameters2) {
...
@Test }
void find_first_available_recruiter_who_can_test_candidate() {
String candidateId = "123"; Ce qui signifie que nous devons transformer nos inputs en
LocalDate interviewDate = of(2021, 2, 21); tableau pour pouvoir les utiliser. Ce qui nous donne le code
suivant :
// Given
var interview = new PlanInterview( @Test
candidates, recruiters, interviews); void find_first_available_recruiter_who_can_test_candidate() {
String[] candidateId = {"123"};
// When LocalDate[] interviewDate = {of(2021, 2, 21)};
Interview plannedInterview =
[Link](candidateId, interviewDate); verifyAllCombinations(this::extracted, candidateId, interviewDate);
}
// Then
assertThat(plannedInterview).isEqualTo(expectedInterview()); Vous allez me dire “ mais pourquoi se donner autant de mal
} pour écrire l'équivalent d’un test qui fonctionnait ? “
La réponse c’est qu'avec Approval Tests on peut lancer plein
Avec Approval Tests je n’ai pas besoin de la partie “Then”. d’inputs sur un seul test. Je vous rappelle que nos inputs sont
C’est lui qui fera ça pour moi. Je peux donc supprimer cette maintenant des tableaux de données et c’est exactement ce
partie. dont nous avons besoin pour utiliser la technique Golden
Ensuite, je vais extraire les parties “Given” et “When” pour Master.
créer une fonction à tester :
Lancement d’Approval Tests :
@Test Le première fois qu’on lance les Approvals Tests, c’est
void find_first_available_recruiter_who_can_test_candidate() { toujours rouge. C’est parce qu’on a besoin de lancer les tests
String candidateId = "123"; au moins une fois pour récolter les outputs dont on a parlé
LocalDate interviewDate = of(2021, 2, 21); pour le Golden Master.
En clair, pas d’image de comparaison = tests rouges
extracted(candidateId, interviewDate); Figure 1
} Si vous regardez dans l’arborescence de votre projet vous
allez voir que 2 nouveaux fichiers ont été générés :
private Interview extracted(String candidateId, LocalDate interviewDate) { • Un fichier nommé “received“
// Given • Un fichier nommé “approved”
var interview = new PlanInterview( Ce sont vos Golden Master : Figure 2
candidates, recruiters, interviews); Maintenant il suffit de copier le contenu du fichier “received”
dans le fichier “approved” pour passer les tests au vert. Vous
// When pouvez aussi supprimer le fichier “approved” et renommer le
return [Link](candidateId, interviewDate); fichier “received” pour “approved”. Figure 3
} Voyons voir à quel point ce premier test était efficace. Pour
[Link] 55
054_057.qxp_249 18/10/2021 16:39 Page56
cela, nous allons regarder la couverture des tests à l’aide données ou encore bien lire le code. Si ce n’est pas possible,
d’IntelliJ. Il suffit de lancer les tests avec ce bouton : essayez de mettre des données aléatoires qui vous semblent
pertinentes par exemple “null”, chaîne de caractère vide, des
Et voici le résultat : Figure 4 chiffres -1, 0, 1, etc.
On peut constater que le seul test qu’on a écrit ne couvre pas Notre test va donc ressembler à ça :
toutes les lignes de notre code, car il faut plus d’inputs. Nous
devons donc trouver plus de données. Pour ça, on peut @Test
demander l’aide d’un expert métier, ou regarder la base de void find_first_available_recruiter_who_can_test_candidate() {
String[] candidateId = {"123", "456", "789", null, ""};
Figure 2 LocalDate[] interviewDate = {
of(2021, 2, 21),
of(2021, 2, 20),
of(2021, 2, 22),
of(1300, 2, 21),
of(3200, 2, 21),
null
};
Trop fort cet outil ! Avec 1 test et 1 ligne de code j’ai obtenu
30 tests d’un coup. Cela correspond à la combinaison de
toutes les données que je lui ai passée en input.
Figure 3
56 [Link]
054_057.qxp_249 18/10/2021 16:39 Page57
@Override une journée complète de travail. Les tests sont donc utiles et
public String toString() { ne sont en aucun cas négligeables. Passer du temps à écrire
return "Candidate{" + des tests est un gain de temps pour le maintien de mes
"skills=" + skills + produits sur le long terme.
", name='" + name + '\'' +
'}'; Conseils :
} • Les Approvals Tests ne sont pas autosuffisants. Il faut créer
des tests unitaires, d’intégration, etc. pour obtenir un
REX :
résultat vraiment fiable.
Personnellement je ne m’amuse pas à refactorer du code • Il ne faut pas se fier à un code qui est couvert à 100 % par
pour me faire plaisir. Si une partie du code fonctionne les tests. Pour cela, essayez d’ajouter du “Mutation
correctement, même s’il est illisible, pas testé ou long, tant Testing”. Vous pouvez le faire à la main, en modifiant des
que je n’ai pas à le modifier, je ne vais pas le refactorer. Il faut conditions ou des valeurs dans votre code. Si après avoir
toujours garder en mémoire que le refactoring a un coût et modifié certaines logiques du code, les tests sont toujours
celui-ci n’est pas négligeable surtout lorsqu'on est face à une verts, c’est que vous n’avez pas de tests fiables.
longue méthode, par exemple. • Ne pas refactorer un code qui fonctionne correctement si
Par contre, si jamais je dois modifier ou améliorer une partie on ne doit pas lui ajouter/supprimer une fonctionnalité.
du code et que celle-ci n’est pas couverte (ou pas
complètement) par des tests j’en profite pour utiliser les FAQ :
Approval Tests. Une fois, cette partie du code couverte par • Peut-on tester une méthode qui renvoi void ? Oui, mais
des tests, je peux ensuite commencer à le refactorer, à le cela ne va pas vraiment nous aider à faire un Golden
casser en plus petits morceaux, afin de le rendre plus Master, car on n’obtient rien en output.
maîtrisable. Je vais aussi mettre en place des nouvelles • Peut-on moquer les dépendances externes ? Oui vous
classes, des méthodes et de nouveaux tests. pouvez très bien utiliser une librairie de bouchon par
Mon refactoring est terminé. Grâce à mes Approval Tests, je exemple Mockito pour Java.
sais que je n’ai rien cassé dans mon code. Je peux
maintenant commencer à les supprimer, puisque j’ai couvert Repos GIT :
mon code par d’autres types de tests (unitaires, d’intégration, • Java :
d’acceptance, etc.) [Link]
Cette procédure peut sembler coûteuse au niveau du temps. tion-1/approval_tests
Cependant, il m’est arrivé plusieurs fois de refactorer un code • C# :
non testé et de créer des bugs, voire des problèmes de prod. [Link]
J’ai donc dû revenir en arrière, ce qui m’a parfois fait perdre tree/solution-1/approval_tests
Figure 5
[Link] 57
058_061.qxp_249 18/10/2021 19:02 Page58
Variables {
Figure 1 seconde 1000
five_secs 5000
minute 60000
}
Display LCD2USB {
Driver 'LCD2USB'
Size '16x2'
Backlight 0
Icons 1
}
58 [Link]
058_061.qxp_249 18/10/2021 19:02 Page59
ve aussi la possibilité de se créer nos propres icônes. Nous définissons donc pour chaque Widget le fait que nous
Une fois défini il faut demander à LCD4Linux d’utiliser le display : allons afficher du texte de longueur 5, qu’il faudra aligner le
texte à gauche (L : Left) et qu’il faudra mettre à jour l’infor-
Display 'LCD2USB' mation toutes les minutes pour la date et toutes les secondes
pour l’heure. Le plus important est ce que nous voulons affi-
Maintenant nous allons devoir définir ce que nous voulons cher c’est-à-dire l’expression. LCD4Linux nous permet d’uti-
afficher, cela se fait à l’aide de composant appelé Widgets : liser la fonction strftime() pour afficher la date courante
time() avec un format spécifique. '%d/%m' pour la date et
Widget Hello { '%H:%M' pour l’heure. Mettons à jour notre Layout :
class 'Text'
expression 'Hello LCD' Layout MyLayout {
width 9 Row1 {
} Col12 'Date'
}
Ici notre Widget texte affichera « Hello LCD ». La longueur du Row2 {
texte est importante, elle servira lors de l'alignement du texte Col12 'Time'
à droite par exemple. }
Nous allons maintenant pouvoir positionner notre Widget au }
sein d’un Layout :
Affichage de l’utilisation du CPU
Layout MyLayout { L’utilisation du CPU sur notre machine est aussi une informa-
Row1 { tion importante, nous allons afficher cette information au
Col1 'Hello' début de la première ligne. Comme précédemment il va nous
} falloir ajouter un Widget qui sera ensuite intégré à notre
Row2 { Layout :
Col2 'Hello'
} Widget Busy {
} class 'Text'
expression proc_stat::cpu('busy', second)
Un Layout se compose donc de lignes (row) et de colonnes postfix '%'
(col) sur lesquelles on va pouvoir afficher des Widgets. Notre width 5
texte sera donc affiché sur la première colonne de la premiè- precision 0
re ligne et la deuxième colonne de la deuxième ligne. align 'R'
Notre fichier de configuration étant complété il ne nous reste update second
plus qu’à tester le résultat en redémarrant le service : }
Layout MyLayout {
sudo systemctl restart lcd4linux
Row1 {
N. B. : Par la suite, il faudra exécuter à nouveau la commande pour Col1 'Busy'
tenir compte de chaque modification.
Col12 'Date'
Affichage de la date et l’heure }
Nous allons afficher la date et l’heure sur les dernières Row2 {
colonnes de nos 2 lignes d’écran LCD. Il faut donc créer 2 Col12 'Time'
Widgets : une pour la date et une pour l’heure : }
}
Widget Date {
class 'Text' Concentrons-nous sur les paramètres importants du Widget.
expression strftime('%d/%m',time()) Nous utilisons postfix pour afficher le caractère % après la
width 5 consommation de notre CPU. Nous alignons notre texte à
align 'L' droite et nous ne voulons aucun chiffre après la virgule : pré-
update minute cision 0. L’expression nous permet de récupérer l’utilisation
} du CPU avec une périodicité de 1 seconde.
[Link] 59
058_061.qxp_249 18/10/2021 19:02 Page60
Row7 '.....'
Row8 '.....'
}
}
Layout MyLayout {
Row1 {
Col1 'Busy'
Col12 'Date'
}
Row2 {
Col1 'MySQLTemp'
Col5 'Degree'
Col12 'Time'
}
}
60 [Link]
058_061.qxp_249 18/10/2021 19:02 Page61
Figure 5
[Link] 61
062_068.qxp_249 18/10/2021 16:54 Page62
Coder Fibonacci
en Java avec des tests
Thierry LERICHE Dans cet article, on se propose de coder le calcul des termes de la Suite de Fibonacci
Architecte et Tech lead
@ThierryLeriche
ainsi que de quelques variantes, comme Tribonacci, le tout en Java, et en écrivant des
tests. L'idée est d'écrire les tests avant, pendant et après le processus d'implémentation
des fonctionnalités.
/**
* Calcule la valeur du terme de la suite de Fibonacci pour le rang indiqué.
Il existe de nombreuses façons de coder Fibonacci, dépen-
*
dant du langage de programmation utilisé, mais également
* REGLE #0 f(0) = 0
des concepts mis en œuvre. Dans ce billet, qui se veut assez
* REGLE #1 f(1) = 1
simple, on va se limiter à de la programmation récursive.
* REGLE #2 f(n) = f(n-1) + f(n-2) si 2 < n
D'autres versions sont disponibles sur le Web. Et dans tous les
* REGLE #3 f(n) = Exception si n < 0
cas, c'est un basique que les étudiants en informatique doi-
*
vent connaître par cœur1.
* Premiers termes : 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597
On va donc employer des tests tout au long du processus, en
*
s'inspirant de TDD (Test Driven Development), sans en faire
* cf. [Link]
au sens pur, et de 3T (Tests en Trois Temps). Les tests permet-
*
tent de structurer la pensée, et donc l'architecture du code.
* @param n le rang
On admet généralement qu'un code non testable est mal
* @return la valeur du terme pour le rang indiqué
conçu, alors qu'un code testable est mécaniquement de
* @throws IllegalArgumentException si le rang indiqué est négatif
meilleure qualité. Les tests vont aider à programmer, mais
*/
également à réfactorer le code, c’est-à-dire à revenir dessus
int calculate(int n);
pour l'améliorer (bugs, performances, beauté, etc.) en ayant
une situation stable au départ, garantie par les tests, pour Si on prévoit d'écrire un peu de doc, l'interface est sans aucun
contrôler l’avancement. doute l'endroit le plus adapté. On peut mettre la doc dans un
fichier séparé, sur le réseau de l'entreprise par exemple, mais
Fibonacci les développeurs préfèrent généralement avoir la doc sous les
En mathématiques, la suite de Fibonacci est une suite d'en- yeux et disponible dans l’éditeur.
tiers dans laquelle chaque terme est la somme des deux À ce stade, on a juste besoin d'une implémentation qui com-
termes qui le précèdent. Elle est très utile, par exemple pour pile. Les IDE, comme Eclipse ou Intellij, savent générer le
calculer le nombre d’or, et on la retrouve largement dans la code à partir de l'interface. Ils retournent habituellement zéro
nature. Bla-bla-bla. On sait déjà ça et c'est très bien expliqué comme valeur par défaut. C'est ce qu'on appelle un nombre
sur la page Wikipedia dédiée2, vers laquelle je vais donc vous magique et c'est quelque chose qu'on veut absolument éviter.
renvoyer pour la longue explication. En effet, cette valeur pourrait correspondre à quelque chose
Dans les grandes lignes, la suite est définie par : de réel pour la fonctionnalité, ce qui est d'ailleurs le cas pour
• f(0) = 0 Fibonacci. Et choisir une autre valeur n'est pas forcément
• f(1) = 1 mieux. Cela porte à confusion. Ici, on va plutôt lancer une
• f(n) = f(n-1) +f(n-2) si 1 < n exception spécialisée, indiquant que le code n'a pas encore
Les premiers termes de cette suite sont donc 0, 1, 1, 2, 3, 5, été écrit. Pour les développeurs, qui liront ce code, le message
8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597… sera clair.
(1) C’est le type d’exercice simple qu’on peut demander aux juniors en public class RecursiveFibonacci implements Fibonacci {
entretien.
(2) [Link] @Override
62 [Link]
062_068.qxp_249 18/10/2021 16:54 Page63
[Link] 63
062_068.qxp_249 18/10/2021 16:54 Page64
64 [Link]
062_068.qxp_249 18/10/2021 16:54 Page65
@Override
public int calculate(int n) { Figure 3
// REGLE 1
if (n == 0) {
return 0;
}
On lance (tous) les tests dès qu'on est content d'un bloc de
code. Ce n'est pas la peine d'attendre, car il n'y a aucune éco-
nomie intéressante à espérer. Figure 3
Et puis on continue avec le deuxième test…
@Override
public int calculate(int n) {
// REGLE 1
if (n == 0) { Figure 4
return 0;
}
Le calcul final, quant à lui, n'est pas super compliqué. C'est
un simple, enfin double, appel récursif.
if (n == 1) {
return 1; @Override
} public int calculate(int n) {
...
throw new UnsupportedOperationException("bientôt");
} return calculate(n – 1) + calculate(n – 2);
}
Ce qui nous permet déjà d'avoir deux cas de test au vert (ie.
passant). Figure 4 On voit que tous les cas des tests paramétrés deviennent verts
On a donc une (petite) situation stable. Et on peut en profiter d'un coup, sauf un... Flute de zut alors !... Hummm... c'est
pour se lancer dans un (petit) refactoring. L'idée est que ça bizarre. Il faut analyser.
marchait avant le refacto, avec rapport de test à l'appui, et En y regardant bien, on voit que ce n'est pas le code qui plan-
que ça doit marcher (en mieux) après. Si des tests verts te, mais que c'est le jeu de test qui est mauvais. Ça arrive. Ici,
deviennent rouges, cela indique qu'on a raté une étape. Dans on s'est trompé en recopiant les valeurs dans le fichier CSV.
ce cas, on pose le stylo et on corrige. Ici le refactoring propo- En effet, on attendait la valeur 7, mais on obtient bien 8 qui
sé est trivial, mais il illustre bien le processus. est la bonne valeur pour le rang 6.
Des fois, c'est le cahier des charges qui contient des erreurs.
@Override
En travaillant en mode Agile, on accepte que ça arrive. Il suf-
public int calculate(int n) {
fit de passer un coup de fil au rédacteur pour clarifier la situa-
// REGLE 1
tion. Ce genre de chose est plus fréquent qu'on ne le pense.
if (n == 0 || n == 1) {
Il ne reste plus qu'à vérifier le cas des rangs négatifs.
return n;
} @Override
public int calculate(int n) {
throw new UnsupportedOperationException("bientôt"); // REGLE 4
} if (n < 0) {
[Link] 65
062_068.qxp_249 18/10/2021 16:54 Page66
@Test @Override
public void testCalculateVeryLong() { public int calculate(int n) {
// Arrange ...
final int n = 50;
final Integer memorized = [Link](n);
// Act if (memorized != null) {
[Link](n); return memorized;
} }
// Act
final int result = [Link](n);
[Link](result);
}
66 [Link]
062_068.qxp_249 18/10/2021 16:54 Page67
BigInteger dont le nom permet d’en comprendre l'utilité. tionne du premier coup. On peut donc envoyer ce nouveau
code au client. Et ainsi de suite…
public interface Fibonacci {
Tribonacci
BigInteger calculate(int n);
La suite de Tribonacci est une suite d'entiers dans laquelle
Ça se change dans l'interface, et donc dans les tests en chaque terme est la somme des trois termes qui le précèdent.
conséquence. Et, là aussi, c'est très bien expliqué sur la page Wikipedia
dédiée4. Dans les grandes lignes, la suite est définie par :
private void doTestCalculate(fina int n, final BigInteger expected) {
• f(0) = 0
...
• f(1) = f(2) = 1
}
• f(n) = f(n-1) +f(n-2) + f(n-3) si 2 < n
Les premiers termes de cette suite sont donc 0, 0, 1, 1, 2, 4, 7, 13, 24,
@ParameterizedTest
44, 81, 149, 274, 504, 927, 1705, 3136, 5768, 10609, 19513, 35890,
@CsvFileSource(...)
66012, 121415, 223317, 410744, 755476, 1389537, 2555757…
public void testCalculate(final int n, final BigInteger expected) {
doTestCalculate(n, expected);
K-bonacci
}
Ce sont des suites dont la relation de récurrence est d'ordre
k. Un terme est la somme des k termes qui le précèdent
@Test
Dans les grandes lignes, la suite est définie par :
public void testCalculateBigValue() {
• f(0) = 0
// Arrange
• f(n) = 1 si 0 < n < p
final int n = 62;
• f(n) = f(n-1) + f(n-2) + ... + f(n-p) si p <= n
Et donc, Fibonacci est de récurrence p=2 et Tribonacci est de
// Act
récurrence p=3. D'une certaine manière, on pourra considé-
final BigInteger result = [Link](n);
rer, d'un point de vue programmation, que ce sont des cas
particuliers de K-Bonacci. Après avoir codé Fibonacci, c'est
// Assert
donc une formalité de proposer une interface pour K-
[Link](0 < [Link]([Link]));
Bonacci. En plus du rang n, il suffit d'ajouter la récurrence p.
}
public interface KBonacci {
Et donc aussi dans l'implémentation, où l’utilisation de
BigInteger à la place du type primitif int est tout de même
/**
moins lisible.
* Calcule la valeur du terme de la suite de K-Bonacci pour le rang n et la récurrence p indiqués.
private Map<Integer, BigInteger> memo = new HashMap<>(); *
* REGLE #0 f(0) = 0
@Override * REGLE #1 f(n) = 1 si 0 < n < p
public BigInteger calculate(int n) { * REGLE #2 f(n) = f(n-1) + f(n-2) + ... + f(n-p-1) si p < n
// REGLE 4 * REGLE #3 f(n) = Exception si n < 0 ou si p < 0
if (n < 0) { *
throw new IllegalArgumentException("The parameter n can not be negative!"); * Premiers termes pour p=3 : 0, 1, 1, 2, 4, 7, 13, 24, 44, 81...
} *
* cf. Tribonacci [Link]
// REGLE 1, 2 *
if (n == 0 || n == 1) { * @param n le rang
return [Link](n); * @param p la récurrence
} * @return la valeur du terme pour le rang et la récurrence indiqués
* @throws IllegalArgumentException si le rang ou la récurrence indiqués sont négatifs
final BigInteger memorized = [Link](n); */
if (memorized != null) { BigInteger calculate(int n, int p);
return memorized;
Comme toujours, on part d'une implémentation vide.
}
public class RecursiveKBonacci implements KBonacci {
// REGLE 3
final BigInteger result = calculate(n – 1).add(calculate(n – 2)); @Override
[Link](n, result); public BigInteger calculate(int n, int p) {
throw new UnsupportedOperationException("bientôt");
return result; }
}
[Link] 67
062_068.qxp_249 18/10/2021 16:54 Page68
@Override
public BigInteger calculate(int n, int p) {
// REGLE 0
if (n == 0) {
return [Link];
}
// REGLE 1
if (n < p) {
return [Link];
}
68 [Link]
069_071.qxp_249 18/10/2021 16:54 Page69
[Link] 69
069_071.qxp_249 18/10/2021 16:54 Page70
segment(x2,y2,x3,y3)
else :
(a,b)=milieu(x1,y1,x2,y2)
(c,d)=milieu(x2,y2,x3,y3)
(e,f)=milieu(a,b,c,d)
bezier(x1,y1,a,b,e,f,n-1)
bezier(e,f,c,d,x3,y3,n-1)
70 [Link]
069_071.qxp_249 18/10/2021 16:54 Page71
Figure 4
Figure 5
right(120) chacun dans un sens, de façon que les ouvertures soient vers
up() l’extérieur. Il ne reste plus qu’à relier de proche en proche les
goto(0, 0) extrémités des motifs pour obtenir la courbe suivante :
Figure 4
En théorie, la construction devrait être répétée indéfiniment
pour obtenir une courbe fractale, car par définition, il s’agit La courbe fractale obtenue après une infinité d’itérations pos-
de la courbe « limite », obtenue après une infinité d’étapes. sède la propriété de passer par chacun des points du carré
Concrètement, on fixe préalablement la profondeur des bâti à partir du motif initial.
appels récursifs. Dans le programme ci-dessous elle est limi-
tée à 6, car d’une part le temps d’exécution est déjà long, et Courbe du dragon
d’autre part, il n’y aurait plus beaucoup de différence visuelle La courbe du dragon a été inventée par J.E. Heighway. Cette
en ajoutant des étapes. Figure 3 courbe fractale ne se recoupe jamais. En 1967, Martin
Il existe des variantes du flocon de von Koch, obtenues en Gardner l'a présentée dans sa chronique de jeux mathéma-
modifiant les valeurs des angles du triangle ou bien la figure tiques du Scientific American, car sa construction est simple.
géométrique de base, et même en 2D (surfaces de von À chaque étape, la courbe obtenue est composée de seg-
Koch). ments de droites qui suivent à angle droit. Au départ, il n’y a
qu’un seul segment. Pour passer d’une étape à la suivante,
POUR ALLER PLUS LOIN en suivant la courbe, il s’agit de remplacer chaque segment
Voici deux exemples supplémentaires de courbes fractales, la rencontré par deux segments à angle droit en effectuant une
courbe de Hilbert et la courbe du dragon, dont il est possible rotation de 45° alternativement à droite puis à gauche.
de tracer des approximations à l’aide de la bibliothèque turtle. Figure 5
1 an de Programmez!
ABONNEMENT PDF : 39 €
Abonnez-vous directement sur
[Link]
[Link] 71
072_075.qxp_248 18/10/2021 16:55 Page72
PROGRAMMATION DYNAMIQUE
Python est un langage fortement dynamique. Mais, dans les faits, qu’est-ce que cela
signifie ? Qu’est-ce que cela peut nous apporter ? Grâce à ce dynamisme, Python per-
Philippe met de résoudre des problèmes de manière élégante et compacte là où les langages
BOULANGER
Manager des expertises classiques nécessiteraient beaucoup de codes.
C/C++ et Python
[Link] DÉFINITION détectés à la compilation plutôt qu’à l’exécution.
Si on se réfère à Wikipédia ([Link] En effet dans les langages statiques (Java, C ou C++ par
Langage_de_programmation_dynamique), la programmation dyna- exemple), les variables sont déclarées et typées avant d’être
mique est définie par : utilisées permettant au compilateur de faire nombre de tests
• On utilise le terme langage de programmation dyna- avant de générer le binaire. Par exemple si on prend le petit
mique en informatique pour décrire une classe de langage programme suivant :
de haut niveau qui exécute au moment de l'exécution des
double linear_interpolation( double a, double b, double x )
actions que d'autres langages ne peuvent exécuter que
{
durant la compilation. Ces actions peuvent inclure des
double v = ( 1 - x ) * a + x * b;
extensions du programme, en ajoutant du code nouveau,
return v;
en étendant des structures de données et en modifiant le
}
système de types, cela pendant l'exécution du programme.
Ces comportements peuvent être émulés dans pratique-
ment tous les langages de complexité suffisante, mais les
int main()
langages dynamiques ne comportent pas de barrière, tel
{
que le typage statique, empêchant d'obtenir directement
// appel correct qui compilera
ces comportements.
double r1 = linear_interpolation( 10, 20, 0.5 ); // appel correct
72 [Link]
072_075.qxp_248 18/10/2021 16:55 Page73
variables sont associées à un espace mémoire par une adres- Cela peut sembler peu de chose, mais c’est extrêmement
se et une taille dépendant du type. L’accès à la donnée est utile : le module argparse en fait usage pour retourner args :
donc direct. Dans le cadre du typage dynamique, on doit
import argparse
passer par un « dictionnaire » qui lie le nom de la variable à
un espace mémoire alloué (dynamiquement) : l’accès est
parser = [Link]( description = 'Process some integers.' )
donc le coût d’une recherche dans cette structure de donnée.
parser.add_argument( 'integers',
metavar = 'N',
Création dynamique de variables type = int,
Python ne dispose pas que d’un typage dynamique, il permet
nargs = '+',
aussi de créer des variables à la volée. En effet, nous avons
help = 'an integer for the accumulator' )
un accès direct aux dictionnaires des variables globales ou
parser.add_argument( '--sum',
locales :
dest = 'accumulate',
• globals() : retourne le dictionnaire des variables globales
action = 'store_const',
• locals() : retourne le dictionnaire des variables locales
const = sum,
Avec le code suivant :
default = max,
names = "xyz" help = 'sum the integers (default: find the max)' )
values = [ 1, "toto", [ 1, 2, 3 ] ]
d = globals() args = parser.parse_args()
# connexion
[Link]=[Link]
[Link]=8080
[Link]=zeus
[Link]=jupiter
[Link]=MY_DB
Ajout de variables à un objet [Link]=aphrodite
En python, tout est objet. Et les objets peuvent être étendu
[Link]=venus
par ajout d’attributs. Nous disposons des fonctions suivantes
pour manipuler la structure interne des objets Python :
# directories
• def hasattr( obj, attr_name )
[Link]=C\Temp\log
Le résultat est True si la chaîne attr_name est le nom d’un
[Link]=C:\Temp\input
des attributs de l’objet, sinon False. L’implémentation
[Link]=C:\Temp\output
appelle getattr(object, name) et regarde si une exception
AttributeError a été levée. Avec le programme suivant, nous allons pouvoir lire ce fichier
• def getattr( obj, attr_name [, default ] ) et nous servir des valeurs lues :
Retourne la valeur de l’attribut associé au nom dans Code complet sur [Link] & github
attr_name de l’objet obj. attr_name doit être une chaîne de La fonction « load_file » lit le fichier ligne par ligne et la fonction
caractères. Si la chaîne est le nom d’un des attributs de « add_variable » crée les variables et les attributs à la volée.
l’objet, le résultat est la valeur de cet attribut. Par exemple,
getattr(x, 'foobar') est équivalent à [Link]. Si l’attribut EVALUATION DYNAMIQUE
n’existe pas, et que default est fourni, il est renvoyé, sinon Nous avons appris à créer des variables et des attributs dyna-
l’exception AttributeError est levée. miquement. Mais est-on capable d’évaluer des formules ou
• def setattr( obj, attr_name, attr_value ) du code dynamiquement ? Si j’ai une formule récupérée
C’est la fonction complémentaire de getattr. Les arguments dans un formulaire, puis-je en évaluer la valeur en fonction ?
sont : un objet Python, une chaîne de caractères, et une
valeur à associer à l’attribut. La chaîne peut nommer un Évaluation de formule
attribut existant ou un nouvel attribut. La fonction assigne C’est en 2000 que j’ai eu la première fois besoin d’évaluer
la valeur à l’attribut, si l’objet l’autorise. Par exemple, setat- dynamiquement des formules. Je travaillais sur un logiciel de
tr(x, 'foobar', 123) équivaut à [Link] = 123. CAO et je créais un plug-in en C++ qui allait permettre de
[Link] 73
072_075.qxp_248 18/10/2021 16:55 Page74
créer des objets 3D à partir d’équations paramétriques saisies tions. Cela permet de construire dynamiquement des fonc-
par un utilisateur dans une fenêtre de l’application. Créer une tions spécialisées ou des fonctions permettant de modifier
telle fonctionnalité m’avait demandé beaucoup de codes, des fonctions existantes via le mécanisme des décorateurs.
mais en Python cela s’avère beaucoup plus facile ; en effet, Supposons que nous souhaitions rajouter une fonctionnalité
il existe une fonction « eval » qui facilite la tâche : de logging :
h = ( b - a ) / nb return wrapper
for i in range( nb + 1 ):
x=a+i*h @log
print( "f(", x, ") =", eval( formula ) ) def compute( x ):
En l’exécutant, on obtient : return x*x*x
formule?x*x
a?0 print( compute(3) )
b?1
Dans ce cas-là, « wrapper » dépend de la fonction passé en
nb?10
paramètre de la fonction « log » et la fonction paramétrique
f( 0.0 ) = 0.0
est créée en appliquant « @log » à la fonction « compute ».
f( 0.1 ) = 0.010000000000000002
f( 0.2 ) = 0.04000000000000001
Création de module
f( 0.30000000000000004 ) = 0.09000000000000002
Un module peut être un simple fichier Python. Et la comman-
f( 0.4 ) = 0.16000000000000003
de « import » permet de charger un module. La question que
f( 0.5 ) = 0.25
l’on peut se poser est : peut-on créer dynamiquement un
f( 0.6000000000000001 ) = 0.3600000000000001
module et le charger ?
f( 0.7000000000000001 ) = 0.4900000000000001
À quoi pourrait servir ce type de fonctionnalité me direz-
f( 0.8 ) = 0.6400000000000001
vous ? Plaçons-nous dans un contexte embarqué avec des
f( 0.9 ) = 0.81
ressources limitées comme un petit robot piloté par un pro-
f( 1.0 ) = 1.0
gramme Python. Notre seul moyen de communication avec
Les fonctions paramétriques notre robot est via un réseau non-filaire (un wifi par
En Python, une fonction est un objet comme un autre : nous exemple). La tâche que doit accomplir notre robot est pro-
pouvons créer des alias à des fonctions et nous pouvons grammée dans un module appelé « mission ». Sa tâche ter-
retourner une fonction d’une fonction. Cette fonctionnalité minée le robot a encore de l’autonomie. Il nous faut donc
peut sembler obscure, au premier abord, et peu utile, mais mettre à jour sa mission :
c’est, en fait, une fonctionnalité extrêmement utile de • envoyer un fichier texte contenant la nouvelle mission
Python : elle est notamment à la base des décorateurs. • recharger le module via un appel à [Link]
Prenons l’exemple suivant : • exécuter la fonction principale du module « mission »
Essayons le code suivant :
def f( a ):
def _f( x ):
import importlib
return x + a
return _f
while True:
formula = input( "f(x)=" )
h = f(3)
if len(formula) == 0:
print( h, end='\n\n' )
break
for i in range(5):
texte = F"""
print( h(i) )
from math import *
Lorsque l’on fait « h=f(3) », h est une fonction : <function
f.<locals>._f at 0x0000023C1B386DC0>. Et dans la def f(x):
boucle, le programme affiche bien la valeur i+3 à chaque return {formula}
itération. « f » est une fonction qui crée une nouvelle fonction """
(que l’on stocke dans « h ») qui dépend des paramètres de
« f ». Certes c’est moins dynamique que la fonction « eval », with open( "my_module.py", "w" ) as file:
mais cela permet d’adapter du code en fonction de condi- [Link]( texte )
74 [Link]
072_075.qxp_248 18/10/2021 16:55 Page75
[Link] 75
076_080.qxp_249 18/10/2021 16:55 Page76
76 [Link]
076_080.qxp_249 18/10/2021 16:55 Page77
println(nullable2?.length) // null
En effet, pas besoin de déclarer les setteurs et getteurs, ils sont générés
automatiquement grâce aux mot-clés val et var. Et comme illustré
//2. Pas Nullsafe: Infération avec NullPointerException (NPE)
précédemment, les propriétés de classe sont publiques par défaut.
val infTest = test() // Inferred type: String (by default)
/ Java println([Link]) // 15
public class GetSet {
private String readOnly = "Only getter defined"; val infTestNull = testNull() // Inferred type: String (by default)
private String writeOnly = "Only setter defined"; println([Link]) // NullPointerException
private String readWrite = "Both defined";
Toutefois, l'interopérabilité entre les deux langages introduit des
problèmes de mapping et de gestion de null pour les objets prove-
public String getReadOnly() { return readOnly; }
nant de code Java.
En règle générale, les types primitifs (int, boolean, long,..) sont
public void setWriteOnly(String writeOnly) { [Link] = writeOnly; }
associés par le compilateur au type Kotlin non null, en l'occurrence
([Link], [Link], [Link],...), définis au sein de la
public String getReadWrite() { return readWrite; }
librairie standard de Kotlin. Mais les types non-primitifs (Integer,
public void setReadWrite(String readWrite) { [Link] = readWrite; }
Boolean, Long, String..), seront traduits, sans déclaration explicite,
}
en leur équivalent Kotlin non-null. Chose qui peut créer un NPE tel
qu'illustré par l'infération par le compilateur du type String, au lieu du
// Kotlin
type null-safe : String?. Il est ainsi fortement recommandé d'expliciter
private val gs = GetSet()
le type retourné à l'invocation d'une méthode Java. Cependant,
println([Link]) // Read-only attribute acts like a val property
avoir à expliciter le type ou encore traiter toutes les valeurs comme
éventuellement nullable avec l'opérateur ? peut rapidement encom-
[Link] = "I have both" // Read-write attribute acts like a var propertyprintln
brer le code avec des vérifications inutiles. Pour réduire ce besoin,
([Link])
Kotlin supporte les annotations @Nullable et @NotNull prévenant
de la JSR 305, ou encore @NonNull de la JSR 308 pour les types
[Link]("No getter") // Write-only properties not supported in Kotlin
génériques.
Cette conversion automatique vers une propriété de classe Kotlin
// Java
fonctionne du moment que la classe Java fournit une méthode sans
public static @Nullable String nullable() { return null; }
paramètres commençant par get et éventuellement une méthode à
public static @NotNull String nonNull() { return "Could be null, but with warning"; }
paramètre unique commençant par set. Cela fonctionne également
pour les expressions Boolean où le getter commence par it au lieu
// Kotlin
de get, mais il n'y a actuellement aucun mécanisme de ce type si
val s1 = nullable() // Inferred type: String?
elles commencent par has ou d'autres préfixes. Les propriétés de
val s2 = nonNull() // Inferred type: String
type WriteOnly (écriture seule) ne sont actuellement pas supportées
par Kotlin, raison pour laquelle setWriteOnly ne peut être appelée Point important: Les objets annotés @NotNull en Java et qui contien-
avec une propriété de syntaxe comme étant un WriteOnly. nent une référence nulle par erreur pourront toujours causer des
NullPointerException en Kotlin. C'est pour cette raison qu'il est néces-
Gestion de null saire de redoubler de vigilance sur les inférences de type en intégrant
Kotlin a pour avantage une gestion avancée des valeurs nulles. les deux langages. Pour finir, on notera que les IDE tels qu’Android
Ceci offre aux programmes implémentés dans ce langage la carac- Studio et IntelliJ peuvent afficher des warnings dès lors que les anno-
téristique de null-safety. Cette caractéristique est l'une des raisons tations JSR 305/308 sont utilisées dans le code Java intégré. Cela per-
principales pour laquelle Kotlin est devenu à ce jour le langage pré- met d'anticiper les risques, voire les besoins de gestion de null-safety.
féré pour la réalisation de solutions mobiles, offrant par ailleurs le
même avantage qu'en programmation iOS via Swift. Vous l'aurez Conflits d'identifiant syntaxiques
compris, moins de NullPointerException, moins de crashs ! Les mot-clés réservés sont un autre point clé à garder dans le viseur.
Par exemple, l'opérateur ? permet d'indiquer qu'une valeur peut être Comme on a pu le voir dans les précédents exemples, Kotlin possède
éventuellement nulle et ainsi en sécuriser l'accès. un ensemble de keywords syntaxiques: val, var, when, fun... Ces mot-clés
déclaratifs en Kotlin ne le sont pas en Java, du moins pas dans cer-
// Java taines versions. Il est ainsi possible en Java, par exemple dans la ver-
public static String test() { return "Je peux être null"; } sion 8, de déclarer une variable portant le même nom. L'exemple ci-
public static String testNull() { return null; } dessous l'illustre :
//Kotlin // Java
class KeywordsAsIdentifiers {
//1. Nullsafe: Gestion explicit de null, public int val = 100;
val test: String? = test() // Explicit type: String? public Object object = new Object();
println(nullable?.length) // 15 public boolean in(List<Integer> list) { return true; }
public void fun() { [Link]("This is fun."); }
val testNull: String? = testNull() // Explicit type: String? }
[Link] 77
076_080.qxp_249 18/10/2021 16:55 Page78
Pour les invoquer en Kotlin, il est nécessaire d'utiliser des backticks (`).
val result = value1 + value2 - value3 // Uses ‘plus’ and ‘minus’ as operators
// Kotlin println([Link]) // 42
val kai = KeywordsAsIdentifiers()
Du code Kotlin depuis du code Java
println(kai.`val`) // 100
Il est tout aussi possible d'embarquer une librairie de code Kotlin au
println(kai.`object`) // [Link]
sein d'un projet Java. Bien que ce sens soit moins conventionnel, il
println(kai.`in`(listOf(1, 2, 3))) // true
n'en est cependant pas dénué d'intérêt. Vous pouvez par exemple
println(kai.`fun`()) // This is fun.
avoir un projet Legacy Android écrit en Java dans lequel vous vou-
Pas de panique, dans la mesure où ce cas de figure devrait se pro- lez réutiliser un module écrit pour un projet plus récent réalisé en
duire, certains IDEs, tels qu'Intellij, intègrent une gestion de ces Kotlin, chose qui vous permet de capitaliser sur une base de code
mots-clés. Ainsi, les backsticks seront rajoutés automatiquement. existante.
Le compilateur de Kotlin traduit toutes les instructions en Java
Gestion de SAM avant de les compiler en bytecode JVM. Au sein de l'IDE Intellij
En Java, on définit depuis la version 8, une interface fonctionnelle IDEA, Il est possible de voir comment un fichier Kotlin sera traduit
comme une interface contenant une seule méthode abstraite. Les en Java. Pour le faire, il suffit d'ouvrir le raccourci de commande
interfaces fonctionnelles sont la base des expressions lambda. On (Shift+Shift) et de saisir 'skb' (Show Kotlin Bytecode), puis de cli-
peut énumérer différentes interfaces dans la librairie standard de quer sur “Décomplier”.
Java : Comparator, Consumer, Collector. Code complet sur [Link] & github
Une simple déclaration data class Personne(...) en Kotlin va ainsi être tra-
// Java
duite en une classe Java intégrant:
interface Producer<T> { // SAM interface (single abstract method)
• Les getters et setters des propriétés de classe : On y verra la créa-
T produce();
tion de getters et setters pour les propriétés en var (i.e age), et que
}
des getters pour les propriétés déclarées en val. En plus, le com-
pilateur rajoute le modificateur final pour les propriétés immuables
// Kotlin
nom et adresse.
private val creator = Producer { BigDecimal(9000) }
• La gestion de hashcode, equals, copy et toString, offrant ainsi
// Inferred type: Producer<BigDecimal>
une implémentation par défaut de ces fonctions.
L'instrumentalisation de ce type d'interface depuis un code Kotlin, • Les annotations de la JSR 305 illustrant la gestion des nulls, tel
ne nécessite pas la création d'une inner-classe ou encore d'un objet. que décrit dans la section précédente.
Il suffit simplement d'invoquer une Lambda telle qu'illustrée dans • Les appels à Intrinsics, insérés par le compilateur Kotlin et per-
l'exemple ci-dessus. Et comme on le constate, la syntaxe est plus mettant de faire des vérifications supplémentaires au runtime.
légère avec l'inférence de type en prime. • La création de méthodes dites synthétiques. Celles-ci permettent de
gérer les invocations depuis Kotlin, tel que l'appel du constructeur
Operators functions Personne sans fournir l'âge qui prend 10 comme valeur par défaut,
Par conception, la surcharge d'opérateurs arithmétiques en Java chose qui n'est pas supportée par Java.
n'est pas supportée. Il n'est pas possible de soustraire ou bien d'ad- Ceci est un aperçu du nombre de lignes de code que l'on peut écono-
ditionner des Objets. Mais dans les langages JVM modernes tels miser à l'aide de Kotlin. On peut désormais comprendre que certains
que Scala ou Kotlin, c'est une pratique courante. Le plus amusant concepts propres à Kotlin n'ont pas nativement leur pareil en Java (i.e
dans tout ça, est que dans le cadre de l'interopérabilité entre Java extensions, file-level functions, file-level class, reified inline func-
et Kotlin, on peut conserver la même richesse syntaxique en mani- tions...), que le code Java généré peut parfois s'avérer faiblement opti-
pulant des Objets Java depuis Kotlin. Par exemple, Il suffit de définir misé, et malheureusement engendrer des collisions de noms.
au sein de la classe Java les méthodes dites operator telles que plus, Dans ce dernier cas, quand on passe d'un univers Kotlin où il est
minus, inc ou div. Cela permet ainsi d'appliquer depuis Kotlin des opé- possible de créer plusieurs classes dans un même fichier, à un uni-
rateurs arithmétiques aux instances de l'objet Java. vers Java dénué de cette flexibilité, on constatera en fonction des
cas que des classes sont générées avec des noms par défaut.
// Java
public class Box { // [Link]
private final int value; package [Link]
public Box(int value) { [Link] = value; } class FileLevelClass // génère la classe [Link]
public Box plus(Box other) { return new Box([Link] + [Link]); } object FileLevelObject // génère [Link] en tant
public Box minus(Box other) { return new Box([Link] - [Link]); } que Singleton
public int getValue() { return value; } fun fileLevelFunction() {} // s'insère dans la classe générée
} SampleNameKt
val fileLevelVariable = "Usable from Java" // s'insère dans la classe
// Kotlin générée SampleNameKt
val value1 = Box(19)
Mais pour notre plus grand bonheur, Kotlin vient avec un ensemble
val value2 = Box(37)
de moyens et d'annotations pour contribuer à influencer le code
val value3 = Box(14)
Java généré.
78 [Link]
076_080.qxp_249 18/10/2021 16:55 Page79
[Link] 79
076_080.qxp_249 18/10/2021 16:55 Page80
@Throws • Les autres visibilités ne peuvent pas être mappées directement, mais
En Kotlin, les exceptions rejetées par une méthode ne sont pas décla- sont plutôt compilées selon la correspondance la plus proche :
rables au niveau de la signature. Contrairement à java, il n'y pas de • Les déclarations privées de haut niveau restent également pri-
clause Throws. Le bytecode JVM généré ne contient pas d'informa- vées. Cependant, pour autoriser les appels depuis le même fichier
tion permettant de gérer les exceptions côté Java de façon pro-acti- Kotlin (qui peut être une classe différente en Java), des méthodes
ve. Ce qui pourrait créer quelques effets indésirables pour le code synthétiques sont générées sur la JVM. De telles méthodes ne
appelant côté Java. C'est là que l'annotation Throws entre en jeu. peuvent pas être appelées directement, mais sont générées pour
Code complet sur [Link] & github transférer des appels qui ne seraient pas autrement possibles.
Comme illustré dans l'exemple ci-dessus, l'intégration de l'annota- Toutes les déclarations internal de Kotlin deviennent publiques, car
tion Throws, permet de rajouter la clause Throws au niveau de la package-private serait trop restrictif. Celles qui sont déclarées à l'in-
signature de la méthode dans le bytecode Java. Ainsi, côté java, il térieur d'une classe subissent une modification des noms pour éviter
devient obligatoire de gérer l'exception déclarée. Dans l'exemple ci- les appels accidentels de Java. Par exemple, une méthode interne
dessus, nous avons déclaré une seule exception, toutefois l'annota- [Link] apparaîtra comme [Link]$module() dans le bytecode, mais
tion peut recevoir une liste d'exceptions si nécessaire. elles ne sont pas invocables en tant que telles. Il est nécessaire
d’utiliser @JvmName pour modifier le nom dans le bytecode Java
Inline Functions et ainsi rendre l’appel possible.
Il est possible d'appeler des inline functions à partir de Java comme
n'importe quelle autre fonction, mais bien sûr, elles ne sont pas réel- Considérations supplémentaires
lement intégrées - une telle fonctionnalité n'existe pas dans Java. Les méthodes qui émettent une exception vérifiée en Java peuvent
L'exemple ci-dessous montre l'inlining lorsqu'il est utilisé depuis être appelées depuis Kotlin sans avoir à gérer l'exception. Ce
Kotlin. Il faut savoir que les inline functions avec des paramètres de concept (throws) n'existe pas en Kotlin et nécessite de redoubler de
type réifié ne peuvent pas du tout être appelées à partir de Java, car vigilance lors de l'intégration d'une librairie Java où certaines
elles ne prennent pas en charge l'inlining. Ainsi, il n'est pas possible méthodes déclarent dans leur signature des exceptions faisant par-
d'utiliser de paramètres de type réifié dans des méthodes qui tie ainsi du contrat d'API.
devraient être utilisables à partir de Java. Vous pouvez récupérer la classe Java d'un objet comme ceci
Code complet sur [Link] & github UnObjet::[Link] ou encore [Link]
Vous pouvez utiliser la réflexion sur les classes Java depuis Kotlin et
KClass Kotlin utiliser une référence à la classe Java comme point d'entrée. Par
KClass est la représentation Kotlin de l'objet Class Java. Elle fournit exemple, UnObjet::[Link] renvoie les
des capacités de réflexion. Pour une fonction Kotlin que l'on souhai- méthodes déclarées de la classe UnObjet.
terait appeler depuis Java et acceptant une Kclass, il sera possible L'héritage fonctionne de façon similaire entre Kotlin et Java; les
d'utiliser la classe prédéfinie [Link] comme deux ne prennent en charge qu'une seule superclass (abstraite ou
illustré dans l'exemple ci-dessous. Depuis Kotlin, on peut accéder non), mais n'importe quel nombre d'interfaces à implémenter.
plus facilement tant à l'objet Kclass que Class. Nothing: il n'y a pas d'équivalent au type Nothing de Kotlin en Java,
car même [Link] accepte null comme valeur. Comme il
// Java
s'agit toujours de la représentation la plus proche de Nothing dis-
import [Link];
ponible en Java, les types et paramètres de retour Nothing sont
import [Link];
mappés à Void. L'utilisation de Nothing comme argument de type
générique génère un type brut en Java pour au moins provoquer
KClass<A> clazz = [Link]([Link]);
des avertissements d'appel non contrôlés. Par exemple,
List<Nothing> devient une liste brute (List) en Java.
// Kotlin
Kotlin mappe non seulement les tableaux primitifs à leurs types
import [Link]
mappés correspondants (IntArray, LongArray, CharArray, etc.) et
vice versa, il n'encourt également aucun coût de performance par
private val kclass: KClass<A> = A::class
rapport à Java lorsque vous les utilisez. Certains des exemples de
private val jclass: Class<A> = A::[Link]
code référencés dans cet article ont été récupérés de l'excellent livre
Visibilité de Peter Sommerhoff, Kotlin for Android App Development. Un livre
Les déclarations de visibilité (private, protected, public) ne corres- qui aidera les lecteurs plus avancés à explorer plus de détails sur l'in-
pondent pas exactement entre Kotlin et Java, et en plus il existe des teropérabilité entre ces deux langages, qui à ce jour, est la plus
déclarations de niveau supérieur au sein Kotlin. Voyons donc com- grande force de Kotlin par rapport aux autres langages JVM.
ment les visibilités sont présentées sur Java. Premièrement, cer-
taines visibilités peuvent être mappées comme ci-dessous : Références
• Les membres privés restent privés et accessibles qu’au niveau de [Link]
la classe qui les déclare [Link]
• Les membres protégés restent protégés, au-delà de la classe [Link]
déclarante, ne sont accessibles que par les classes héritants et [Link]
présentes dans le même package. [Link]
• Les éléments à visibilité publique restent publiques sont acces- java-interoperability
sibles sans restriction. [Link]
80 [Link]
081_082.qxp_249 19/10/2021 17:12 Page81
[Link] 81
081_082.qxp_249 19/10/2021 17:12 Page82
82 [Link]
083.qxp_249 18/10/2021 16:56 Page83
084.qxp_249 18/10/2021 16:56 Page84