Derniers messages sur Zeste de Savoirhttps://zestedesavoir.com/forums/2023-09-13T13:30:52+02:00Les derniers messages parus sur le forum de Zeste de Savoir.Injection de dépendance avec samber/do, message #2520652023-09-13T13:30:52+02:00pyoroalb/@pyoroalbhttps://zestedesavoir.com/forums/sujet/17139/injection-de-dependance-avec-samberdo/?page=1#p252065<p>Pour moi l’avantage de répartir l’initialisation de chaque service indépendament est d’éviter d’avoir tout au même endroit. J’ai tendance à considérer un service comme un package qui est indépendant et donc cela me permet d’abstraire leurs initialisation. C’est son propre package qui se débrouille pour savoir comment il doit-être initialiser.<br>
Le but est aussi d’éviter tout le code qui ne me semble pas intéressant qui est uniquement de récupérer des valeurs dans des variables d’environement, des arguments, … et de les donner aux fonctions d’initialisations des service.</p>
<p>Quand je parle d’un fichier très long c’est plusieurs miliers de lignes qui vont juste initialiser des services avec les valeurs qui vont être récupérer depuis l’environement ou autre.</p>
<hr>
<p>Le package envconfig semble intéressant, cependant je pense qu’il ne résoudrait qu’une partie de ce que je cherche à faire. Plutot que de récupérer les variables d’environement, par exemple avec <a href="https://github.com/spf13/viper">viper</a>, il me permetterait d’avoir mes service (qui sont des <code>struct</code>) directement initialisé de ce que je vois.<br>
Il me resterait parcontre a avoir dans mon main <em>plus que</em> l’appel à la fonction <code>envconfig.Process</code> pour chaque service. Je pense neanmoins que ce package pourrait me permettre de raccourcir le code pour récupérer ces valeurs.</p>Injection de dépendance avec samber/do, message #2520632023-09-13T13:07:28+02:00ache/@achehttps://zestedesavoir.com/forums/sujet/17139/injection-de-dependance-avec-samberdo/?page=1#p252063<p>Est-ce qu’une solution type <a href="https://pkg.go.dev/github.com/sethvargo/go-envconfig">envconfig</a> te conviendrait ?</p>Injection de dépendance avec samber/do, message #2520622023-09-13T12:21:36+02:00tleb/@tlebhttps://zestedesavoir.com/forums/sujet/17139/injection-de-dependance-avec-samberdo/?page=1#p252062<p>Salut,</p>
<p>J’ai du mal à comprendre en quoi c’est avantageux de répartir le code de création de chaque <em>service</em> plutôt qu’avoir un <code>main()</code> qui est lisible linéairement.</p>
<p>Tu parles d’éviter un fichier très long, on parle de quelle taille ? Est-ce gênant ? Instinctivement, je n’aurai pas envie de (1) ajouter une dépendance et (2) éparpiller une procédure linéaire simple (initialisation des services) en pleins de petits blocs dans chaque fichier.</p>
<p>Le principal problème que je vois, outre la complexité ajoutée, est qu’il devient difficile de savoir quels services sont utilisées en pratique. Il faut lire chaque fichier pour voir le comportement.</p>Injection de dépendance avec samber/do, message #2520552023-09-13T11:04:13+02:00pyoroalb/@pyoroalbhttps://zestedesavoir.com/forums/sujet/17139/injection-de-dependance-avec-samberdo/?page=1#p252055<p>Bonjour,
Je suis en train d’essayer d’utiliser le package <a href="https://github.com/samber/do">samber/do</a> en le combinant avec les fonctions <code>init()</code> de Go.</p>
<p>Mon but est déviter d’avoir un fichier d'<em>initalisation</em> qui va configurer mes services et mon application.
Je souhaite éviter un tel fichier car il est souvent très volumineux et peu pratique à modifier. </p>
<p>On peut avoir un exemple de ça <a href="https://github.com/albdewilde/tdi/blob/710db2513e2cc9a779742418a06f73c6e160df78/main.go#L15">ici</a></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></div><pre><code class="hljs language-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
<span class="hljs-comment">// Usual way to initialize out app</span>
userFetch := usualinfra.NewUserFetch(<span class="hljs-string">"example.com"</span>, <span class="hljs-string">"key"</span>)
locFetch := usualinfra.NewLocationFetch(<span class="hljs-string">"location.com"</span>, <span class="hljs-string">"key"</span>)
usualApp := usualdomain.NewApp(userFetch, locFetch)
<span class="hljs-comment">// Do stuff with our app</span>
usualApp.DoStuff()
[...]
</code></pre></div>
<p>Ici j’initialise les services <code>userFetch</code> et <code>locFetch</code> que je donne en suite à mon application. Dans le cas où j’ai beaucoup de service avec beaucoups de configuration le fichier peut-être très long, c’est ce que je cherche à éviter.</p>
<hr>
<p>Pour éviter cette suite d’initialisation de service je suis donc parti en faisant quelque chose comme ça:</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-function"><span class="hljs-keyword">func</span> <span class="hljs-title">init</span><span class="hljs-params">()</span></span> {
uf := do.MustInvoke[*userfetcher.UserFetch](<span class="hljs-literal">nil</span>)
lf := do.MustInvoke[*locationfetcher.LocationFetch](<span class="hljs-literal">nil</span>)
do.Provide(
<span class="hljs-literal">nil</span>,
<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(i *do.Injector)</span> <span class="hljs-params">(*App, error)</span></span> {
<span class="hljs-keyword">return</span> NewApp(uf, lf), <span class="hljs-literal">nil</span>
},
)
}
</code></pre></div>
<p>Dans la fonction <code>init</code> du package de mon application, j’utilise <em>samber</em> pour récupérer les instances des service de localisation et des utilisateurs. En suite je crée mon application.</p>
<p>Les services de localisation et des utilisateurs sont eux crée de la même manière, dans la fonction <code>init</code> de leurs package. </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-function"><span class="hljs-keyword">func</span> <span class="hljs-title">init</span><span class="hljs-params">()</span></span> {
do.Provide(
<span class="hljs-literal">nil</span>,
<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(i *do.Injector)</span> <span class="hljs-params">(*LocationFetch, error)</span></span> {
<span class="hljs-keyword">return</span> NewLocationFetch(<span class="hljs-string">"location.com"</span>, <span class="hljs-string">"key"</span>), <span class="hljs-literal">nil</span>
},
)
}
</code></pre></div>
<p>Ici on remarque que je crée mon service de localisation et je <em>l’enregistre dans</em> samber.</p>
<p>Avec cette méthode pour initialiser mes service, je me retrouve donc avec seulement ceci dans la fonction <code>main</code>:</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></div><pre><code class="hljs language-go"><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
[...]
<span class="hljs-comment">// Initialize the app with samber/do</span>
samberApp := do.MustInvoke[*samber.App](<span class="hljs-literal">nil</span>)
<span class="hljs-comment">// Do stuff with our app</span>
samberApp.DoStuff()
}
</code></pre></div>
<hr>
<p>Quels sont vos avis sur la méthode d’utiliser les fonction <code>init</code> de Go combiné à <em>samber</em> pour initialiser plus facilement ses service ? Est ce que vous avec des expériences avec une tel méthode ?</p>
<p>Voici <a href="https://github.com/albdewilde/tdi/tree/master">le dépot github</a> dans lequel sont mes exemples.</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>Go : avenir et domaines d'applications ?, message #2419152022-03-30T17:36:50+02:00Arius/@Ariushttps://zestedesavoir.com/forums/sujet/15045/go-avenir-et-domaines-dapplications/?page=1#p241915<p>Merci de créer un sujet propre pour poser vos questions et de ne pas déterrer des sujets anciens. <img src="/static/smileys/svg/smile.svg" alt=":)" class="smiley"> </p>Go : avenir et domaines d'applications ?, message #2418782022-03-28T21:26:07+02:00Jack/@Jackhttps://zestedesavoir.com/forums/sujet/15045/go-avenir-et-domaines-dapplications/?page=1#p241878<p>adri1,</p>
<p>Désolé de mal m’exprimer encore, je sais que GCC et CC1 compilent du C, mais pourquoi exiger GCC et CC1. J’avais résolu le tout (installation de MSYS2.MSYS) en resuivant le lien Fyne que j’avais exploré avant que tu me le fournisse.</p>
<p>J’ai compris malgré tout, étant un peu bête que Fyne veut GCC et CC1, avoue tout de même que c’est pas cool.</p>
<p>Moi j’aime que çà roule, ayant fait du Basic, Forth (plus personne ne connait) C, C++, Perl, Cobol, Java, C#, je me dis parfois que l’open source est un peu usine à gaz, détrompe moi.</p>
<p>J’ai connu le développement des langages depuis 1984 et je suis toujours sur le c.l de voir toutes ces nouveautés qui sont quand même intéressantes comme Go et Rust. Bon le truc bien et pas bien en go c’est le "unused" qui perturbe pour les variables ou les bibliothèques non utilisées (qui interdit toute compilation, ils ont copié Rust) mais on s’y fait.</p>
<p>J’aime bien le paradigme de Go, comme Rust, après il faut s’y faire, Rust dur à apprendre me plait car il interdit toute erreur de mémoire ou de variable avant la compilation, mais dur dur..</p>
<p>Bonne nuit</p>Go : avenir et domaines d'applications ?, message #2418742022-03-28T18:53:38+02:00Jack/@Jackhttps://zestedesavoir.com/forums/sujet/15045/go-avenir-et-domaines-dapplications/?page=1#p241874<p>Coucou adri1,</p>
<p>Merci de ta réponse,</p>
<p>Je me suis mal exprimé, en installant Fyne (écrit en Go je crois) et étant sous Windows 10 je me posais la question pourquoi cette bibliothèque (bien que ce n’est pas toujours le cas mais j’ai vu des bibliothèques détecter l’OS) ne détecte pas l’OS et nous insulte avec CC1 et GCC, (si c’est via MinGW64) alors que MSYS264 bits , nickel,ce devrait être simple et transparent alors que le compilateur C est présent (et CC1 et GCC aussi).</p>
<p>Je veux dire "Tu es sur Windows 10, pas besoin de CC1 et GCC, mais je rêve sans doute"</p>
<p> Bon , j’ai bien compris les ponts nécessaires qu’apporte sans doute la liaison ce qui invalide sans doute ma phrase précédente.</p>
<p> "Fyne, il serait de bon ton de les poser dans ton propre sujet"</p>
<p> D’accord mais je pouvais toute même poser des questions en relation avec Go, d’autant que Fyne serait développée en Go</p>
<p>Je n’ai pas encore vu tes liens que je vais explorer.</p>
<p>Merci encore à toi de m’avoir répondu.</p>
<p>PS : 1/ Tu ne me dis pas pourquoi MingGW64 ne fonctionne pas.
2/ J’ai mis Rust en stand by, courbe d’apprentissage très rude, mais c’est bon pour les méninges, et pareil , graphisme, hello les bibliothèques.</p>Go : avenir et domaines d'applications ?, message #2418522022-03-27T21:41:18+02:00adri1/@adri1https://zestedesavoir.com/forums/sujet/15045/go-avenir-et-domaines-dapplications/?page=1#p241852<p>Salut,</p>
<p><a href="/@Jack" rel="nofollow" class="ping ping-link">@<span class="ping-username">Jack</span></a> : si tu as des questions sur l’utilisation de <code>Fyne</code>, il serait de bon ton de les poser dans ton propre sujet. Maintenant, pour répondre à ta question, la réponse est sur la page d’installation de Fyne</p>
<blockquote>
<p>Fyne requires […] a C compiler (to connect with system graphics drivers)</p>
</blockquote>
<p>Comme Go n’est pas un langage système, s’interfacer avec des lib est plus facile en écrivant une petite couche de C pour faire le pont entre les lib de bas niveau et Go. En allant voir le code, on trouve par exemple <a href="https://github.com/fyne-io/fyne/blob/c8fbd5213331764b22929ca6877302e028ab0a0d/internal/driver/mobile/app/x11.c">ce genre de chose</a> pour faire le pont avec X11, <a href="https://github.com/fyne-io/fyne/blob/6e90820ea9ca4df836db5a99384c51073415571e/app/app_mobile_and.c">ou bien celui-là</a> pour faire le pont avec Android. Il y aussi d’autre petits bouts de C qui sont des dépendances en C que Fyne embarque directement.</p>Go : avenir et domaines d'applications ?, message #2418512022-03-27T21:27:13+02:00Jack/@Jackhttps://zestedesavoir.com/forums/sujet/15045/go-avenir-et-domaines-dapplications/?page=1#p241851<p>Re Bonsoir à tous,</p>
<p>Je connais de loin GCC (le compilateur), quelqu’un peut il m’expliquer que ayant installer GO en ayant au préalable installé les outils de compilation C et C++ pourquoi Fyne a besoin de GCC et de CC1, je me coucherais moins idiot si quelqu’un me répond.</p>
<p>Re Bonne soirée</p>Go : avenir et domaines d'applications ?, message #2418482022-03-27T21:07:38+02:00Jack/@Jackhttps://zestedesavoir.com/forums/sujet/15045/go-avenir-et-domaines-dapplications/?page=1#p241848<p>Bonjour à tous,</p>
<p>Go m’a beaucoup plu, mais en absence de bibliothèque graphique je me suis intéressé à la bibliothèque Fyne où je me suis arraché les cheveux pour arriver à avoir une fenêtre Windows construite en Go.</p>
<p>Mauvaises explications et mauvais téléchargements. J’ai réussi avec MSYS2.MSYS et pas avec MinGW64, un comble ! Avec MinGW64, à la compilation , GO ne trouvait pas GCC malgré mes variables d’environnement bien renseignées. J’ai été très énervé car cela doit rouler et vite.</p>
<p>De plus MingGW64 est une une daube où il faut cliquer sur des milliers de packages pour les installer et idem pour les désinstaller, pas d’uninstall, nul !</p>
<p>Bref en restant zen j’ai installé MSYS2.MSYS en suivant scrupuleusement la doc, et là ça marche !</p>
<p>Je suis un vieux de la vieille , je pense que la prochaine fois si je dois avoir affaire à de tels problèmes pour un nouveau langage, je laisserai tomber.</p>
<p>Ce n’est pas normal d’avoir de la doc inefficace avec cette perte de temps.</p>
<p>Maintenant j’ai une belle fenêtre Windows avec widgets mais quasiment aucun tuto.</p>
<p>Il faut en vouloir pour faire du GO sachant que j’ai mis en stand by Rust particulièrement imbuvable, notamment sur les Strings.</p>
<p>Bonne soirée à tous.</p>Gestion des Threads en Go, message #2397592021-12-22T14:06:09+01:00nohar/@noharhttps://zestedesavoir.com/forums/sujet/15932/gestion-des-threads-en-go/?page=1#p239759<blockquote>
<p>ça m’a l’air en effet un petit peu plus compliqué que ma dernière solution</p>
</blockquote>
<p>Ça se fait plutôt bien avec la lib standard et la structure <code>sync.Pool</code> : </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"></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><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><span data-count="42"></span><span data-count="43"></span><span data-count="44"></span><span data-count="45"></span><span data-count="46"></span><span data-count="47"></span><span data-count="48"></span><span data-count="49"></span><span data-count="50"></span><span data-count="51"></span><span data-count="52"></span><span data-count="53"></span><span data-count="54"></span><span data-count="55"></span><span data-count="56"></span><span data-count="57"></span><span data-count="58"></span><span data-count="59"></span><span data-count="60"></span><span data-count="61"></span><span data-count="62"></span><span data-count="63"></span><span data-count="64"></span><span data-count="65"></span><span data-count="66"></span><span data-count="67"></span><span data-count="68"></span><span data-count="69"></span><span data-count="70"></span><span data-count="71"></span><span data-count="72"></span><span data-count="73"></span><span data-count="74"></span><span data-count="75"></span><span data-count="76"></span><span data-count="77"></span><span data-count="78"></span><span data-count="79"></span><span data-count="80"></span><span data-count="81"></span><span data-count="82"></span><span data-count="83"></span><span data-count="84"></span><span data-count="85"></span><span data-count="86"></span><span data-count="87"></span><span data-count="88"></span><span data-count="89"></span><span data-count="90"></span><span data-count="91"></span><span data-count="92"></span><span data-count="93"></span><span data-count="94"></span><span data-count="95"></span><span data-count="96"></span></div><pre><code class="hljs language-go"><span class="hljs-keyword">package</span> main
<span class="hljs-keyword">import</span> (
<span class="hljs-string">"fmt"</span>
<span class="hljs-string">"runtime"</span>
<span class="hljs-string">"sync"</span>
<span class="hljs-string">"time"</span>
)
<span class="hljs-comment">// Ici je simule des "handles"</span>
<span class="hljs-keyword">type</span> Handle <span class="hljs-keyword">int</span>
<span class="hljs-keyword">var</span> (
handle Handle
handleMtx sync.Mutex
)
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">newHandle</span><span class="hljs-params">()</span> <span class="hljs-title">Handle</span></span> {
time.Sleep(<span class="hljs-number">10</span> * time.Millisecond) <span class="hljs-comment">// obtenir un handle coûte 10ms</span>
handleMtx.Lock()
<span class="hljs-keyword">defer</span> handleMtx.Unlock()
handle++
<span class="hljs-keyword">return</span> handle
}
<span class="hljs-comment">// Ici l'implémentation du pool</span>
<span class="hljs-keyword">type</span> ClientFunc <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(Handle)</span> <span class="hljs-title">error</span></span>
<span class="hljs-keyword">type</span> ClientPool <span class="hljs-keyword">struct</span> {
sync.Pool
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">NewClientPool</span><span class="hljs-params">()</span> *<span class="hljs-title">ClientPool</span></span> {
c := &ClientPool{
Pool: sync.Pool{
New: <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span> <span class="hljs-title">interface</span></span>{} {
in := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> ClientFunc)
out := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> error)
<span class="hljs-keyword">go</span> runner(in, out)
<span class="hljs-keyword">return</span> &executor{in, out}
},
},
}
<span class="hljs-keyword">return</span> c
}
<span class="hljs-keyword">type</span> executor <span class="hljs-keyword">struct</span> {
In <span class="hljs-keyword">chan</span><- ClientFunc
Out <-<span class="hljs-keyword">chan</span> error
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">runner</span><span class="hljs-params">(in <-<span class="hljs-keyword">chan</span> ClientFunc, out <span class="hljs-keyword">chan</span><- error)</span></span> {
<span class="hljs-keyword">defer</span> <span class="hljs-built_in">close</span>(out)
runtime.LockOSThread()
<span class="hljs-keyword">defer</span> runtime.UnlockOSThread()
handle := newHandle()
<span class="hljs-keyword">for</span> f := <span class="hljs-keyword">range</span> in {
err := f(handle)
out <- err
}
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(c *ClientPool)</span> <span class="hljs-title">Run</span><span class="hljs-params">(f ClientFunc)</span> <span class="hljs-title">error</span></span> {
e := c.Get().(*executor)
e.In <- f
err, ok := <-e.Out
<span class="hljs-keyword">if</span> !ok || err != <span class="hljs-literal">nil</span> {
<span class="hljs-comment">// une erreur s'est produite, </span>
<span class="hljs-comment">// ou alors la goroutine a paniqué (ce qui a fermé e.Out)</span>
<span class="hljs-built_in">close</span>(e.In)
<span class="hljs-keyword">return</span> err
}
<span class="hljs-comment">// pas d'erreur, on peut retourner le runner au pool</span>
c.Put(e)
<span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
}
<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
start := time.Now()
pool := NewClientPool()
<span class="hljs-keyword">var</span> wg sync.WaitGroup
wg.Add(<span class="hljs-number">100000</span>)
<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < <span class="hljs-number">100000</span>; i++ {
<span class="hljs-keyword">go</span> <span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">()</span></span> {
<span class="hljs-keyword">defer</span> wg.Done()
pool.Run(<span class="hljs-function"><span class="hljs-keyword">func</span><span class="hljs-params">(h Handle)</span> <span class="hljs-title">error</span></span> {
time.Sleep(time.Millisecond)
<span class="hljs-keyword">return</span> <span class="hljs-literal">nil</span>
})
}()
}
wg.Wait()
fmt.Println(<span class="hljs-string">"elapsed: "</span>, time.Since(start))
fmt.Println(<span class="hljs-string">"allocated handles: "</span>, handle)
}
</code></pre></div>
<p>Ici, avec 100K goroutines qui vont taper sur le pool et bosser pendant 1ms chacune, ça me donne :</p>
<div class="hljs-code-div hljs-code-text"><div class="hljs-line-numbers"><span data-count="1"></span><span data-count="2"></span></div><pre><code class="hljs language-text">elapsed: 3.09040842s
allocated handles: 602
</code></pre></div>
<p>C’est sûrement un exemple un peu violent, mais l’intérêt du pool, c’est surtout de réutiliser facilement des ressources qui sont coûteuses à obtenir : ici on voit que même en stressant le pool en lui balançant 100K tâches d’un coup, il n’a créé que 600 (c’est variable en fait, ça oscille entre 400 et 800 chez moi) connexions/goroutines épinglées.</p>