Les 8 et 9 février 2024 se tenait Touraine Tech, l’événement spécialisé tech de la région. Au programme, des dizaines de speakeurs/euses d’univers différents : Web, Mobile, Craft, Soft-Skills… Ippon y a brillé avec pas moins de 6 speakeurs, un stand bien animé avec le fameux Burger Quiz revisité et une participation en tant que sponsor. Nous allons résumer chacun la conférence qui nous a le plus marquée. Nous serons trois Ippon : Thomas, développeur mobile Android, Julien, développeur Web et Dylan, développeur mobile iOS.
Les classes abstraites c’est fini (et c’est la faute à TDD)
par Dorra BARTAGUIZ , résumé par Thomas Boutin
Une classe abstraite, c’est une classe qu’on ne peut pas instancier. Pourquoi est-ce qu’on l’utilise ? Pour deux raisons principales :
- Pour factoriser du code entre deux classes. Le principe est le suivant : le code commun est mis dans la classe abstraite, qui est la classe parente, et le code spécifique dans les classes enfants.
- Pour partager un même contrat entre des classes qui sont analogues. Dans ce cas, la classe abstraite, toujours parente, définit une méthode à implémenter, et les classes enfants l’implémentent. Prenons cet exemple montré par Dorra :
Ici, pour calculer le montant d’une commande dans le cadre d’un concessionnaire qui se situe en France et au Luxembourg, on fait implémenter à nos classes FrenchOrder et LuxembourgishOrder une interface IOrder qui contient une méthode CalculateAmount. Le calcul de TVA diffère entre les pays.
Le TDD et les classes abstraites
Après avoir rappelé ce qu’était qu’une classe abstraite, Dorra prend un exemple concret de développement d’un algorithme. Elle le fait en TDD, cette pratique de développement qui consiste en une succession de cycles rouge / vert / refactor. On crée un test qui spécifie un comportement attendu (red), on développe la solution minimale qui fait passer ce test (green) et ensuite on refactor.
source: Neo-Soft
Ce que Dorra explique, c’est que lorsqu’on développe en TDD, et en particulier dans les phases de refactor, on va régulièrement être tenté de factoriser du code. Le design pattern template method utilise justement des classes abstraites pour réusiner notre code.
On crée donc une classe abstraite BaseOrder qui contient la logique commune entre nos classes enfants. Les classes enfants implémentent le spécifique.
Continuer d’ajouter des fonctionnalités avec les classes abstraites
Dorra continue sa démonstration en ajoutant d’autres fonctionnalités. Mais rapidement, ça coince. Admettons que nous souhaitions ajouter les USA dans l’équation. Son calcul de prix se fait bien avec un montant hors taxe et un calcul de TVA, mais on y ajoute une taxe forfaitaire de 10%. Par défaut, on est tenté de mettre une condition dans la classe abstraite :
En améliorant un peu cette solution, on redéfinit la fonction calculateTotal dans USAPrice :
Mais dans ce cas, cela veut dire qu’on est contre le L de SOLID. Le principe de substitution de Liskov. Pour faire simple, une classe enfant doit pouvoir être substituée par une autre sans que le contrat de l’interface change. Dans le cas présent, le contrat est brisé.
Autre problème : pour vérifier que ce contrat est bien respecté, il faut faire autant de tests qu’il y a de classes enfants puisqu’on ne peut pas instancier BasePrice. C’est soutenable s’il y peu d’enfants. Mais que dire s’il y en a une dizaine ?
Alors comment faire ? Dorra nous montre qu’une autre voie est possible, toujours au travers du TDD : l’utilisation de la composition. Plutôt qu’hériter d’un parent, considérons un TotalCalculator par défaut et un second qui prendrait en compte les pourboires (pour les USA) :
On passe chaque Price par constructeur. Ce principe, si vous l’avez reconnu, c’est le D de SOLID : l’inversion de dépendances. Autre avantage de l’approche : on utilise uniquement ce dont on a besoin. Pas de notions de pourboires dans le calcul du prix français ou luxembourgeois. Les tests sont aussi simplifiés là où on voyait rapidement les limites avec les classes abstraites.
En résumé
À travers sa présentation, Dorra montre que lorsque c’est dur de tester, c’est sûrement le mauvais chemin. TDD brille dans ce genre d’implémentation exploratoire, avec des cas d’utilisation qui s’ajoutent et se complexifient au fur et à mesure. La composition est plus que jamais meilleure que l’héritage. Là aussi, c’est un autre principe de développement 😛.
Super talk, merci Dorra !
PS: pour les curieux, elle est co-autrice du livre Software Craft, une bonne introduction à plein de concept de software craftsmanship. Jetez-y un oeil 😉
🧑🎤🎸 La preuve de programme vous fera apprécier les tests
par William Bartlett, résumé par Julien Message
À l'opposé de l'artisanat, il y a l'industrie, et dans l'industrie, il y a des normes. Dans sa conférence, William Bartlett nous a présenté différentes façons de répondre à ces normes dans le cadre du développement informatique.
Les systèmes informatiques peuvent être évalués selon sept Niveaux d'Assurance d'Évaluation (EAL). Ces niveaux correspondent aux différents critères de conception et de tests à appliquer à notre système et vont de simples tests fonctionnels (EAL1) à une conception vérifiée de façon formelle et testée (EAL7).
À titre d'exemple, la plupart des applications civiles demanderont un EAL compris entre 1 et 4, là où les applications plus critiques (transports, énergie, …) ou militaires demanderont un EAL plus élevé.
La suite de la conférence nous a montré différents types de tests, du plus simple test unitaire à la plus complexe preuve de programme.
Tests par cas
C'est la manière classique de tester un programme ou une fonction : on crée un ensemble de cas d'utilisation précis, on appelle la fonction avec chacun de ces cas avant de vérifier que le résultat est correct.
Exemple avec une fonction triant une liste :
- [] → []
- [2, 3, 1] → [1, 2, 3]
Le choix des cas de tests devient vite complexe car il faut couvrir tous les cas possibles sans être redondant. C'est là que les tests par propriétés interviennent.
Tests par propriétés
Une méthode de tests plus avancée consiste à trouver des propriétés sur le résultat et à laisser le moteur de tests générer les paramètres d'entrée pour vérifier le bon fonctionnement du programme.
Pour notre algorithme de tri, on doit vérifier seulement deux propriétés :
- le résultat est une liste constituée des mêmes éléments que la liste de départ
- la résultat est une liste triée
Note : le moteur de tests crée également de nombreux cas limites en plus des cas de bases.
Pour aller plus loin, voici un article décrivant les tests par propriété plus en détails : Property-based Testing ou l'insuffisance des tests unitaires
Mais la conférence n'était pas destinée à nous parler de simples tests automatisés. Les plus hauts niveaux d'EAL exigent quelque chose de bien plus fort : la preuve de programme.
La preuve de programme
C'est une méthode consistant à démontrer la fiabilité d'un programme à l'aide d'outils mathématiques. Elle permet de prouver la conformité du programme aux exigences fonctionnelles et de garantir l'absence de bugs. Le logiciel Coq et le langage Gallina permettent ce type de preuves.
Coq est un assistant de preuves développé dans les années 1980 pour informatiser la démonstration de théorèmes en mathématiques. Il permet de définir des méthodes dans un langage fonctionnel et de démontrer que celles-ci fonctionnent par des outils mathématiques.
Note : La page Wikipédia contient des exemples d'utilisation de Coq dont une démonstration (relativement simple) que tout nombre entier est soit pair, soit impair.
L'effort requis pour mettre en place ce type de preuve conjugué à l'utilisation d'un autre langage peut être assez décourageant. D'autant plus que de nombreuses questions se posent : comment intégrer Coq dans un système informatique ? Comment intégrer des preuves de programme dans des bases de code Python ou C ?
La difficulté à écrire des preuves de programmes et à les intégrer dans des logiciels est probablement ce qui explique le titre de la conférence : la complexité de la preuve de programme nous fera apprécier la simplicité des tests.
(Re)devenez pote avec le CSS.
par Olivier Leplus et Yohan Lasorsa, abrégé par Dylan Le Hir
Nous avons tous subi la période COVID de plein fouet avec son port du masque, son pass sanitaire et son lot de contraintes en tout genre pour la plupart basées sur l'interdiction formelle de mettre les pieds dehors. Obligé de rester enfermé, un florilège d'activités a pu voir le jour comme par exemple l'explosion des streamers sur Twitch ou encore l'envolée spectaculaire de coaching virtuel en tout genre (Coucou Cyril Lignac, merci pour "Tous en cuisine"). La programmation n'a pu échapper à ce phénomène planétaire : avez-vous déjà entendu parler de ces petits challenges CSS qui ont fleuri sur internet ? Créer une illustration avec une seule balise Div ? Faire un dessin animé en CSS ? Toutes ces filouteries auront permis de redonner un gros coup de boost au langage CSS. Les développeurs ont ressorti toute une flopée de nouvelles fonctionnalités, oui oui vous m'avez bien lu, de nombreuses, de très nombreuses… Bon ok : Trop de nouveautés. C'est pour ça qu'ici nous allons nous concentrer sur ce qui nous a été présenté lors de Touraine Tech 2024 : Let's go…
Les custom properties
On déclare une custom property à l'aide de deux tirets : - -.
On l'utilise avec var().
L'objectif est d'éviter une duplication de code dans notre projet.
:root{
--red: #ff0000
}
Body {
backgroud-color: var(--red);
}
Les custom properties implémente un système de fallback :
.element {
background-color: var(--color-primary, red);
}
Il est possible de récupérer des custom properties via du JavaScript :
element.Style.getPropertyValue("--my-var");
💡 Le saviez-vous ? Il est maintenant possible de faire du Nesting avec CSS.
Color-mix()
Cette classe permet, comme son nom l’indique, de mixer des couleurs afin d’obtenir des teintes plus claires, plus sombres ou encore une opacité différente :
:root{
--base-color: #2898e2;
--color-light: color-mix(in srgb, var(--base-color) 20%, white 100%);
--color-dark: color-mix(in srgb, var(--base-color) 80%, black 30%);
--color-opacity: color-mix(in srgb, var(--base-color) 50%, rgba(0,0,0,0) 50%);
--color-mix: color-mix(in srgb, var(--base-color) 50%, red 50%);
}
Les Media Queries Range
On connaît tous les media queries range avec une écriture classique :
@media (width 750px){
/* Au dessus ou en dessous ? */
}
Maintenant c’est plus explicite !
@media (width <= 750px){
/* Cool, merci ! Maintenant c'est clair ! */
}
Les Container Queries
Les container queries sont considérés comme l’évolution des media queries. Là où les média queries classiques se basent sur le viewport, les containers queries eux se basent sur le composant parent.
- Media Query :
@media (max-width: 30em){
}
- Container Query :
@container (width > 15em){
}
Voici un exemple d'utilisation : https://codepen.io/mxbck/pen/XWMrMOp
Viewports
De base on utilise des vh (viewport height) ou des vw (viewport width), unités de mesure basé sur le viewport en question. Problème : si on change la taille du device, ça casse…
Du coup maintenant il est possible d'utiliser des SVH (small view height) ou des SVW (small view width) mais encore plus puissant il existe des DVH (dynamic view height).
Voici un exemple d'utilisation : https://dev.to/frehner/css-vh-dvh-lvh-svh-and-vw-units-27k4
Animation-timeline
Grâce à cette propriété CSS, nous allons pouvoir ajouter du style à notre page en fonction du scroll. Comme nous allons le voir dans l'exemple suivant, le but étant de pouvoir jouer avec l'animation, nous pouvons lui dire quand l'animation commence et quand elle finit. Pour utiliser animation-timeline, il faut déclarer une propriété animation au préalable.
mark {
animation: highlight linear forwards;
animation-timeline: view(60% 1%);
background: linear-gradient(
to right,
oklch(0.86 0.19 84.89 / 1) 50%,
oklch(0.86 0.19 84.89 / 0) 50%
);
background-position: 100%;
background-size: 200% 100%;
border-radius: 0.125rem;
padding-inline: 0.125rem;
}
Ici on constate qu'animation-timeline commence à 20% d'apparition du texte et se termine à 60%.
Voici un exemple d'utilisation : https://codepen.io/pen/ExJmNoB
View-transition
La view-transition c'est tout simplement le passage d'une page à l'autre avec une animation. Grâce à cette nouvelle propriété, nous allons pouvoir modifier la transition à notre guise.
Avec l'ajout d'un simple bout de code en JavaScript, nous allons pouvoir reprendre la main sur l'animation :
document.startViewTransition(() => {
})
Ce code JavaScript va permettre au navigateur de faire un snapshot de ma page actuelle. Il va aller charger la page suivante, faire un snapshot de la page et là il nous passe la main (intérieur de la méthode).
Par défaut, l'animation de transition est un fade des deux pages.
Il ne vous reste plus qu'à surcharger le sélecteur CSS view-transition par l'animation qui vous fait rêver et le tour est joué.
Voici un exemple d'utilisation : https://live-transitions.pages.dev/ (Clique sur les vidéos)
En conclusion, de nombreuses nouveautés CSS sont sorties durant le COVID, je ne vous ai présenté ici qu'une infime partie. Si vous souhaitez mettre le nez dedans, je vous conseille fortement de passer par la bible du CSS : W3C. Et si vous souhaitez voir ce qui va être pris en charge en termes de compatibilité par les navigateurs web dans les mois à venir vous pouvez vous rendre sur le site Interop.
Conclusion
Nous vous avons présenté ici trois des sujets qui nous ont le plus marqués. Une sélection assez technique, mais la conférence fut riche et a balayé un large éventail de sujets. Nous vous invitons donc à regarder les replays :-) À l’heure où nous écrivons ces lignes, ils sont disponibles ici, triés par salle. L’agenda est ici pour vous repérer dans les vidéos.
Merci aux organisateurs, sponsors et bénévoles ! À l’année prochaine !