Derniers messages sur Zeste de Savoirhttps://zestedesavoir.com/forums/2024-02-25T14:41:44+01:00Les derniers messages parus sur le forum de Zeste de Savoir.Les tests unitaires en Java, message #2541212024-02-25T14:41:44+01:00Arius/@Ariushttps://zestedesavoir.com/forums/sujet/16651/les-tests-unitaires-en-java/?page=1#p254121<p>Bonjour,</p>
<p>La bêta du contenu « Les tests unitaires en Java » a été désactivée.</p>Interface non exportée, message #2486152023-01-26T11:43:04+01:00nohar/@noharhttps://zestedesavoir.com/forums/sujet/16769/interface-non-exportee/?page=1#p248615<p>Je viens de tilter qu’en fait, j’ai un cas de <em>sealed interface</em> que je croise tous les jours au boulot depuis 3 ans sans le réaliser… gRPC en génère !</p>
<p>Voilà le cas que j’ai trouvé ce matin. Je veux faire un microservice qui choisit la bonne faction pour un nouveau joueur qui rejoint un monde dans notre jeu (vocabulaire business à nous, inutile de m’étendre sur le sujet), ça donne le protobuf suivant.</p>
<div class="hljs-code-div hljs-code-protobuf"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span><span data-count="16"></span><span data-count="17"></span><span data-count="18"></span></div><pre><code class="hljs language-protobuf">syntax = <span class="hljs-string">"proto3"</span>;
<span class="hljs-keyword">package</span> faction.v1;
<span class="hljs-keyword">option</span> csharp_namespace = <span class="hljs-string">"Gabsee.Backend.FactionAPI.V1"</span>;
<span class="hljs-keyword">option</span> go_package = <span class="hljs-string">"gitlab.com/gabsee/backend/grpc/factionpb/v1"</span>;
<span class="hljs-class"><span class="hljs-keyword">message</span> <span class="hljs-title">PickFactionRequest</span> </span>{
<span class="hljs-built_in">string</span> world = <span class="hljs-number">1</span>;
}
<span class="hljs-class"><span class="hljs-keyword">message</span> <span class="hljs-title">PickFactionResponse</span> </span>{
<span class="hljs-built_in">string</span> faction = <span class="hljs-number">1</span>;
}
<span class="hljs-class"><span class="hljs-keyword">service</span> <span class="hljs-title">Picker</span> </span>{
<span class="hljs-function"><span class="hljs-keyword">rpc</span> PickFaction(PickFactionRequest) <span class="hljs-keyword">returns</span> (PickFactionResponse) </span>{}
}
</code></pre></div>
<p>À partir de cette définition, pour implémenter ce service en Go, on doit générer les packages qui vont bien, puis créer un serveur comme celui-ci (qui délègue absolument tout à un service de la couche métier interne, et se contente de rajouter une ligne de log) :</p>
<div class="hljs-code-div hljs-code-go"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span><span data-count="16"></span><span data-count="17"></span><span data-count="18" class="hll"></span><span data-count="19"></span><span data-count="20"></span><span data-count="21"></span><span data-count="22"></span><span data-count="23"></span><span data-count="24"></span><span data-count="25"></span><span data-count="26"></span><span data-count="27"></span><span data-count="28"></span><span data-count="29"></span><span data-count="30"></span><span data-count="31"></span><span data-count="32"></span></div><pre><code class="hljs language-go"><span class="hljs-keyword">package</span> pickerv1
<span class="hljs-keyword">import</span> (
<span class="hljs-string">"context"</span>
v1 <span class="hljs-string">"gitlab.com/gabsee/backend/grpc/factionpb/v1"</span>
<span class="hljs-string">"gitlab.com/gabsee/backend/pkg/domain/faction"</span>
<span class="hljs-string">"gitlab.com/gabsee/backend/pkg/log"</span>
<span class="hljs-string">"go.uber.org/zap"</span>
<span class="hljs-string">"google.golang.org/grpc"</span>
)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewGRPC</span><span class="hljs-params">(svc faction.PickerService)</span> *<span class="hljs-title">server</span></span> {
<span class="hljs-keyword">return</span> &server{svc: svc}
}
<span class="hljs-keyword">type</span> server <span class="hljs-keyword">struct</span> {
v1.UnimplementedPickerServer
svc faction.PickerService
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *server)</span> <span class="hljs-title">Register</span><span class="hljs-params">(srv *grpc.Server)</span></span> {
v1.RegisterPickerServer(srv, s)
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(s *server)</span> <span class="hljs-title">PickFaction</span><span class="hljs-params">(ctx context.Context, in *v1.PickFactionRequest)</span> <span class="hljs-params">(*v1.PickFactionResponse, error)</span></span> {
log.WithContext(ctx).Info(<span class="hljs-string">"pick faction"</span>,
zap.String(<span class="hljs-string">"world"</span>, in.World),
)
faction, err := s.svc.PickFaction(ctx, in.World)
<span class="hljs-keyword">return</span> &v1.PickFactionResponse{Faction: faction}, err
}
</code></pre></div>
<p>On voit ici ligne 18 que le serveur est obligé d’embarquer la struct <code>v1.UnimplementedPickerServer</code>. Ceci est enforcé par l’interface <code>v1.PickerServer</code> qui est également générée par protobuf : </p>
<div class="hljs-code-div hljs-code-go"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span></div><pre><code class="hljs language-go"><span class="hljs-keyword">package</span> v1
<span class="hljs-comment">// ...</span>
<span class="hljs-comment">// PickerServer is the server API for Picker service.</span>
<span class="hljs-comment">// All implementations must embed UnimplementedPickerServer</span>
<span class="hljs-comment">// for forward compatibility</span>
<span class="hljs-keyword">type</span> PickerServer <span class="hljs-keyword">interface</span> {
PickFaction(context.Context, *PickFactionRequest) (*PickFactionResponse, error)
mustEmbedUnimplementedPickerServer()
}
</code></pre></div>
<p>Cette <em>sealed interface</em> sert à s’assurer que l’on embarque bien l’implémentation "de base" du service, qui retourne une erreur avec le code <code>Unimplemented</code> à chaque appel. Tous les implémenteurs de ce service doivent forcément embarquer cette implem de base.</p>
<p>C’est une façon de sécuriser l’utilisation que les gens font du code généré et, comme l’indique le commentaire du fichier généré, d’assurer la forward compatibility : si on rajoute une rpc à ce service dans la déclaration protobuf et que l’on régénère le code, les implémentations existantes ne seront pas cassées et pourront continuer à compiler sans problème, elle se contenteront de renvoyer <code>Unimplemented</code> lorsqu’on appellera la nouvelle méthode.</p>
<p>Au passage : pour des raisons évidentes (si on veut au contraire que ça pète à build-time quand on oublie d’adapter les implems), ce comportement de grpc est optionnel et on peut bien sûr le désactiver.</p>Interface non exportée, message #2485872023-01-25T15:15:02+01:00nohar/@noharhttps://zestedesavoir.com/forums/sujet/16769/interface-non-exportee/?page=1#p248587<blockquote>
<p><a href="/@nohar" rel="nofollow" class="ping ping-link">@<span class="ping-username">nohar</span></a> c’est quoi la façon canonique d’imiter un sum type en Go si c’est pas en trichant avec une interface scellée ?</p>
</blockquote>
<p>Excellente question !</p>
<p>Ma réponse va être décevante : "je sais pas trop, je n’ai jamais ou presque croisé ce genre de use-case dans la pratique, donc pour moi ça va se faire au cas par cas suivant le contexte".</p>
<p>L’intérêt des sum types c’est que l’on peut faire des switches dessus pour émuler une certaine forme de <em>pattern matching</em>. Le seul truc dont je suis à peu près sûr, c’est qu’il est en principe plus efficace de faire un switch sur la valeur d’un enum (sur des constantes d’un type custom qui est un alias de <code>uint8</code>, par exemple), qu’un <em>type switch</em> où l’on essayee <em>downcaster</em> une interface vers les implems possibles pour trouver le bon cas (le genre de chose que je répugne intuitivement à faire).</p>
<p>Du coup je ne sais pas trop. J’imagine que devant un tel cas, je ferais d’abord quelque chose de très bas du front avec un enum, mais pour répondre avec précision il faudrait que je fasse des tests sur un cas concret.</p>
<p>Ce qui me fait peur avec cette approche, c’est de faire intervenir la réflexivité dans le processus, parce que c’est le genre de truc qui plombe presque systématiquement les perfs, du coup la réponse la plus honnête que j’ai à te proposer est "je n’ai pas (encore) de façon canonique de faire ça".</p>Interface non exportée, message #2485862023-01-25T14:38:30+01:00adri1/@adri1https://zestedesavoir.com/forums/sujet/16769/interface-non-exportee/?page=1#p248586<p>Je vois deux cas d’utilisations.</p>
<ul>
<li>Celui mentionné avant de ne pas faire fuiter une interface parce que ses détails internes sont compliqués. Un cas très légitime de ça est lorsqu’on refactorise un vieux code et qu’on n’est pas encore sur d’avoir compris quelle est la bonne interface à avoir mais qu’on en a trouvé une qui rend service pour le refactoring. Si on la scelle, on évite qu’elle se répande dans la <em>codebase</em> avant d’être propre (mais effectivement, c’est un <em>code smell</em> à corriger plus tard).</li>
<li>C’est juste une façon de construire un truc qui ressemble à un <em>sum type</em>, au sens algébrique du terme. C’est à dire comme une <code>enum</code> si les variantes pouvaient contenir autre chose que le type unitaire. Du coup, ça me rend un peu curieux, <a href="/@nohar" rel="nofollow" class="ping ping-link">@<span class="ping-username">nohar</span></a> c’est quoi la façon canonique d’imiter un <em>sum type</em> en Go si c’est pas en trichant avec une interface scellée ?</li>
</ul>Interface non exportée, message #2485852023-01-25T13:39:36+01:00ache/@achehttps://zestedesavoir.com/forums/sujet/16769/interface-non-exportee/?page=1#p248585<p>Ok, donc c’est à peu prêt la même réaction que j’ai eu face à cette pratique. <img src="/static/smileys/svg/hihi.svg" alt="^^" class="smiley"><br>
Je demande à voir un case d’utilisation intéressant.</p>Interface non exportée, message #2485832023-01-25T11:52:36+01:00nohar/@noharhttps://zestedesavoir.com/forums/sujet/16769/interface-non-exportee/?page=1#p248583<p>Ok, je comprends l’idée.</p>
<p>Je n’ai jamais eu à utiliser ce genre de trucs du coup je n’ai pas d’opinion tranchée sur le sujet. J’imagine qu’il y a des cas où l’on ne veut <em>vraiment pas</em> laisser n’importe qui implémenter une interface en considérant qu’elle n’est utile que pour nous à cause de détails d’implémentations qu’on serait obligé de leaker vers les utilisateurs si on voulait qu’ils soient capable de l’implémenter…</p>
<p>Mais à première vue ça ressemble à un <em>smell</em>.</p>Interface non exportée, message #2485822023-01-25T11:30:02+01:00ache/@achehttps://zestedesavoir.com/forums/sujet/16769/interface-non-exportee/?page=1#p248582<blockquote>
<p>Mais tu me mets le doute, je ne suis pas certain de comprendre la même chose que toi sur ce qu’est une interface scellée. </p>
</blockquote>
<p>J’ai l’impression oui. <img src="/static/smileys/svg/hihi.svg" alt="^^" class="smiley"><br>
Pour moi, de ce que je comprends de la définition d’interface scellée de Go.
C’est que c’est une interface avec une méthode privée. </p>
<p>Un exemple avec des bonbons 🍬 🍭:</p>
<div class="hljs-code-div hljs-code-golang"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5" class="hll"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span><span data-count="16"></span><span data-count="17"></span><span data-count="18"></span><span data-count="19"></span><span data-count="20"></span><span data-count="21"></span><span data-count="22"></span><span data-count="23"></span><span data-count="24"></span><span data-count="25"></span><span data-count="26"></span><span data-count="27"></span><span data-count="28"></span><span data-count="29"></span><span data-count="30"></span><span data-count="31" class="hll"></span><span data-count="32" class="hll"></span><span data-count="33"></span><span data-count="34"></span><span data-count="35"></span><span data-count="36"></span><span data-count="37"></span><span data-count="38"></span><span data-count="39"></span><span data-count="40"></span><span data-count="41"></span></div><pre><code class="hljs language-golang"><span class="hljs-keyword">package</span> sweets
<span class="hljs-keyword">type</span> SweetCandy <span class="hljs-keyword">interface</span> {
SweetLevel() <span class="hljs-keyword">int</span>
seal()
}
<span class="hljs-keyword">type</span> Flavor <span class="hljs-keyword">string</span>
<span class="hljs-keyword">const</span> (
Chocolate = Flavor(<span class="hljs-string">"chocolate"</span>)
Vanilla = Flavor(<span class="hljs-string">"Vanilla"</span>)
)
<span class="hljs-keyword">type</span> Berlingo <span class="hljs-keyword">struct</span> {
SugarQuantity <span class="hljs-keyword">float32</span>
Flavor Flavor
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(x Berlingo)</span> <span class="hljs-title">SweetLevel</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
<span class="hljs-keyword">switch</span> x.Flavor {
<span class="hljs-keyword">case</span> Chocolate:
<span class="hljs-keyword">return</span> <span class="hljs-keyword">int</span>(x.SugarQuantity * <span class="hljs-number">2</span>)
<span class="hljs-keyword">case</span> Vanilla:
<span class="hljs-keyword">return</span> <span class="hljs-keyword">int</span>(x.SugarQuantity + <span class="hljs-number">10</span>)
<span class="hljs-keyword">default</span>:
<span class="hljs-keyword">return</span> <span class="hljs-keyword">int</span>(x.SugarQuantity)
}
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(x Berlingo)</span> <span class="hljs-title">seal</span><span class="hljs-params">()</span></span> {
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">GetSweetLevel</span><span class="hljs-params">(x SweetCandy)</span> <span class="hljs-title">int</span></span> {
<span class="hljs-keyword">switch</span> candy := x.(<span class="hljs-keyword">type</span>) {
<span class="hljs-keyword">case</span> Berlingo:
<span class="hljs-keyword">return</span> candy.SweetLevel()
<span class="hljs-keyword">default</span>:
<span class="hljs-keyword">return</span> <span class="hljs-number">1</span>
}
}
</code></pre></div>
<p>Et du coup, on ne peut plus créer de types qui remplisse l’interface <code>SweetCandy</code>. Ici, j’essaye de créer un nouveau bonbon :</p>
<div class="hljs-code-div hljs-code-golang"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5" class="hll"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span><span data-count="10"></span><span data-count="11"></span><span data-count="12"></span><span data-count="13"></span><span data-count="14"></span><span data-count="15"></span><span data-count="16"></span><span data-count="17"></span><span data-count="18"></span><span data-count="19"></span><span data-count="20"></span><span data-count="21"></span><span data-count="22"></span><span data-count="23"></span><span data-count="24"></span><span data-count="25"></span></div><pre><code class="hljs language-golang"><span class="hljs-keyword">package</span> main
<span class="hljs-keyword">import</span> (
<span class="hljs-string">"fmt"</span>
<span class="hljs-string">"pudding-maker/sweets"</span>
)
<span class="hljs-keyword">type</span> Pantteri <span class="hljs-keyword">struct</span> {
SugarQuantity <span class="hljs-keyword">int</span>
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(x Pantteri)</span> <span class="hljs-title">SweetLevel</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {
<span class="hljs-keyword">return</span> x.SugarQuantity
}
<span class="hljs-comment">// Ici, on essaye mais ça ne marche pas.</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(x Pantteri)</span> <span class="hljs-title">seal</span><span class="hljs-params">()</span></span> {}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
candy := sweets.Berlingo{<span class="hljs-number">10.5</span>, sweets.Chocolate}
finishCandy := Pantteri{<span class="hljs-number">5</span>}
fmt.Println(sweets.GetSweetLevel(candy))
fmt.Println(sweets.GetSweetLevel(finishCandy))
}
</code></pre></div>
<p>La ligne 23 j’obtiens l’erreur <code>cannot use finishCandy (variable of type Pantteri) as type sweets.SweetCandy in argument to sweets.GetSweetLevel: Pantteri does not implement sweets.SweetCandy (missing seal method)</code>.</p>
<p>Ça permet de faire un switch exhaustif <code>switch candy := x.(type)</code> car le code client ne pourra pas créer d’autres bonbons.</p>
<p>PS: J’ai trouvé ça sur Wikipédia, ça à l’air trop bon !
<a href="https://fr.wikipedia.org/wiki/Pantteri">https://fr.wikipedia.org/wiki/Pantteri</a></p>Interface non exportée, message #2485722023-01-24T22:13:19+01:00nohar/@noharhttps://zestedesavoir.com/forums/sujet/16769/interface-non-exportee/?page=1#p248572<blockquote>
<p>Ce que tu décris c’est juste l’utilisation d’une interface publique, non ?</p>
</blockquote>
<p>Oui… C’est la façon classique de faire ça dans les langages OO habituels comme Java. Mais en Go on ne fait pas ce genre de choses par défaut. Puisque ses interfaces sont très malléables, la plupart du temps on se contente de retourner des pointeurs vers des structs bien concrètes et libre à l’utilisateur de définir une interface s’il en a besoin.</p>
<p>Retourner une interface, on le fait seulement quand on a une bonne raison concrète de le faire. Typiquement si un fonction peut retourner des types différents.</p>
<p>L’extension de type est toujours possible, il suffit d’embarquer l’interface que tu souhaites étendre dans une structure et surcharger les méthodes qui vont bien.</p>
<div class="hljs-code-div hljs-code-go"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span></div><pre><code class="hljs language-go"><span class="hljs-keyword">type</span> MyInterface <span class="hljs-keyword">interface</span> {
Foo()
Bar()
}
<span class="hljs-keyword">type</span> Extended <span class="hljs-keyword">struct</span> {
MyInterface <span class="hljs-comment">// Extended implémente MyInterface par construction</span>
}
</code></pre></div>
<p>Mais tu me mets le doute, je ne suis pas certain de comprendre la même chose que toi sur ce qu’est une interface scellée.</p>
<p><del>Cela dit, je n’ai jamais vu ni eu besoin d’étendre un type d’un package étranger en Go.</del> Edit: en fait, si, une fois, pour abstraire un mécanisme de discovery et de failover derrière l’interface de <code>grpc.ClientConnection</code> tout en wrappant ce dernier, ce qui m’a permis d’injecter facilement ce comportement dans mon code existant.</p>Interface non exportée, message #2485712023-01-24T21:50:58+01:00ache/@achehttps://zestedesavoir.com/forums/sujet/16769/interface-non-exportee/?page=1#p248571<blockquote>
<p>Typiquement, toutes les fonctions du package io prennent en paramètre des interfaces définies dans ce package : là où elles sont utilisées, donc.</p>
</blockquote>
<p>Intéressant ! Ça a du sens sur un bout de code qui peut être importé. Par-contre, ça n’a pas de sens de faire ça dans le package main ou avec une interface non exportée (en minuscule).</p>
<blockquote>
<p>Pour ce qui est des interfaces […] ton interface publique qui, elle, reste fixe, "scellée".</p>
</blockquote>
<p>Là par-contre, je ne te suis pas du tout.<br>
Ce que tu décris c’est juste l’utilisation d’une interface publique, non ? Mais une interface scellée publique ne laisse plus que la possibilité d’étendre les types déjà définis (puisqu’on ne peut pas créer de nouveau type qui implémente cette interface). Je ne vois pas l’apport par rapport à une interface publique simple.</p>Interface non exportée, message #2485592023-01-24T14:50:16+01:00nohar/@noharhttps://zestedesavoir.com/forums/sujet/16769/interface-non-exportee/?page=1#p248559<p>Salut !</p>
<p>Alors, pour le premier conseil (définir les interfaces là où elles sont utilisées), c’est exactement ce que fait la lib standard avec <code>io.Writer</code>, par exemple. C’est une façon très simple de faire de la programmation par contrat.</p>
<p>Typiquement, toutes les fonctions du package <code>io</code> prennent en paramètre des interfaces définies dans ce package : là où elles sont utilisées, donc. Tu vas donc avoir :</p>
<div class="hljs-code-div hljs-code-go"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span></div><pre><code class="hljs language-go"><span class="hljs-keyword">type</span> Writer <span class="hljs-keyword">interface</span> {
Write(p []<span class="hljs-keyword">byte</span>) (n <span class="hljs-keyword">int</span>, err error)
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">WriteString</span><span class="hljs-params">(w Writer, s <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">(n <span class="hljs-keyword">int</span>, err error)</span></span> {
<span class="hljs-comment">// implem de la fonction WriteString</span>
}
</code></pre></div>
<p>Autrement dit, le contrat sur le premier argument est défini dans le même package que la fonction, si tu veux utiliser <code>io.WriteString</code>, tu as juste à lui passer une structure possédant une méthode <code>Write</code> telle que définie par cette interface, puisque Go te dispense d’avoir à faire des déclarations du style <code>type MachinWriter implements io.Writer</code>.</p>
<p>Dans le cas général, on n’a vraiment pas besoin de plus que ça pour faire du polymorphisme. Au lieu de définir des grosses interfaces communes à plein de composants, on définit l’interface minimale très précise dont on a besoin pour fonctionner. </p>
<p>En y réfléchissant, ça rapproche beaucoup l’utilisation des interfaces de celle de fonctions de callback : </p>
<div class="hljs-code-div hljs-code-go"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span></div><pre><code class="hljs language-go"><span class="hljs-keyword">type</span> EventCallback = <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(*Event)</span> <span class="hljs-title">error</span></span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">WatchEvents</span><span class="hljs-params">(ctx context.Context, callback EventCallback)</span> <span class="hljs-title">error</span></span> {
<span class="hljs-comment">// ...</span>
}
</code></pre></div>
<p>Dans les faits c’est une sorte de "duck-typing typé statiquement".</p>
<hr>
<p>Pour ce qui est des interfaces scellées, c’est une simple façon de protéger l’implémentation : la structure, ses champs, son implémentation est privée, et tu la retournes "abstraite" par l’interface publique. Tu peux changer l’implémentation privée de la struct, renommer ses champs, retourner des structures complètement différentes en fonction d’un paramètre passé au constructeur etc. sans jamais risquer de casser le code client, puisque ce dernier ne dépend que de ton interface publique qui, elle, reste fixe, "scellée".</p>
<p>C’est bêtement de l’encapsulation.</p>Interface non exportée, message #2485562023-01-24T13:47:17+01:00ache/@achehttps://zestedesavoir.com/forums/sujet/16769/interface-non-exportee/?page=1#p248556<p>Bonjour,</p>
<p>Je lis <a href="https://blog.chewxy.com/2018/03/18/golang-interfaces/#dont-do-this">cet article</a> et je suis plutôt sceptique à certains conseils comme:</p>
<ol>
<li><em>Define the interface at point of use</em>.</li>
<li>L’utilisation des interfaces scellée.</li>
</ol>
<p>J’ai vraiment du mal à voir l’apport. C’est moi qui passe à coté d’une pratique courante où un gain est clairement identifié ?</p>
<p>Qu’en pensez vous ? Quel est votre expérience à ce sujet ?</p>
<p>Merci</p>
<details class="custom-block custom-block-spoiler"><summary class="custom-block-heading">tl;dr / Résumé de l’article</summary><div class="custom-block-body"><p>L’auteur recommande de faire:</p><div class="hljs-code-div hljs-code-golang"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span></div><pre><code class="hljs language-golang"><span class="hljs-keyword">package</span> animals
<span class="hljs-keyword">type</span> Dog <span class="hljs-keyword">struct</span>{}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a Dog)</span> <span class="hljs-title">Speaks</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> { <span class="hljs-keyword">return</span> <span class="hljs-string">"woof"</span> }
</code></pre></div><p>Puis: </p><div class="hljs-code-div hljs-code-golang"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span></div><pre><code class="hljs language-golang"><span class="hljs-keyword">package</span> circus
<span class="hljs-keyword">type</span> Speaker <span class="hljs-keyword">interface</span> {
Speaks() <span class="hljs-keyword">string</span>
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Perform</span><span class="hljs-params">(a Speaker)</span> <span class="hljs-title">string</span></span> { <span class="hljs-keyword">return</span> a.Speaks() }
</code></pre></div><p>Plutôt que :</p><div class="hljs-code-div hljs-code-golang"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span><span data-count="6"></span><span data-count="7"></span><span data-count="8"></span><span data-count="9"></span></div><pre><code class="hljs language-golang"><span class="hljs-keyword">package</span> animals
<span class="hljs-keyword">type</span> Animal <span class="hljs-keyword">interface</span> {
Speaks() <span class="hljs-keyword">string</span>
}
<span class="hljs-comment">// implementation of Animal</span>
<span class="hljs-keyword">type</span> Dog <span class="hljs-keyword">struct</span>{}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(a Dog)</span> <span class="hljs-title">Speaks</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> { <span class="hljs-keyword">return</span> <span class="hljs-string">"woof"</span> }
</code></pre></div><p>Et ça:</p><div class="hljs-code-div hljs-code-golang"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span><span data-count="3"></span><span data-count="4"></span><span data-count="5"></span></div><pre><code class="hljs language-golang"><span class="hljs-keyword">package</span> circus
<span class="hljs-keyword">import</span> <span class="hljs-string">"animals"</span>
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Perform</span><span class="hljs-params">(a animal.Animal)</span> <span class="hljs-title">string</span></span> { <span class="hljs-keyword">return</span> a.Speaks() }
</code></pre></div></div></details>Les tests unitaires en Java, message #2476772022-12-16T11:47:05+01:00Ksass`Peuk/@Ksass%60Peukhttps://zestedesavoir.com/forums/sujet/16651/les-tests-unitaires-en-java/?page=1#p247677<p>Je t’ai mis ça ici : <a href="http://allan-blanchard.fr/teaching/only-test.pdf">http://allan-blanchard.fr/teaching/only-test.pdf</a></p>
<p>Si tu comptes utiliser des bouts de taille notable, dis moi juste que je te donne les auteurs <img src="/static/smileys/svg/smile.svg" alt=":)" class="smiley"></p>Les tests unitaires en Java, message #2476342022-12-14T18:47:57+01:00artragis/@artragishttps://zestedesavoir.com/forums/sujet/16651/les-tests-unitaires-en-java/?page=1#p247634<p>Ca peut m’intéresser, même si je ne sais pas ce que je peux en faire par rapport au tuto actuel.</p>Les tests unitaires en Java, message #2476302022-12-14T16:30:26+01:00Ksass`Peuk/@Ksass%60Peukhttps://zestedesavoir.com/forums/sujet/16651/les-tests-unitaires-en-java/?page=1#p247630<p>Pour les connaissances plus générales à propos des tests, notamment critères de couverture, j’ai un cours que j’ai repris d’un collègues qui contient pas mal d’information et de références. Notamment si tu veux lister un peu plus en détails les critères de couvertures qui existent.</p>
<p>Je ne sais pas si ça peut t’intéresser ?</p>Les tests unitaires en Java, message #2475952022-12-12T20:11:34+01:00artragis/@artragishttps://zestedesavoir.com/forums/sujet/16651/les-tests-unitaires-en-java/?page=1#p247595<p>Bonjour les agrumes !</p>
<p>La bêta a été mise à jour et décante sa pulpe
à l’adresse suivante :</p>
<div class="align-center"><p> <a href="https://zestedesavoir.com/contenus/beta/274/les-tests-unitaires-en-java/">Les tests unitaires en Java</a> </p></div>
<p>Merci d’avance pour vos commentaires.</p>Les tests unitaires en Java, message #2475322022-12-09T22:31:32+01:00artragis/@artragishttps://zestedesavoir.com/forums/sujet/16651/les-tests-unitaires-en-java/?page=1#p247532<p>Bonjour les agrumes !</p>
<p>La bêta a été mise à jour et décante sa pulpe
à l’adresse suivante :</p>
<div class="align-center"><p> <a href="https://zestedesavoir.com/contenus/beta/274/les-tests-unitaires-en-java/">Les tests unitaires en Java</a> </p></div>
<p>Merci d’avance pour vos commentaires.</p>Les tests unitaires en Java, message #2475012022-12-08T21:18:11+01:00artragis/@artragishttps://zestedesavoir.com/forums/sujet/16651/les-tests-unitaires-en-java/?page=1#p247501<blockquote>
<p>Je pense honnêtement que le début passe à côté des plus gros intérêts des TU, et en particulier que le tableau est faux. S’ils sont commités avec le code, des tests faits avec des main peuvent tout à fait être reproductibles, compréhensibles et documentés – j’ai même croisé un très vieux projet avec plus de 500 main de test dedans. Par contre, c’est ultra pénible parce que c’est spécifique au projet, éparpillé, mélangé avec le code de production, difficile à découvrir et surtout, surtout, impossible à lancer en tant que « suite de tests ». Ça ne produit pas non plus de rapports standards à intégrer dans un flux de travail sain (intégration continue, etc).<br>
Pour moi il manque aussi une notion importante : avoir des TU, c’est aussi faire en sorte que le code soit architecturé de façon à être testable. Toute personne qui a tenté d’ajouter des tests à postériori sur un code historique voit très bien ce dont je veux parler.</p>
</blockquote>
<p>Pas encore pris en compte, ça viendra, mais je veux me poser pour bien faire ça.</p>
<blockquote>
<p>Pinaillage : JUnit est un framework de test de Java, pas le seul existant.</p>
</blockquote>
<p>Fixed</p>
<blockquote>
<p>Pour l’installation, ça serait pas mal de renvoyer vers les documentation pour Maven et Gradle.</p>
</blockquote>
<p>Je verrai comment faire.</p>
<blockquote>
<p>Je ne vois vraiment pas l’intérêt de présenter les tests avec fail(), je ne connais personne qui fait ça – de fait, l’utilisation de fail() en-dehors du cas d’un test pas encore implémenté est très rare. Pour moi il vaut mieux présenter directement les assertions avec les méthodes assertXXX(), parce que ça correspond bien mieux à ce qui se fait (donc évite de commencer par apprendre quelque chose d’inutilisé), et surtout permet de montrer dès le début qu’une méthode de test peut être simple (c’est important pour faire accepter l’idée de passer du temps à faire des tests).</p>
</blockquote>
<p>J’ai totalement revamp cette partie là. Je pense que ça permet d’aller petit à petit vers le concept sans jeter fonctions que le débutant ne peut pas deviner de suite ni pourquoi elles sont là.</p>
<blockquote>
<p>Pourquoi ce paragraphe est sur Eclipse et pas sur IntelliJ ?</p>
</blockquote>
<p>Car je n’ai pas encore eu le temps de le reprendre, comme je le disais, je n’ai pu revamp qu’une toute petite partie du tuto et que pour moi le coverage n’a pas sa place <em>aussi tôt</em> dans le tuto.</p>
<blockquote>
<p>Je ne sais pas si c’est pertinent de présenter les mocks comme étant des implémentations ad hoc des interfaces. Je n’ai jamais vu personne faire ça dans toute ma carrière (je le vois très souvent sur des interfaces extérieures au programme – typiquement les entrées/sorties –, mais jamais sur les interfaces Java au sein du projet). Par contre, présenter directement des outils comme Mockito peut être trop difficile.</p>
</blockquote>
<p>Le chapitre des mocks DOIT être revamp, ne serait-ce que parce qu’il passe à côté de cette si loufoque et géniale traduction francophone "le bouchonnage". </p>
<p>Blague a part, le tutoriel initial est vieux, je le reprends car j’aime pas trop l’idée de perdre le référencement, mais certaines parties seront jetées et refaites alors que sur d’autres je vais par exemple garder la trame voire les idées d’exemples.</p>Les tests unitaires en Java, message #2473712022-12-05T10:02:16+01:00SpaceFox/@SpaceFoxhttps://zestedesavoir.com/forums/sujet/16651/les-tests-unitaires-en-java/?page=1#p247371<p>J’y pense aussi parce que c’est un truc qui surprend : ça peut être tout à fait normal d’avoir plus de lignes de code dans les tests que dans le code testé. Je pense que ça peut être utile de donner cette information quelque part.</p>Les tests unitaires en Java, message #2473242022-12-03T21:53:53+01:00artragis/@artragishttps://zestedesavoir.com/forums/sujet/16651/les-tests-unitaires-en-java/?page=1#p247324<p>Les mocks seront vraiment mis en fin de tuto. Je vais y aller progressivement, faut par contre que je trouve un bon exemple.</p>Les tests unitaires en Java, message #2473162022-12-03T21:17:21+01:00SpaceFox/@SpaceFoxhttps://zestedesavoir.com/forums/sujet/16651/les-tests-unitaires-en-java/?page=1#p247316<p>C’est clair que la progressivité est nécessaire, et pas toujours évidente pour ce qui est de la présentation des tests.</p>
<p>La plus grosse difficulté que je vois à gérer, c’est le problème des mocks.</p>Les tests unitaires en Java, message #2473082022-12-03T18:30:04+01:00artragis/@artragishttps://zestedesavoir.com/forums/sujet/16651/les-tests-unitaires-en-java/?page=1#p247308<p>Merci pour ce retour. </p>
<p>D’une manière ou d’une autre il sera totalement pris en compte. Juste pour comprendre pourquoi le texte actuel est comme ça : je reprends le vieux tutoriel. Il se peut que l’immense majorité du texte soit revue, mais dans mon travail préliminaire, j’ai repris le texte original en adaptant à JUnit 5.</p>
<p>Notons que le but de ce tuto est de démarrer avec une personne qui débute dans l’apprentissage du test, pas une personne qui travaille déjà en entreprise et qui se projette dans un base de code complexe. </p>
<p>Comme je le disais l’ensemble des remarques que tu as faite seront prises en compte, je ne sais pas encore comment exactement car il <strong>faut</strong> être progressif vu le public du tutoriel. Sinon autant faire un lien vers baeldung.</p>