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 projeto de software. Eles não são códigos que você copia diretamente, mas sim modelos que orientam como estruturar seus tipos e suas interações para resolver desafios específicos.
Os padrões de projeto normalmente são agrupados em três categorias:
| Categoria | Finalidade | Exemplos |
|---|---|---|
| Criacionais | Mecanismos de criação de objetos | Singleton, Factory, Builder |
| Estruturais | Composição de tipos em estruturas maiores | Adapter, Decorator, Composite |
| Comportamentais | Comunicação entre objetos | Observer, Strategy, Command |
Em Go, os padrões de projeto são diferentes dos padrões em linguagens tradicionais de POO.
Sem classes ou herança, Go implementa padrões usando interfaces, incorporação de structs e composição. Por exemplo, onde Java poderia usar uma classe abstrata, Go usa uma interface. Onde C++ poderia usar herança, 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 explicações longas.
Nas próximas lições, você implementará vários padrões fundamentais em Go, começando com padrões criacionais, como Singleton e Factory, e depois avançando para padrões comportamentais, como Observer e Strategy.
Desafio
FácilVamos criar um sistema simples de notificações que demonstra como os padrões de projeto 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 bem organizados 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 do seu sistema de notificações.Crie uma interface
Notifiercom um único métodoSend(message string) stringque retorna uma mensagem de confirmação.Implemente dois tipos concretos de notificadores:
EmailNotifiercom um campoAddress. Seu métodoSendretornaEmail to [address]: [message]SMSNotifiercom um campoPhoneNumber. Seu métodoSendretornaSMS to [phone]: [message]
dispatcher.go: Crie um dispatcher que funcione com qualquer notificador por meio da interface.Crie uma struct
NotificationDispatcherque contenha uma slice de interfacesNotifier. Adicione estes métodos:AddNotifier(n Notifier)para registrar um notificadorBroadcast(message string) []stringque envia a mensagem por meio de todos os notificadores registrados e retorna uma slice com todas as strings de confirmação
Crie também um construtor
NewNotificationDispatcher()que retorne um dispatcher inicializado.main.go: Configure os notificadores e transmita as mensagens.Leia uma quantidade 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 dispatcher.Em seguida, leia uma mensagem para transmitir. Envie-a por todos os notificadores e imprima cada confirmação em sua própria linha.
As entradas a seguir serão fornecidas:
- Linha 1: Número de notificadores
- Para cada notificador: tipo (
emailousms), depois o destino - Linha final: Mensagem a ser transmitida
Por exemplo, dadas as entradas:
3
email
alice@example.com
sms
555-1234
email
bob@example.com
Server maintenance at midnightSua saída deverá 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 dadas as entradas:
2
sms
555-9999
sms
555-0000
Meeting canceledSua saída deverá ser:
SMS to 555-9999: Meeting canceled
SMS to 555-0000: Meeting canceledEssa estrutura demonstra como as interfaces permitem designs flexíveis e extensíveis. O dispatcher não conhece os tipos específicos de notificadores. Ele apenas sabe que eles podem usar Send. Essa é a base sobre a qual padrões como Observer e Strategy são construídos!
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Ler o número de notificadores
countStr, _ := reader.ReadString('\n')
count, _ := strconv.Atoi(strings.TrimSpace(countStr))
// Criar um novo dispatcher
dispatcher := NewNotificationDispatcher()
// Ler cada notificador e adicionar ao dispatcher
for i := 0; i < count; i++ {
notifierType, _ := reader.ReadString('\n')
notifierType = strings.TrimSpace(notifierType)
destination, _ := reader.ReadString('\n')
destination = strings.TrimSpace(destination)
// TODO: Criar o notificador apropriado com base no tipo
// e adicioná-lo ao dispatcher
// Dica: Verifique se notifierType é "email" ou "sms"
}
// Ler a mensagem para transmitir
message, _ := reader.ReadString('\n')
message = strings.TrimSpace(message)
// TODO: Transmitir a mensagem e imprimir 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 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 Arquivos3Ponteiros 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