Introduction : Le débat éternel dans le monde de Go
\n\nGo est un langage qui a brisé les schémas. Il ne pardonne pas la complexité excessive, mais offre au développeur une liberté incroyable. Quiconque commence à coder en Go (ou migre depuis Python ou Java) finit tôt ou tard par se heurter à un mur : « Quelle est la bonne manière ? Écrire comme en Java avec des classes et de l'héritage, ou comme en Haskell avec des fonctions sans effets de bord ? »
\n\nRévélation : Go n'est ni un langage purement orienté objet, ni purement fonctionnel. C'est un pragmatique. Mais cela ne signifie pas que vous ne pouvez pas (et ne devez pas) utiliser les deux paradigmes. Dans cet article, nous verrons comment combiner l'approche fonctionnelle et orientée objet en Go pour que votre code soit lisible, testable et rapide. Nous ne ferons pas de cours magistraux, mais nous examinerons des exemples concrets et des anti-patrons.
\n\nSi vous cherchez les meilleures pratiques Go et souhaitez comprendre comment écrire du code propre dans ce langage, vous êtes au bon endroit.
\n\n1. Approche orientée objet en Go : Sans classes, mais avec de l'âme
\n\nBeaucoup pensent que puisque Go n'a pas de class, il n'y a pas de POO. C'est un mythe. La POO en Go repose sur deux piliers : les structures (structs) et les interfaces (interfaces). Il n'y a pas d'héritage, mais il y a la composition, que les créateurs du langage considèrent comme plus flexible.
Structures et méthodes
\nVous pouvez définir des méthodes pour n'importe quel type. C'est la base du code orienté objet.
\n\npackage main\n\nimport "fmt"\n\n// Définition d'une interface (contrat)\ntype Notifier interface {\n Send(message string) error\n}\n\n// Structure implémentant l'interface\ntype EmailNotifier struct {\n Email string\n}\n\n// Méthode pour EmailNotifier\nfunc (e EmailNotifier) Send(message string) error {\n fmt.Printf("Envoi d'email à %s : %s\\n", e.Email, message)\n return nil\n}\n\n// Une autre structure\ntype SMSNotifier struct {\n Phone string\n}\n\nfunc (s SMSNotifier) Send(message string) error {\n fmt.Printf("Envoi de SMS à %s : %s\\n", s.Phone, message)\n return nil\n}\n\n// Fonction qui accepte n'importe quel Notifier (polymorphisme)\nfunc AlertAll(notifiers []Notifier, msg string) {\n for _, n := range notifiers {\n n.Send(msg)\n }\n}\n\nfunc main() {\n notifiers := []Notifier{\n EmailNotifier{Email: "user@example.com"},\n SMSNotifier{Phone: "+1234567890"},\n }\n AlertAll(notifiers, "Le serveur est tombé !")\n}\n\nAvantages de la POO en Go :
\n- \n
- Polymorphisme via les interfaces. Vous écrivez une fois la fonction
AlertAll, et elle fonctionne avec n'importe quel type qui implémenteNotifier. C'est un couplage faible. \n - État. Les structures sont idéales pour les modèles de données (User, Order, Config). \n
- Encapsulation. Les champs en minuscule sont privés, ceux en majuscule sont publics. Simple et efficace. \n
Inconvénients :
\n- \n
- Il est facile de tomber dans le « style Java » : créer des hiérarchies profondes, même si Go ne les supporte pas. \n
- La mutabilité (changement d'état) complique les tests et le débogage. \n
2. Programmation fonctionnelle en Go : La pureté pour la vitesse
\n\nLe style fonctionnel en Go n'est pas le calcul lambda, mais plutôt un ensemble de principes : minimum d'effets de bord, immuabilité, fonctions comme arguments. Avec l'arrivée des génériques (Go 1.18+), ce style est devenu plus puissant.
\n\nFonctions pures et immuabilité
\nLa règle principale de la programmation fonctionnelle : une fonction reçoit des données, retourne de nouvelles données, mais ne modifie rien à l'extérieur.
\n\npackage main\n\nimport "fmt"\n\n// Mauvais : on mute la tranche d'entrée (effet de bord)\nfunc BadAddPrefix(users []string, prefix string) {\n for i := range users {\n users[i] = prefix + users[i]\n }\n}\n\n// Bon : on retourne une nouvelle tranche (fonction pure)\