Padrão Abstract Factory
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 88 de 107.
O padrão Abstract Factory estende o padrão Factory ao criar famílias de objetos relacionados sem especificar seus tipos concretos. Em vez de uma única função de fábrica, você define uma interface de fábrica que múltiplas fábricas concretas implementam, cada uma produzindo um conjunto consistente de objetos relacionados.
Considere um toolkit de UI que precisa criar botões e checkboxes. Diferentes sistemas operacionais exigem implementações diferentes, mas os componentes devem funcionar juntos de forma consistente:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}
// Interface da factory abstrata
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}Cada fábrica concreta produz uma família de componentes relacionados:
// Família Windows
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// Família Mac
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }O código cliente trabalha com a interface da fábrica, permanecendo independente de implementações concretas:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}Use o Abstract Factory quando seu sistema precisar trabalhar com múltiplas famílias de produtos relacionados e você quiser garantir que os componentes da mesma família sejam usados juntos. Ele é mais complexo do que um Factory simples, portanto, utilize-o apenas quando você realmente precisar gerenciar famílias de produtos.
Desafio
FácilVamos construir um sistema de loja de móveis usando o padrão Abstract Factory! Você criará fábricas que produzem famílias de itens de mobiliário relacionados — cada fábrica produz um estilo consistente (Moderno ou Vintage), garantindo que cadeiras e mesas da mesma fábrica combinem esteticamente.
Você organizará seu código em três arquivos:
furniture.go: Defina suas interfaces de produtos e tipos de móveis concretos.Crie duas interfaces de produto:
Chaircom um métodoSitOn() stringTablecom um métodoPlaceItems() string
Implemente duas famílias de móveis:
- Família Modern:
ModernChairretorna"Sitting on a sleek modern chair"eModernTableretorna"Placing items on a glass modern table" - Família Vintage:
VintageChairretorna"Sitting on an ornate vintage chair"eVintageTableretorna"Placing items on a wooden vintage table"
factory.go: Defina sua interface de fábrica abstrata e fábricas concretas.Crie uma interface
FurnitureFactorycom dois métodos:CreateChair() ChairCreateTable() Table
Implemente duas fábricas concretas:
ModernFactoryque produzModernChaireModernTableVintageFactoryque produzVintageChaireVintageTable
Também crie uma função
GetFactory(style string) FurnitureFactoryque retorna a fábrica apropriada com base no estilo ("modern"ou"vintage"). UseModernFactorycomo padrão para estilos não reconhecidos.main.go: Use suas fábricas para mobiliar cômodos.Crie uma função
FurnishRoom(factory FurnitureFactory) stringque usa a fábrica para criar uma cadeira e uma mesa, e então retorna uma string formatada mostrando ambas as ações em linhas separadas.Leia uma entrada de estilo, obtenha a fábrica apropriada, mobilie um cômodo e imprima o resultado.
As seguintes entradas serão fornecidas:
- Linha 1: Estilo de móveis (
"modern","vintage", ou algo diferente)
Por exemplo, dado:
modernSua saída deve ser:
Sitting on a sleek modern chair
Placing items on a glass modern tableE dado:
vintageSua saída deve ser:
Sitting on an ornate vintage chair
Placing items on a wooden vintage tableE dado:
rusticSua saída deve ser:
Sitting on a sleek modern chair
Placing items on a glass modern tableObserve como a função FurnishRoom funciona com qualquer fábrica através da interface — ela não sabe se está criando móveis modernos ou vintage. Isso garante que todos os móveis em um cômodo venham da mesma família, mantendo um estilo consistente!
Folha de consulta
O padrão Abstract Factory cria famílias de objetos relacionados sem especificar seus tipos concretos. Ele define uma interface de fábrica que múltiplas fábricas concretas implementam, cada uma produzindo um conjunto consistente de objetos relacionados.
Estrutura
Defina interfaces de produto para cada tipo de objeto:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}Crie uma interface de fábrica abstrata que declara métodos para criar cada tipo de produto:
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}Implementações Concretas
Implemente famílias de produtos concretos onde cada família possui estilo ou comportamento consistente:
// Windows family
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// Mac family
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }Código do Cliente
O código do cliente trabalha com a interface da fábrica, permanecendo independente de implementações concretas:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}Quando Usar
Use o Abstract Factory quando seu sistema precisar trabalhar com múltiplas famílias de produtos relacionados e você quiser garantir que os componentes da mesma família sejam usados juntos. Ele é mais complexo do que um Factory simples, portanto, use-o apenas quando você realmente precisar gerenciar famílias de produtos.
Experimente você mesmo
package main
import "fmt"
// FurnishRoom usa uma factory para criar móveis e retorna o resultado
// TODO: Implementar esta função
// - Use a factory para criar uma cadeira e uma mesa
// - Retorne uma string com o resultado de SitOn() na primeira linha e o resultado de PlaceItems() na segunda linha
func FurnishRoom(factory FurnitureFactory) string {
// TODO: Criar cadeira e mesa usando a factory
// TODO: Retornar uma string formatada com ambas as ações em linhas separadas
return ""
}
func main() {
var style string
fmt.Scanln(&style)
// TODO: Obter a factory apropriada com base no style
// TODO: Mobiliar um quarto usando a factory
// TODO: Imprimir o resultado
}
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