Menu
Coddy logo textTech

L’interfaccia come contratto

Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 27 di 107.

Pensare alle interfacce come a contratti ti aiuta a progettare programmi Go migliori. Quando una funzione accetta un tipo interfaccia, sta dicendo: "Non mi interessa quale tipo concreto mi dai, purché rispetti questo contratto."

Questo modo di pensare basato sui contratti ti consente di scrivere funzioni che si concentrano sul comportamento anziché su tipi specifici. Considera una funzione che deve elaborare dati provenienti da varie origini:

type DataSource interface {
    FetchData() string
}

func ProcessData(src DataSource) {
    data := src.FetchData()
    fmt.Println("Processing:", data)
}

La funzione ProcessData non sa né le importa se riceve una connessione al database, un client API o un lettore di file. Richiede solo che l'input possa eseguire FetchData(). Qualsiasi tipo che rispetti questo contratto va bene.

type Database struct{ ConnectionString string }
func (d Database) FetchData() string { return "data from DB" }

type APIClient struct{ Endpoint string }
func (a APIClient) FetchData() string { return "data from API" }

func main() {
    db := Database{ConnectionString: "localhost"}
    api := APIClient{Endpoint: "https://api.example.com"}
    
    ProcessData(db)   // Funziona!
    ProcessData(api)  // Funziona anche questo!
}

Anche questo approccio basato sui contratti rende il tuo codice più facile da testare. Durante i test, puoi creare un tipo fittizio che soddisfi l'interfaccia senza bisogno di database reali o connessioni di rete. La funzione sotto test non si accorge della differenza, perché il contratto è rispettato.

challenge icon

Sfida

Facile

Costruiamo un sistema di notifiche che mostri come le interfacce fungano da contratti. Creerai un’interfaccia Notifier che definisce cosa significa inviare una notifica, poi implementerai più canali di notifica che rispettino tutti questo contratto.

Organizzerai il codice in due file:

  • notifiers.go: Definisci un’interfaccia Notifier con un solo metodo Notify(message string) string. Poi crea tre struct diversi che soddisfino questo contratto:
    • ConsoleNotifier con un campo Prefix
    • FileNotifier con un campo Filename
    • WebhookNotifier con un campo URL
    Ogni struct dovrebbe implementare il metodo Notify per restituire una stringa che descriva come verrebbe inviata la notifica.
  • main.go: Crea una funzione chiamata BroadcastAlert che accetti uno slice di valori Notifier e una stringa contenente il messaggio. Questa funzione dovrebbe chiamare Notify su ogni notifier e stampare i risultati. Leggi la configurazione dall’input, crea tutti e tre i tipi di notifier e invia un avviso tramite ciascuno di essi.

Verranno forniti i seguenti input:

  • Riga 1: Prefisso della console
  • Riga 2: Nome del file
  • Riga 3: URL del webhook
  • Riga 4: Messaggio di avviso

I tuoi metodi Notify dovrebbero restituire stringhe con i seguenti formati:

  • ConsoleNotifier: [Prefix] [message]
  • FileNotifier: Writing to [Filename]: [message]
  • WebhookNotifier: POST [URL]: [message]

Per esempio, con ALERT:, log.txt, https://hooks.example.com e Server down!, l’output dovrebbe essere:

ALERT: Server down!
Writing to log.txt: Server down!
POST https://hooks.example.com: Server down!

Il vantaggio di questo design è che la tua funzione BroadcastAlert non conosce né si preoccupa dei tipi specifici di notifier. Richiede solo che ognuno rispetti il contratto Notifier. In seguito potresti aggiungere uno SlackNotifier o un EmailNotifier senza modificare affatto la funzione di invio.

Provalo tu

package main

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

// BroadcastAlert invia un messaggio tramite tutti i notificatori
// TODO: Implementa questa funzione in modo da:
// - Accettare una slice di valori Notifier e una stringa di messaggio
// - Chiamare Notify su ogni notificatore e stampare i risultati
func BroadcastAlert(notifiers []Notifier, message string) {
	// TODO: Scorri notifiers e stampa il risultato di ogni notifica
}

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Leggi il prefisso della console
	scanner.Scan()
	prefix := scanner.Text()
	
	// Leggi il nome del file
	scanner.Scan()
	filename := scanner.Text()
	
	// Leggi l'URL del webhook
	scanner.Scan()
	url := scanner.Text()
	
	// Leggi il messaggio di avviso
	scanner.Scan()
	message := scanner.Text()
	
	// TODO: Crea istanze di tutti e tre i tipi di notificatore
	// - ConsoleNotifier con il prefisso
	// - FileNotifier con filename
	// - WebhookNotifier con l'URL
	
	// TODO: Crea una slice di Notifier contenente tutti e tre i notifier
	
	// TODO: Chiama BroadcastAlert con i notifier e il messaggio
	
	_ = prefix
	_ = filename
	_ = url
	_ = message
	fmt.Println("TODO: Implement the solution")
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Go online