Menu
Coddy logo textTech

Inclusion ou agrégation

Fait partie de la section Programmation Orientée Objet du Journey GO de Coddy. Leçon 38 sur 107.

Vous avez appris que l’intégration promeut les champs et les méthodes vers la structure externe. Mais Go prend également en charge l’agrégation, dans laquelle vous incluez une autre structure en tant que champ nommé. Comprendre quand utiliser chaque approche est essentiel pour une bonne conception.

Avec l’intégration, vous omettez le nom du champ, et les membres du type interne deviennent directement accessibles :

type Writer struct{}
func (w Writer) Write() string { return "writing" }

type Document struct {
    Writer  // embedding - pas de nom de champ
}

d := Document{}
d.Write()  // accès direct

Avec l’agrégation, vous donnez au champ un nom explicite, ce qui vous oblige à utiliser ce nom pour accéder au type interne :

type Document struct {
    writer Writer  // agrégation - champ nommé
}

d := Document{writer: Writer{}}
d.writer.Write()  // doit utiliser le nom du champ

La différence essentielle réside dans la relation que vous exprimez. L’imbrication suggère une relation « est-un », où le type externe adopte l’identité du type interne. L’agrégation suggère une relation « possède-un », où le type externe se contente de contenir le type interne.

Utilisez l’intégration lorsque vous voulez que le type externe satisfasse aux interfaces implémentées par le type interne, ou lorsque l’accès direct aux méthodes rend l’API plus claire. Utilisez l’agrégation lorsque le type interne est un détail d’implémentation qui ne devrait pas être exposé, ou lorsque vous voulez contrôler explicitement la manière dont le type interne est accessible.

challenge icon

Défi

Facile

Construisons un système de génération de rapports qui démontre quand utiliser l’embedding plutôt que l’aggregation. Vous allez créer un système dans lequel certains composants doivent être directement accessibles (embedding), tandis que d’autres doivent rester des détails d’implementation internes (aggregation).

Vous organiserez votre code sur trois fichiers :

  • formatter.go : Create une structure Formatter avec un champ Style (chaîne de caractères) et une méthode Format(text string) string qui renvoie le texte entouré par le style, comme [Style] text [/Style]. Il s’agira d’un helper interne qui ne devrait pas être directement exposé.
  • report.go : Create deux structures qui gèrent la génération de rapports différemment :
    • une structure Metadata avec les champs Author et Date. Elle représente des informations publiques qui doivent être directement accessibles
    • une structure Report avec un champ Title qui embeds Metadata (pour un accès direct à l’auteur et à la date), mais utilise l’aggregation pour Formatter (comme détail d’implementation interne avec un champ nommé formatter)
    Ajoutez une méthode Generate() string à Report qui utilise le formatter interne pour formater le titre et inclut les métadonnées.
  • main.go : Lisez les détails du rapport depuis l’entrée, créez un Report avec des métadonnées intégrées et un formatter agrégé. Démontrez la différence en accédant directement à l’auteur sur le rapport (via l’embedding), puis en générant le rapport formaté (qui utilise le formatter caché en interne).

Les entrées suivantes seront fournies :

  • Ligne 1 : titre du rapport
  • Ligne 2 : nom de l’auteur
  • Ligne 3 : Date
  • Ligne 4 : style du Formatter

Votre méthode Format doit renvoyer :

[Style] text [/Style]

Votre méthode Generate doit renvoyer :

[formatted title]
Author: [Author], Date: [Date]

Dans votre fichier principal, affichez trois lignes :

  1. L’auteur accessed directement sur le Report (démontrant que l’embedding promeut le field)
  2. La date accessed via le nom du type embedded (report.Metadata.Date)
  3. Le résultat de l’appel à Generate()

Par exemple, avec Quarterly Results, Jane Doe, 2024-01-15 et BOLD, votre sortie devrait être :

Jane Doe
2024-01-15
[BOLD] Quarterly Results [/BOLD]
Author: Jane Doe, Date: 2024-01-15

Remarquez comment Author et Date sont accessibles directement sur le Report grâce à l’embedding, tandis que le formatter reste caché. Vous ne pouvez pas appeler directement report.Format(), car il est agrégé avec un champ nommé. Cela démontre concrètement la relation « is-a » par opposition à « has-a ».

Essayez vous-même

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	scanner.Scan()
	title := scanner.Text()
	
	scanner.Scan()
	author := scanner.Text()
	
	scanner.Scan()
	date := scanner.Text()
	
	scanner.Scan()
	style := scanner.Text()
	
	// TODO: Créer une struct Metadata avec l'author et la date
	
	// TODO: Créer un Formatter avec le style donné
	
	// TODO: Créer un Report qui embarque Metadata et agrège Formatter
	
	// TODO: Afficher l'author accédé directement sur le Report (l'embedding promeut le champ)
	
	// TODO: Afficher la date accédée via le nom du type embarqué (report.Metadata.Date)
	
	// TODO: Afficher le résultat de l'appel à Generate()
	
	// Supprimer les avertissements de variables non utilisées (retirez-les lorsque vous utilisez les variables)
	_ = title
	_ = author
	_ = date
	_ = style
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet

Entraînez-vous par vous-même : Compilateur Go en ligne