Menu
Coddy logo textTech

Padrão Adapter

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 92 de 107.

O padrão Adapter permite que interfaces incompatíveis trabalhem juntas ao envolver um tipo existente com uma nova interface. Enquanto o Command encapsula ações, o Adapter traduz uma interface em outra que os clientes esperam.

Imagine que você tem uma impressora legada que usa uma assinatura de método diferente da que sua aplicação espera:

// Interface alvo que sua aplicação utiliza
type Printer interface {
    Print(text string) string
}

// Impressora legada com interface incompatível
type OldPrinter struct{}

func (o OldPrinter) PrintDocument(doc string, copies int) string {
    return fmt.Sprintf("Old printer: %s (x%d)", doc, copies)
}

O adaptador envolve o tipo legado e implementa a interface esperada:

type OldPrinterAdapter struct {
    oldPrinter OldPrinter
}

func (a OldPrinterAdapter) Print(text string) string {
    return a.oldPrinter.PrintDocument(text, 1)
}

Agora a impressora legada funciona perfeitamente com o código que espera a interface Printer:

func PrintMessage(p Printer, msg string) string {
    return p.Print(msg)
}

adapter := OldPrinterAdapter{oldPrinter: OldPrinter{}}
result := PrintMessage(adapter, "Hello")  // "Old printer: Hello (x1)"

O padrão Adapter é inestimável ao integrar bibliotecas de terceiros, trabalhar com código legado ou quando você precisa usar classes existentes que não correspondem aos seus requisitos de interface. Ele atua como uma ponte sem modificar o código original.

challenge icon

Desafio

Fácil

Vamos construir um sistema de player de mídia usando o padrão Adapter! Você tem um player de áudio moderno que espera uma interface unificada, mas precisa integrar equipamentos de áudio legados que usam assinaturas de método completamente diferentes. Seu adaptador fará a ponte entre essa lacuna, permitindo que o hardware antigo funcione perfeitamente com o novo sistema.

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

  • player.go: Defina sua interface de destino e um dispositivo de áudio legado.

    Crie uma interface AudioPlayer que seu sistema moderno espera, com um método Play(track string) string que recebe o nome de uma faixa e retorna uma mensagem de reprodução.

    Também defina um dispositivo legado que não corresponde a esta interface:

    • Estrutura VinylPlayer com um campo SpeedRPM (int) — ela possui um método DropNeedle(record string, side string) string que retorna Playing [record] ([side] side) at [speed]rpm

    A interface do VinylPlayer é incompatível com o AudioPlayer — ela requer dois parâmetros e usa um nome de método completamente diferente.

  • adapter.go: Crie seu adaptador que torna o dispositivo legado compatível.

    Construa uma estrutura VinylAdapter que envolve um VinylPlayer e implementa a interface AudioPlayer. Quando Play for chamado, o adaptador deve chamar o método DropNeedle do player de vinil, definindo A como padrão para o parâmetro side.

    Também crie um player moderno para comparação:

    • Estrutura DigitalPlayer — seu método Play retorna Streaming: [track]
  • main.go: Demonstre ambos os players funcionando através da mesma interface.

    Crie uma função PlayMusic(player AudioPlayer, track string) string que usa a interface para tocar música — esta função não deve saber ou se importar se está usando um player digital ou um player de vinil adaptado.

    Leia um tipo de player (digital ou vinyl) e, se for vinil, leia também a velocidade RPM. Em seguida, leia o nome da faixa. Crie o player apropriado (usando o adaptador para vinil), toque a faixa através da sua função PlayMusic e imprima o resultado.

As seguintes entradas serão fornecidas:

  • Linha 1: Tipo de player (digital ou vinyl)
  • Linha 2: Velocidade RPM (apenas se for vinil)
  • Última linha: Nome da faixa para tocar

Por exemplo, dado:

digital
Bohemian Rhapsody

Sua saída deve ser:

Streaming: Bohemian Rhapsody

E dado:

vinyl
33
Abbey Road

Sua saída deve ser:

Playing Abbey Road (A side) at 33rpm

E dado:

vinyl
45
Blue Monday

Sua saída deve ser:

Playing Blue Monday (A side) at 45rpm

Observe como a função PlayMusic funciona de forma idêntica com ambos os players — ela não tem ideia de que o player de vinil está, na verdade, sendo adaptado de uma interface completamente diferente. O adaptador traduz as chamadas da interface moderna para o formato legado nos bastidores!

Folha de consulta

O padrão Adapter permite que interfaces incompatíveis trabalhem juntas ao envolver um tipo existente com uma nova interface.

Defina uma interface de destino que sua aplicação espera:

type Printer interface {
    Print(text string) string
}

Um tipo legado com uma interface incompatível:

type OldPrinter struct{}

func (o OldPrinter) PrintDocument(doc string, copies int) string {
    return fmt.Sprintf("Old printer: %s (x%d)", doc, copies)
}

Crie um adaptador que envolva o tipo legado e implemente a interface de destino:

type OldPrinterAdapter struct {
    oldPrinter OldPrinter
}

func (a OldPrinterAdapter) Print(text string) string {
    return a.oldPrinter.PrintDocument(text, 1)
}

Use o adaptador para fazer o tipo legado funcionar com o código que espera a interface de destino:

func PrintMessage(p Printer, msg string) string {
    return p.Print(msg)
}

adapter := OldPrinterAdapter{oldPrinter: OldPrinter{}}
result := PrintMessage(adapter, "Hello")  // "Old printer: Hello (x1)"

O padrão Adapter é útil para integrar bibliotecas de terceiros, trabalhar com código legado ou adaptar classes existentes para corresponder às interfaces exigidas sem modificar o código original.

Experimente você mesmo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// PlayMusic usa a interface AudioPlayer para tocar música
// Esta função funciona com qualquer implementação de AudioPlayer
// TODO: Implemente esta função para chamar player.Play(track) e retornar o resultado
func PlayMusic(player AudioPlayer, track string) string {
	// TODO: Implemente esta função
	return ""
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Lê o tipo de player
	playerType, _ := reader.ReadString('\n')
	playerType = strings.TrimSpace(playerType)
	
	var player AudioPlayer
	var track string
	
	if playerType == "vinyl" {
		// Lê a velocidade RPM para vinil
		rpmStr, _ := reader.ReadString('\n')
		rpmStr = strings.TrimSpace(rpmStr)
		rpm, _ := strconv.Atoi(rpmStr)
		
		// Lê o nome da faixa
		track, _ = reader.ReadString('\n')
		track = strings.TrimSpace(track)
		
		// TODO: Crie um VinylPlayer com o RPM fornecido
		// TODO: Envolva-o em um VinylAdapter
		// TODO: Atribua à variável player
		_ = rpm // Remova esta linha quando você usar rpm
	} else {
		// Lê o nome da faixa
		track, _ = reader.ReadString('\n')
		track = strings.TrimSpace(track)
		
		// TODO: Crie um DigitalPlayer
		// TODO: Atribua à variável player
	}
	
	// TODO: Chame PlayMusic e imprima o resultado
	_ = player // Remova esta linha quando você usar player
	_ = track  // Remova esta linha quando você usar track
}
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