Allons-y Programmation orientée objet à Golang

Go est un étrange mélange d’anciennes et de nouvelles idées. Il a une approche très rafraîchissante où il n’est pas peur de jeter les notions établies de «comment faire les choses». Beaucoup de gens ne savent même pas si Go est un langage orienté objet. Permettez-moi de mettre cela au repos pour le moment. Il est! 

Dans ce tutoriel, vous découvrirez toutes les subtilités de la conception orientée objet dans Go, comment les piliers de la programmation orientée objet, tels que l'encapsulation, l'héritage et le polymorphisme, sont exprimés dans Go et comment ils se comparent à d'autres langages..

Go est un langage de programmation incroyablement puissant. Vous y apprendrez tout, de l'écriture de simples utilitaires à la construction de serveurs Web évolutifs et flexibles dans notre cours complet.

La philosophie du design

Les racines de Go sont basées sur C et plus largement sur la famille Algol. Ken Thompson a dit à moitié en plaisantant que Rob Pike, Robert Granger et lui-même s'étaient réunis et avaient décidé de détester le C ++. Que ce soit une blague ou non, Go est très différent du C ++. Plus sur cela plus tard. Go, c'est la simplicité ultime. Ceci est expliqué en détail par Rob Pike dans Less is exponentiellement plus.

Aller contre d'autres langues

Go n'a pas de classes, pas d'objets, pas d'exceptions ni de modèles. Il a la collecte des ordures et la concurrence simultanée. L'omission la plus frappante en ce qui concerne les objets est qu'il n'y a pas de hiérarchie des types dans Go. Cela contraste avec la plupart des langages orientés objet tels que C ++, Java, C #, Scala et même des langages dynamiques comme Python et Ruby..

Go Fonctionnalités du langage orienté objet

Go n'a pas de cours, mais il a des types. En particulier, il a des structures. Les structures sont des types définis par l'utilisateur. Les types de structures (avec méthodes) servent des buts similaires aux classes dans d'autres langages.

Structs

Une structure définit l'état. Voici une structure de créature. Il possède un champ Nom et un drapeau booléen appelé Réel, qui indique s'il s'agit d'une vraie créature ou d'une créature imaginaire. Les structures ne contiennent que l'état et aucun comportement.

type de créature struct Nom chaîne Real bool 

Les méthodes

Les méthodes sont des fonctions qui opèrent sur des types particuliers. Ils ont une clause de séquestre qui stipule quel type ils opèrent. Voici une Déverser() méthode qui opère sur les structures Creature et affiche leur état:

func (c Créature) Dump () fmt.Printf ("Nom: '% s', Réel:% t \ n", c.Name, c.Real)

C'est une syntaxe inhabituelle, mais elle est très explicite et claire (contrairement au "ceci" implicite ou au "soi" déroutant de Python).

Enrobage

Vous pouvez intégrer des types anonymes les uns dans les autres. Si vous intégrez une structure sans nom, la structure intégrée fournit directement son état (et ses méthodes) à la structure d'intégration. Par exemple, le FlyingCreature a un sans nom Créature struct intégré dans ce qui signifie un FlyingCreature est un Créature.

type FlyingCreature struct Créature WingSpan int

Maintenant, si vous avez une instance de FlyingCreature, vous pouvez accéder directement à ses attributs Name et Real.

dragon: = & FlyingCreature Créature "Dragon", faux,, 15, fmt.Println (dragon.Name) fmt.Println (dragon.Real) fmt.Println (dragon.WingSpan)

Des interfaces

Les interfaces sont la marque du support orienté objet de Go. Les interfaces sont des types qui déclarent des ensembles de méthodes. De la même manière que les interfaces dans d'autres langages, elles n'ont aucune implémentation. 

Les objets qui implémentent toutes les méthodes d'interface implémentent automatiquement l'interface. Il n'y a pas d'héritage, de sous-classement ou de mot clé "implémente". Dans l'extrait de code suivant, le type Foo implémente l'interface Fooer (par convention, les noms des interfaces Go se terminent par "er").

interface de type Fooer Foo1 () Foo2 () Foo3 () type Foo struct  func (f Foo) Foo1 () fmt.Println ("Foo1 () ici") func (f Foo) Foo2 () fmt .Println ("Foo2 () ici") func (f Foo) Foo3 () fmt.Println ("Foo3 () ici")

Conception orientée objet: The Go Way

Voyons comment Go se compare aux piliers de la programmation orientée objet: encapsulation, héritage et polymorphisme. Ce sont les fonctionnalités des langages de programmation basés sur les classes, qui sont les langages de programmation orientés objet les plus populaires..

Au centre, les objets sont des constructions de langage dont l'état et le comportement agissent sur l'état et l'exposent de manière sélective à d'autres parties du programme.. 

Encapsulation

Go encapsule les choses au niveau du paquet. Les noms commençant par une lettre minuscule ne sont visibles que dans ce package. Vous pouvez masquer n'importe quoi dans un paquet privé et simplement exposer des types, interfaces et fonctions d'usine spécifiques. 

Par exemple, ici pour masquer le Foo tapez ci-dessus et exposez uniquement l'interface, vous pouvez le renommer en minuscule foo et fournir un NewFoo ()fonction qui renvoie l'interface publique Fooer:

tapez foo struct  func (foo) Foo1 () fmt.Println ("Foo1 () ici") func (f foo) Foo2 () fmt.Println ("Foo2 () ici") func (f foo) Foo3 () fmt.Println ("Foo3 () ici") func NewFoo () Fooer return & Foo 

Alors le code d'un autre paquet peut utiliser NewFoo () et avoir accès à un Fooer interface mise en œuvre par l'interne foo type:

f: = NewFoo () f.Foo1 () f.Foo2 () f.Foo3 ()

Héritage

L'héritage ou le sous-classement a toujours été une question controversée. L'héritage d'implémentation pose de nombreux problèmes (par opposition à l'héritage d'interface). L'héritage multiple tel que mis en œuvre par C ++, Python et d'autres langages souffre du problème mortel du diamant de la mort, mais même l'héritage simple n'est pas un pique-nique avec le problème fragile de la classe de base. 

Les langues modernes et la pensée orientée objet privilégient désormais la composition au détriment de l'héritage. Go le prend à cœur et n’a aucune hiérarchie de types. Il vous permet de partager les détails de la mise en œuvre via la composition. Mais Go, dans une torsion très étrange (qui a probablement pour origine des préoccupations pragmatiques), permet la composition anonyme via l'intégration. 

À toutes fins utiles, la composition en incorporant un type anonyme équivaut à l'héritage d'implémentation. Une structure intégrée est aussi fragile qu'une classe de base. Vous pouvez également intégrer une interface, ce qui revient à hériter d'une interface dans des langages tels que Java ou C ++. Cela peut même conduire à une erreur d'exécution qui n'est pas découverte à la compilation si le type d'intégration ne met pas en œuvre toutes les méthodes d'interface. 

Ici, SuperFoo intègre l'interface Fooer, mais n'implémente pas ses méthodes. Le compilateur Go vous laissera volontiers créer un nouveau SuperFoo et appeler les méthodes Fooer, mais échouera évidemment au moment de l'exécution. Cela compile:

tapez SuperFooer struct Fooer func main () s: = SuperFooer  s.Foo2 ()

L'exécution de ce programme provoque une panique:

panique: erreur d'exécution: adresse mémoire non valide ou déréférence de pointeur nil [signal 0xb code = 0x1 addr = 0x28 pc = 0x2a78] goroutine 1 [en cours d'exécution]: panique (0xde180, 0xc82000a0d0) /usr/local/Cellar/go/1.6/libexec/ src / runtime / panic.go: 464 + 0x3e6 main.main () /Users/gigi/Documents/dev/go/src/github.com/oop_test/main.go:104 + 0x48 état de sortie 2 Processus terminé avec code de sortie 1

Polymorphisme

Le polymorphisme est l'essence même de la programmation orientée objet: la possibilité de traiter des objets de types différents de manière uniforme, à condition qu'ils adhèrent à la même interface. Les interfaces Go fournissent cette fonctionnalité de manière très directe et intuitive. 

Voici un exemple élaboré où plusieurs créatures (et une porte!) Qui implémentent l'interface Dumper sont créées et stockées dans une tranche, puis le Déverser() La méthode est appelée pour chacun. Vous remarquerez également différents styles d'instanciation des objets.

package principal import "fmt" type de créature struct Nom chaîne réelle boolé func Dump (c * créature) fmt.Printf ("Nom: '% s', réel:% t \ n", c.Name, c.Real ) func (c Créature) Dump () fmt.Printf ("Nom: '% s', Réel:% t \ n", c.Name, c.Real) type FlyingCreature struct Creature WingSpan int func ( fc FlyingCreature) Dump () fmt.Printf ("Nom: '% s', Réel:% t, WingSpan:% d \ n", fc.Name, fc.Real, fc.WingSpan) tapez Unicorn struct Creature  type Dragon struct FlyingCreature type Pterodactyl struct FlyingCreature func NouveauPterodactyl (wingSpan int) * Pterodactyl animal: = & Pterodactyl  FlyingCreature Creature "Pterodactyl", true,, wingSpan,, renvoi animal type interface Dump () type Door struct Épaisseur int Color string func (d Door) Dump () fmt.Printf ("Door => Thickness:% d, Color:% s", d.Epaisseur, d.Color)  func main () creature: = & Creature "une créature", false, uni: = Unicorn Creature "Unicorn", false,, pet1: = & & Pterodactyl FlyingCreature Creature "Pterodactyl", true,, 5,, pet2: = NewPterodactyl (8) door: = & & Door 3, "red" Dump (créature) créature.Dump () uni.Dump () pet1.Dump ( ) pet2.Dump () creatures: = [] Créature * creature, uni.Creature, pet1.Creature, pet2.Creature fmt.Println ("Dump () dans le type imbriqué de créature") pour _, creature: = range creatures creature.Dump () dumpers: = [] Dumper creature, uni, pet1, pet2, door fmt.Println ("Dump () via l'interface Dumper") pour _, dumper: = plage dumpers dumper.Dump ( )

Conclusion

Go est un véritable langage de programmation orienté objet. Il permet la modélisation par objets et encourage l'utilisation des interfaces au lieu des hiérarchies de types concrets. Go a fait des choix syntaxiques inhabituels, mais dans l’ensemble, travailler avec des types, des méthodes et des interfaces semble simple, léger et naturel.. 

L’incorporation n’est pas très pure, mais apparemment, le pragmatisme était à l’œuvre et l’incorporation a été fournie au lieu de la composition uniquement par son nom..