Introdução aos Padrões de Projeto
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 85 de 107.
Padrões de projeto são soluções reutilizáveis para problemas comuns que surgem no design de software. Eles não são códigos que você copia diretamente, mas sim modelos que orientam como você estrutura seus tipos e suas interações para resolver desafios específicos.
Padrões de projeto são tipicamente agrupados em três categorias:
| Categoria | Propósito | Exemplos |
|---|---|---|
| Criacional | Mecanismos de criação de objetos | Singleton, Factory, Builder |
| Estrutural | Composição de tipos em estruturas maiores | Adapter, Decorator, Composite |
| Comportamental | Comunicação entre objetos | Observer, Strategy, Command |
Em Go, os padrões de projeto parecem diferentes do que em linguagens POO tradicionais.
Sem classes ou herança, o Go implementa padrões usando interfaces, incorporação de structs e composição. Por exemplo, onde o Java poderia usar uma classe abstrata, o Go usa uma interface. Onde o C++ poderia usar herança, o Go usa incorporação.
Aprender padrões ajuda você a reconhecer problemas comuns e a comunicar soluções de forma eficaz com outros desenvolvedores. Quando alguém menciona "use o padrão Strategy aqui", toda a equipe entende a abordagem sem a necessidade de explicações longas.
Nas próximas lições, você implementará vários padrões fundamentais em Go, começando com padrões de criação como Singleton e Factory, e depois passando para padrões comportamentais como Observer e Strategy.
Desafio
FácilVamos construir um sistema de notificação simples que demonstra como os padrões de design ajudam a organizar o código! Você criará uma estrutura que poderá ser facilmente estendida com padrões como Observer ou Strategy em lições futuras — mas, por enquanto, concentre-se em configurar interfaces e tipos limpos que sigam bons princípios de OOP.
Você organizará seu código em três arquivos:
notifier.go: Defina a interface principal e os tipos para o seu sistema de notificação.Crie uma interface
Notifiercom um único métodoSend(message string) stringque retorna uma mensagem de confirmação.Implemente dois tipos concretos de notificador:
EmailNotifiercom um campoAddress— seu métodoSendretornaEmail to [address]: [message]SMSNotifiercom um campoPhoneNumber— seu métodoSendretornaSMS to [phone]: [message]
dispatcher.go: Crie um despachante que funcione com qualquer notificador por meio da interface.Construa uma struct
NotificationDispatcherque contenha um slice de interfacesNotifier. Adicione estes métodos:AddNotifier(n Notifier)para registrar um notificadorBroadcast(message string) []stringque envia a mensagem através de todos os notificadores registrados e retorna um slice de todas as strings de confirmação
Também crie um construtor
NewNotificationDispatcher()que retorna um despachante inicializado.main.go: Configure os notificadores e transmita as mensagens.Leia uma contagem de notificadores a serem criados. Para cada notificador, leia seu tipo (
emailousms) e seu destino (endereço ou número de telefone). Adicione cada um a um despachante.Em seguida, leia uma mensagem para transmitir. Envie-a através de todos os notificadores e imprima cada confirmação em sua própria linha.
As seguintes entradas serão fornecidas:
- Linha 1: Número de notificadores
- Para cada notificador: tipo (
emailousms), depois o destino - Linha final: Mensagem para transmitir
Por exemplo, dado:
3
email
alice@example.com
sms
555-1234
email
bob@example.com
Server maintenance at midnightSua saída deve ser:
Email to alice@example.com: Server maintenance at midnight
SMS to 555-1234: Server maintenance at midnight
Email to bob@example.com: Server maintenance at midnightE dado:
2
sms
555-9999
sms
555-0000
Meeting canceledSua saída deve ser:
SMS to 555-9999: Meeting canceled
SMS to 555-0000: Meeting canceledEsta estrutura demonstra como as interfaces permitem designs flexíveis e extensíveis. O despachante não conhece os tipos específicos de notificador — ele apenas sabe que eles podem Send. Esta é a base sobre a qual padrões como Observer e Strategy são construídos!
Folha de consulta
Padrões de projeto são soluções reutilizáveis para problemas comuns no design de software. Eles são modelos que orientam como estruturar tipos e suas interações.
Padrões de projeto são agrupados em três categorias:
| Categoria | Objetivo | Exemplos |
|---|---|---|
| Criacional | Mecanismos de criação de objetos | Singleton, Factory, Builder |
| Estrutural | Composição de tipos em estruturas maiores | Adapter, Decorator, Composite |
| Comportamental | Comunicação entre objetos | Observer, Strategy, Command |
Em Go, os padrões de projeto são implementados usando interfaces, incorporação de structs e composição, em vez de classes e herança. Onde as linguagens POO tradicionais usam classes abstratas, Go usa interfaces. Onde elas usam herança, Go usa incorporação.
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Lê o número de notificadores
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
// Cria um novo despachante
dispatcher := NewNotificationDispatcher()
// Lê cada notificador e adiciona ao despachante
for i := 0; i < count; i++ {
notifierType, _ := reader.ReadString('\n')
notifierType = strings.TrimSpace(notifierType)
destination, _ := reader.ReadString('\n')
destination = strings.TrimSpace(destination)
// TODO: Cria o notificador apropriado com base no tipo
// e adiciona-o ao despachante
// Dica: Verifique se notifierType é "email" ou "sms"
}
// Lê a mensagem para transmitir
message, _ := reader.ReadString('\n')
message = strings.TrimSpace(message)
// TODO: Transmite a mensagem e imprime cada confirmação
}
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 ExternosGo Workspace e MódulosPacotes e ImportaçõesNomes Exportados vs Não ExportadosIntrodução a OOP em GoStructs como ClassesDefinindo Métodos em StructsRecebedores de Ponteiro vs ValorInicialização de StructsFunções ConstrutorasRecapitulação - Calculadora Simples4Interfaces
Introdução a InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Type AssertionType SwitchComposição de InterfacesInterfaces Stringer & ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs Não ExportadosEncapsulamento em Nível de PacoteMétodos Getter e SetterOcultação de Informação em GoRecapitulação - Registros de Estudantes10Generics (Go 1.18+)
Introdução a GenericsParâmetros de TipoRestrições de TipoStructs GenéricasSolução Alternativa 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 Strategy2Mergulho Profundo em Tipos e Structs
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosStruct TagsStructs AnônimasStructs AninhadasZero Values e PadrõesRecapitulação - Agenda de Contatos5Composição em vez de Herança
Por que Go não possui HerançaFundamentos de Incorporação de StructsPromoção de MétodosIncorporando Múltiplas StructsIncorporação vs AgregaçãoShadowing de Métodos IncorporadosRecapitulação - Hierarquia de Funcionários8Tratamento de Erros e OOP
A Interface errorTipos de Erro PersonalizadosError Wrapping (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - File Parser3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs ReferênciaA Função new()Garbage Collection em GoRecapitulação - Construtor de Lista Encadeada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfaceColeções PolimórficasInjeção de DependênciaRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e ComunicaçãoCanais com Buffer vs. Sem BufferInstrução Selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de Structs Thread-SafeRecapitulação - Worker Pool