Menu
Coddy logo textTech

Por que Go não tem herança

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 34 de 107.

Linguagens tradicionais orientadas a objetos, como Java e C++, usam herança para compartilhar código entre tipos. Uma classe filha estende uma classe pai, herdando todos os seus campos e métodos. Go deliberadamente omite esse recurso.

A herança cria um forte acoplamento entre os tipos. Quando uma classe pai muda, todas as classes filhas são afetadas.

Hierarquias de herança profundas se tornam difíceis de entender e manter. O "problema da classe base frágil" ocorre quando modificações em uma classe base quebram inesperadamente as classes derivadas.

Os designers de Go escolheram um caminho diferente: composição em vez de herança. Em vez de dizer "um Dog é um Animal", Go incentiva você a dizer "um Dog has comportamentos semelhantes aos de Animal". Essa mudança sutil leva a um código mais flexível e fácil de manter.

Go alcança a reutilização de código por meio de dois mecanismos que você já aprendeu:

  • Interfaces definem contratos de comportamento sem detalhes de implementação
  • Incorporação de structs permite que os tipos incluam outros tipos e reutilizem seus métodos

Considere esta comparação. Na OOP tradicional, você poderia escrever class Dog extends Animal. Em Go, você incorpora uma struct Animal dentro de Dog e implementa interfaces compartilhadas. O resultado é uma funcionalidade semelhante, com um acoplamento mais fraco entre os tipos.

Este capítulo explora a incorporação de estruturas em profundidade, mostrando como Go obtém os benefícios da herança sem suas desvantagens.

challenge icon

Desafio

Fácil

Vamos criar um sistema de notificações que demonstra a abordagem de composição do Go em vez de herança. Você criará tipos que compartilham comportamento por meio de interfaces e incorporação de structs, em vez de hierarquias de classes.

Você organizará seu código em três arquivos:

  • notifier.go: Defina uma interface Notifier com um único método Notify(message string) string. Crie também uma struct BaseNotifier com um campo Name que será incorporada por outros tipos. Dê à BaseNotifier um método chamado Format(message string) string que retorna a mensagem prefixada com o nome do notificador entre colchetes.
  • channels.go: Crie dois tipos de canais de notificação que incorporem BaseNotifier e implementem a interface Notifier:
    • EmailNotifier com um campo adicional Address
    • SMSNotifier com um campo adicional Phone
    O método Notify de cada tipo deve usar o método incorporado Format e incluir as informações específicas de seu canal na saída.
  • main.go: Crie uma função chamada SendAlert que aceite qualquer Notifier e uma mensagem, e então retorne o resultado da chamada de Notify. Leia os detalhes da notificação da entrada, crie os dois tipos de notificadores e demonstre como eles podem ser usados de forma intercambiável por meio da interface.

As seguintes entradas serão fornecidas:

  • Linha 1: Nome do notificador para e-mail
  • Linha 2: Endereço de e-mail
  • Linha 3: Nome do notificador para SMS
  • Linha 4: Número de telefone
  • Linha 5: Mensagem de alerta

Seu método Format em BaseNotifier deve retornar:

[Name] message

Seus métodos Notify devem retornar:

  • EmailNotifier: Email to [Address]: [formatted message]
  • SMSNotifier: SMS to [Phone]: [formatted message]

Por exemplo, dadas as entradas Alerts, user@mail.com, Urgent, 555-1234 e Server down, sua saída deve ser:

Email to user@mail.com: [Alerts] Server down
SMS to 555-1234: [Urgent] Server down

Observe como os dois tipos de notificadores reutilizam o método Format de BaseNotifier por meio da incorporação, enquanto cada um fornece sua própria implementação de Notify. A função SendAlert funciona com qualquer Notifier sem conhecer o tipo concreto. Isso é composição em vez de herança na prática.

Experimente você mesmo

package main

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

// SendAlert aceita qualquer Notifier e uma mensagem, retorna o resultado de chamar Notify
// TODO: Implemente a função SendAlert

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// Lê o nome do notificador de email
	scanner.Scan()
	emailName := scanner.Text()
	
	// Lê o endereço de email
	scanner.Scan()
	emailAddress := scanner.Text()
	
	// Lê o nome do notificador de SMS
	scanner.Scan()
	smsName := scanner.Text()
	
	// Lê o número de telefone
	scanner.Scan()
	phoneNumber := scanner.Text()
	
	// Lê a mensagem de alerta
	scanner.Scan()
	alertMessage := scanner.Text()
	
	// TODO: Crie um EmailNotifier com emailName e emailAddress
	
	// TODO: Crie um SMSNotifier com smsName e phoneNumber
	
	// TODO: Use SendAlert para enviar o alertMessage através de ambos os notificadores
	// e imprima os resultados
	
	// Suprima avisos de variáveis não utilizadas (remova estes quando usar as variáveis)
	_ = emailName
	_ = emailAddress
	_ = smsName
	_ = phoneNumber
	_ = alertMessage
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de Go online