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 Command encapsula ações, Adapter traduz uma interface em outra que os clientes esperam.
Imagine que você tenha uma impressora legada que usa uma assinatura de método diferente da esperada pela sua aplicação:
// Interface alvo que sua aplicação usa
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 requisitos da sua interface. Ele atua como uma ponte sem modificar o código original.
Desafio
FácilVamos criar um sistema de reprodução de mídia usando o padrão Adapter! Você tem um reprodutor de áudio moderno que espera uma interface unificada, mas precisa integrar equipamentos de áudio legados que usam assinaturas de métodos 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-alvo e um dispositivo de áudio legado.Crie uma interface
AudioPlayerque seu sistema moderno espera, com um métodoPlay(track string) stringque recebe o nome de uma faixa e retorna uma mensagem de reprodução.Defina também um dispositivo legado que não corresponde a essa interface:
- struct
VinylPlayercom um campoSpeedRPM(int). Ele possui um métodoDropNeedle(record string, side string) stringque retornaPlaying [record] ([side] side) at [speed]rpm
A interface de VinylPlayer é incompatível com AudioPlayer. Ela exige dois parâmetros e usa um nome de método completamente diferente.
- struct
adapter.go: crie seu adaptador para tornar o dispositivo legado compatível.Crie um struct
VinylAdapterque encapsule umVinylPlayere implemente a interfaceAudioPlayer. QuandoPlayfor chamado, o adaptador deverá chamar o métodoDropNeedledo reprodutor de vinil, usandoAcomo padrão para o parâmetro do lado.Crie também um reprodutor moderno para comparação:
- struct
DigitalPlayer: seu métodoPlayretornaStreaming: [track]
- struct
main.go: demonstre os dois reprodutores funcionando por meio da mesma interface.Crie uma função
PlayMusic(player AudioPlayer, track string) stringque use a interface para reproduzir música. Essa função não deve saber nem se importar se está usando um reprodutor digital ou um reprodutor de vinil adaptado.Leia um tipo de reprodutor (
digitalouvinyl) e, se for vinyl, leia também a velocidade em RPM. Em seguida, leia o nome da faixa. Crie o reprodutor apropriado (usando o adaptador para vinyl), reproduza a faixa por meio da funçãoPlayMusice imprima o resultado.
As entradas a seguir serão fornecidas:
- Linha 1: tipo de reprodutor (
digitalouvinyl) - Linha 2: velocidade em RPM (somente se for vinyl)
- Última linha: nome da faixa a ser reproduzida
Por exemplo, dada a entrada:
digital
Bohemian RhapsodySua saída deverá ser:
Streaming: Bohemian RhapsodyE dada a entrada:
vinyl
33
Abbey RoadSua saída deverá ser:
Playing Abbey Road (A side) at 33rpmE dada a entrada:
vinyl
45
Blue MondaySua saída deverá ser:
Playing Blue Monday (A side) at 45rpmObserve como a função PlayMusic funciona de forma idêntica com os dois reprodutores. Ela não faz ideia de que o reprodutor 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!
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 vinyl
rpmStr, _ := reader.ReadString('\n')
rpmStr = strings.TrimSpace(rpmStr)
rpm, _ := strconv.Atoi(rpmStr)
// Lê o nome da track
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 usar rpm
} else {
// Lê o nome da track
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
}
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
1Fundamentos de OOP em Go
Arquivos ExternosWorkspace e Módulos do GoPacotes e ImportsNomes Exportados vs. Não ExportadosIntrodução à OOP em GoStructs como ClassesDefinindo Métodos em StructsReceptores de Ponteiro vs. de ValorInicialização de StructsFunções ConstrutorasRecapitulação — Calculadora Simples4Interfaces
Introdução às InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Asserção de TipoSwitch de TipoComposição de InterfacesInterfaces Stringer e ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs. Não ExportadosEncapsulamento no Nível de PacoteMétodos Getters e SettersOcultação de Informações em GoRecapitulação - Registros de Alunos10Genéricos (Go 1.18+)
Introdução aos genéricosParâmetros de tipoRestrições de tipoStructs genéricasAlternativa para métodos genéricosRecapitulação - Coleção genérica13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão Abstract FactoryPadrão ObserverPadrão Strategy2Tipos e Structs em Profundidade
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosTags de StructStructs AnônimasStructs AninhadasValores Zero e PadrõesRecapitulação - Livro de Contatos5Composição em vez de herança
Por que Go não tem herançaNoções básicas de incorporação de structsPromoção de métodosIncorporação de várias structsIncorporação vs agregaçãoOcultação de métodos incorporadosRecapitulação - Hierarquia de funcionários8Tratamento de Erros e OOP
A interface errorTipos de Erro PersonalizadosEncapsulamento de Erros (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - Analisador de Arquivos11Biblioteca Padrão e POO
io.Reader e io.Writersort.InterfaceInterface fmt.Stringerencoding/json com StructsInterface http.HandlerRevisão - Modelos de API REST14Padrões de Projeto — Parte 2
Padrão de ComandoPadrão AdapterPadrão DecoratorPadrão de Método TemplatePadrão de EstadoPadrão CompositeMiddleware como Decorator3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs. ReferênciaA Função new()Coleta de Lixo em GoRecapitulação - Construtor de Lista Ligada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfacesColeções PolimórficasInjeção de DependênciasRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e comunicaçãoCanais com e sem bufferInstrução selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de structs thread-safeRevisão - Worker PoolPratique por conta própria: Compilador de Go online