SubEquipe_01
FOCO_01: GoFs Criacionais & IA Generativa
Participantes no Foco_01
| Nome do Membro | Lições Aprendidas | Uso da IA Generativa (SENSO CRÍTICO) |
|---|---|---|
| Andre Henrique de Souza Belarmino | Aprendi que o Abstract Factory serve para criar famílias de objetos relacionados por meio de uma única interface, sem que o cliente conheça as classes concretas. Diferente do Factory Method, que escolhe a classe de um único produto, ele garante que todas as peças criadas pertençam à mesma família. Entendi também o equilíbrio do padrão: acrescentar uma nova família é fácil e não afeta o cliente, mas acrescentar um novo tipo de produto obriga a alterar a fábrica abstrata e todas as fábricas concretas. Por fim, percebi que os produtos abstratos devem definir comportamento, para que cada família implemente esse comportamento do seu jeito. | Usei o Claude Sonnet 5 para gerar o primeiro diagrama. Comparando com os slides, encontrei erros: o cliente não usava os produtos abstratos, os produtos não tinham comportamento e faltava a classe ConfiguracaoComunidade. Usei o diagrama apenas como rascunho para montar o meu. Na implementação, usei o Claude Code com o MCP do Figma para trazer as imagens do protótipo e revisar o código. A IA ajuda a começar, mas erra com confiança, então tudo precisa ser conferido. |
| Giovanna Felipe Guimaraes | Aprendi que o Singleton resolve um problema bem concreto do portal: várias telas dependem dos mesmos dados, e ter um único repositório evita cópias inconsistentes. Na implementação, percebi que o JavaScript não tem construtor privado, então precisei criar um mecanismo próprio (o atributo estático #liberado) para impedir o new fora do getInstancia(). Também ficou mais clara para mim a diferença entre composição e agregação, e como dois padrões criacionais podem trabalhar juntos: o Builder monta a notícia e o Singleton guarda. Por outro lado, entendi que o Singleton funciona como um estado global e deve ser usado com cuidado, só quando realmente precisa existir uma única instância. |
Usei o Gemini para gerar uma primeira versão do diagrama de classes. Ele acertou a estrutura básica do Singleton, mas analisando com calma encontrei erros: usou composição entre o repositório e a notícia (sendo que quem cria a notícia é o Builder), não previu nenhum método de publicação, usou associação onde deveria ser dependência e misturou nomes como getInstance e obterInstancia. Por isso não usei o diagrama dele como resposta final, e sim como ponto de partida para montar o meu. A IA ajuda a começar rápido, mas o resultado precisa ser conferido com o conteúdo das aulas e com o contexto real do projeto. |
| Joao Pedro Lopes da Cruz | Aprendi que o builder é a escolha ideal quando não se sabe como será a estrutura final do objeto, no caso do projeto da notícia, uma vez que os blocos de texto, imagens, mídia, etc podem aparecer em diferentes ordens. Assim, o builder se apresentou como a solução ideal por construir a página da notícia conforme solicitado pelo usuário, através da orquestração do builder | O uso de IA é descrito em detalhes no texto, mas no geral a IA foi usada para melhorar o texto escrito (sempre passando por revisão final humana) e geração de diagramas para o ponto de partida (no decorrer da página coloquei minhas considerações quanto a qualidade dos diagramas gerados). Além disso, a IA auxiliou na implementação do código através do MCP com o figma, que reaproveita o protótipo navegável transformando-o automaticamente em código HTML e CSS. Foi usado o Gemini pro 3.1 (diagrama), claude Sonnet 5.5 (diagrama) e o claude opus 5.5 (aproveitamento do protótipo navegável) |
Metodologia do Foco_01
A SubEquipe 01 ficou responsável pelos GoFs criacionais. Para que cada membro tivesse uma contribuição própria, cada um escolheu um padrão diferente e o aplicou a uma parte real do Burburinho: João Pedro usou o Builder na montagem da página de notícia, Giovanna usou o Singleton no repositório de notícias e André usou o Abstract Factory na criação de comunidades. Os padrões também se complementam, já que o Builder monta a notícia que o Singleton armazena.
Para manter o relatório uniforme, todos seguiram o mesmo fluxo:
- Descrever o contexto, mostrando o problema do portal que o padrão resolve.
- Gerar um diagrama inicial com IA generativa (Gemini e Claude) e analisá-lo com senso crítico, usando como base os slides da aula de GoFs Criacionais, que têm como referência o livro de Gamma, Helm, Johnson e Vlissides.
- Elaborar o próprio diagrama, corrigindo os problemas encontrados no diagrama da IA.
- Implementar o padrão em JavaScript, rodando no navegador e seguindo o protótipo do Figma, com um vídeo do padrão em execução.
O uso de IA é descrito em cada seção individual, mostrando o diagrama gerado, os erros encontrados e as decisões tomadas por cada membro. Assim, fica possível distinguir o que foi sugerido pela IA do que foi definido pela equipe.
No repositório, João Pedro abriu a branch subgrupo-1 e montou a estrutura deste relatório. Cada membro trabalhou em uma branch própria e integrou sua parte por pull request na branch do subgrupo, onde ela era revisada antes de ser incorporada. Cada contribuição foi registrada na tabela de Versionamentos.
GoF Criacional
Contexto (Problema)
1. Builder de notícias - João Pedro Lopes da Cruz
Pensando na funcionalidade de criar uma notícia pensei que poderia haver templatezinho de estrutura de como ficaria a página da notícia, alguns com a imagem em destaque grande, outro com apenas uma imagem pequena ao lado do texto e uma notícia que comporte um carrossel de imagens. Pensando nessas estruturas pré definidas optei por implementar a geração da página usando um builder, pois todas as notícias seguirão a mesma estrutura base de informações (mídias, texto, título, categoria, autor, etc).
2. Singleton do Repositório de Notícias - Giovanna Felipe Guimaraes
No portal, várias telas dependem das mesmas notícias publicadas: a página inicial mostra as mais recentes, a página de categoria filtra por assunto e a área de destaques puxa as principais. Se cada uma dessas telas criasse o seu próprio repositório de notícias, cada uma teria a sua própria cópia dos dados. Assim, uma notícia publicada poderia aparecer na home e não aparecer na categoria, ou aparecer duplicada, além de abrir várias "conexões" para buscar a mesma informação sem necessidade. Por isso escolhi o Singleton para o repositório de notícias: ele garante que exista uma única instância guardando as notícias publicadas e oferece um ponto de acesso global a ela. Com isso, toda tela que chama o repositório recebe o mesmo objeto, e o que é publicado num lugar aparece igual em todos os outros. Ele também se conecta ao Builder do João, porque a notícia que o Builder monta é publicada nesse repositório único.
3. Abstract Factory de Comunidades - André Henrique de Souza Belarmino
Na Entrega 2, o diagrama de classes que modelei tratava Comunidade como uma especialização de Tag, com os atributos corTema e regrasModeracao e o método atualizarDados(). Só que, olhando de novo para esse modelo com a lente de Padrões de Projeto, percebi um problema: criar uma nova comunidade não deveria significar simplesmente preencher esses dois campos de texto livre. Comunidades de categorias diferentes (ex: uma comunidade técnica, no estilo dev.to, e uma comunidade de eventos) precisam nascer com um conjunto coerente de configurações, um tema visual que combine com o propósito da comunidade, uma política de moderação com o rigor adequado ao tipo de conteúdo, e um widget de destaque relevante para aquele público. Se isso for feito com um amontoado de if/else checando a categoria toda vez que uma comunidade for criada, é fácil alguém acabar misturando peças incompatíveis (ex: tema de comunidade técnica com política de moderação pensada para eventos).
Esse é exatamente o problema que o Abstract Factory resolve: ele define uma interface para criar famílias de objetos relacionados sem o cliente precisar conhecer as classes concretas, garantindo que os produtos de uma mesma família sejam sempre usados em conjunto. Por isso escolhi aplicá-lo à criação de comunidades, retomando e aprofundando o meu próprio recorte da Entrega 2.
Modelagem (Notação UML)
1. Builder de notícias
Desse modo, pedi para a inteligência artificial Gemini 3.1 pro gerar como ele imagina o diagrama do builder nesse contexto
Diagrama GoF criacional: Gemini - prompt por João Pedro Lopes da Cruz
Usei a inteligência artificial para me dar um norte na diagramação, contudo não gostei muito da contrução dele, uma vez que ele não usou corretamente o relacionamento entre o diretor e a interface builder. Além disso, a falta de variedade de builders concretos mostrou uma falta de proveito do polimorfismo providenciado pela interface, além de enrijecer a estrutura
classDiagram
class DiretorDeNoticia {
- builder: NoticiaBuilder
+ setBuilder(builder: NoticiaBuilder)
+ construirNoticiaUrgente()
+ construirNoticiaComMidia()
}
class NoticiaBuilder {
<<interface>>
+ adicionarTitulo(titulo: String)
+ adicionarTexto(texto: String)
+ inserirTags(tags: List~String~)
+ anexarMidia(midia: String)
+ build() Noticia
}
class PortalNoticiaBuilder {
- noticia: Noticia
+ PortalNoticiaBuilder()
+ adicionarTitulo(titulo: String)
+ adicionarTexto(texto: String)
+ inserirTags(tags: List~String~)
+ anexarMidia(midia: String)
+ build() Noticia
}
class Noticia {
- titulo: String
- texto: String
- tags: List~String~
- midia: String
+ exibir()
}
DiretorDeNoticia --> NoticiaBuilder : usa
NoticiaBuilder <|.. PortalNoticiaBuilder : implementa
PortalNoticiaBuilder ..> Noticia : cria
Figura: Diagrama de pacotes gerado pela IA (Fonte: Google Gemini 3.1 Pro, 2026)
Após a modelagem feita pelo Gemini, decidi enviar para o Claude Sonnet 5.5 o diagrama do Gemini e pedir para ele fazer uma estrutura mais alinhada com o exemplo dos slides, ele acabou gerando um diagrama com um embasamento melhor
Diagrama GoF criacional: Claude - prompt por João Pedro Lopes da Cruz
O diagrama gerado pelo Claude separa o cliente do diretor, cria dois builders concretos (texto e multimídia), aproveitando o polimorfismo da interface, e modela a mídia como uma classe própria associada à notícia.
classDiagram
class Client
class DiretorDeNoticia {
- builder: NoticiaBuilder
+ setBuilder(builder: NoticiaBuilder)
+ construirNoticiaUrgente()
+ construirNoticiaComMidia()
}
class NoticiaBuilder {
<<interface>>
+ reset()
+ adicionarTitulo(titulo: String)
+ adicionarTexto(texto: String)
+ inserirTags(tags: List~String~)
+ anexarMidia(midia: Midia)
+ build() Noticia
}
class NoticiaTextoBuilder {
- noticia: Noticia
+ reset()
+ adicionarTitulo(titulo: String)
+ adicionarTexto(texto: String)
+ inserirTags(tags: List~String~)
+ anexarMidia(midia: Midia)
+ build() Noticia
}
class NoticiaMultimidiaBuilder {
- noticia: Noticia
+ reset()
+ adicionarTitulo(titulo: String)
+ adicionarTexto(texto: String)
+ inserirTags(tags: List~String~)
+ anexarMidia(midia: Midia)
+ build() Noticia
}
class Noticia {
- titulo: String
- texto: String
- tags: List~String~
- midias: List~Midia~
+ exibir()
}
class Midia {
- tipo: String
- url: String
}
Client ..> DiretorDeNoticia : usa
DiretorDeNoticia o-- NoticiaBuilder
NoticiaBuilder <|.. NoticiaTextoBuilder
NoticiaBuilder <|.. NoticiaMultimidiaBuilder
NoticiaTextoBuilder ..> Noticia : cria
NoticiaMultimidiaBuilder ..> Noticia : cria
Noticia o-- Midia
Figura: Diagrama de classes gerado pela IA (Fonte: Claude Sonnet 5.5, 2026)
Os dois diagramas gerados por inteligência artificial não conseguiram captar corretamente a minha intenção no uso do builder. A minha ideia central era que os diferentes blocos de uma notícia aparecem em ordens diferentes escolhidas pelo jornalista, isto é, os blocos de texto, imagens, carrosséis e vídeos devem ser posicionados conforme a intenção do redator, por isso um builder usando director seria a melhor jogada, o director seria o responsável por chamar na ordem definida pelo redator os métodos que montariam a página da notícia. Desse modo, montei meu próprio diagrama para representar essa estrutura
Diagrama GoF criacional Builder: João Pedro Lopes da Cruz
classDiagram
direction TB
class Cliente {
}
class NoticiaDirector {
-builder: NoticiaBuilder
+NoticiaDirector()
+setBuilder() void
+construirPersonalizada() void
+construirNoticiaPadrao() void
-validar() void
}
class NoticiaBuilder {
+reset() void
+definirCabecalho() void
+definirSubtitulo() void
+definirAutor() void
+adicionarTexto() void
+adicionarImagem() void
+adicionarCarrossel() void
+adicionarTopicos() void
+definirRodape() void
+getResultado() Noticia
}
class NoticiaDestaqueBuilder {
-noticia: Noticia
+definirCabecalho() void
+definirSubtitulo() void
+definirAutor() void
+adicionarCarrossel() void
+getResultado() Noticia
}
class NoticiaCompactaBuilder {
-noticia: Noticia
+definirCabecalho() void
+definirSubtitulo() void
+definirAutor() void
+adicionarCarrossel() void
+getResultado() Noticia
}
class NoticiaMistaBuilder {
-noticia: Noticia
-posicaoTitulo: PosicaoTitulo
+NoticiaMistaBuilder()
+definirCabecalho() void
+definirSubtitulo() void
+definirAutor() void
+adicionarImagem() void
+adicionarCarrossel() void
+getResultado() Noticia
}
class Noticia {
-titulo: String
-subtitulo: String
-categoria: String
-capa: ImagemBloco
-posicaoTitulo: PosicaoTitulo
-autor: Autor
-dataCriacao: Date
-blocos: List~Bloco~
-rodape: String
+Noticia()
+adicionar() void
+getDataCriacao() Date
+renderizar() String
}
class Bloco {
+renderizar() String
}
class TextoBloco {
}
class ImagemBloco {
-url: String
-largura: int
}
class CarrosselBloco {
}
class TopicosBloco {
}
<<interface>> NoticiaBuilder
<<abstract>> Bloco
note for Noticia "dataCriacao é gerada automaticamente
no construtor (new Date()): nem o
Cliente nem o Builder informam a data"
note for NoticiaMistaBuilder "título e subtítulo ficam sobre a capa,
na posição recebida no construtor"
note for Noticia "As informações do autor serão obtidas pelo servidor
(id do user no banco por exemplo)"
Cliente --> NoticiaDirector : usa
Cliente ..> NoticiaDestaqueBuilder : escolhe um
Cliente ..> NoticiaCompactaBuilder : escolhe um
Cliente ..> NoticiaMistaBuilder : escolhe um e a posição
NoticiaDirector o--> NoticiaBuilder : builder
NoticiaBuilder <|.. NoticiaDestaqueBuilder
NoticiaBuilder <|.. NoticiaCompactaBuilder
NoticiaBuilder <|.. NoticiaMistaBuilder
NoticiaDestaqueBuilder --> Noticia : constrói
NoticiaCompactaBuilder --> Noticia : constrói
NoticiaMistaBuilder --> Noticia : constrói
Noticia *-- Bloco
Bloco <|-- TextoBloco
Bloco <|-- ImagemBloco
Bloco <|-- CarrosselBloco
Bloco <|-- TopicosBloco
Figura: Diagrama de classes do Builder de notícias com Director (Fonte: João Cruz, 2026) · Ver em tela cheia
Diagrama feito manualmente para melhor representar minha inteção com o uso do Builder, a IA foi usada para revisar e ela sugeriu que eu adicionasse a composição de Notícia com Bloco, dizendo que uma notícia é formada por vários blocos, achei uma boa acrescentar, uma vez que cada bloco representará uma parte da página de notícia
2. Singleton do Repositório de Notícias
Para começar a modelagem, pedi ao Gemini um diagrama de classes do Singleton aplicado ao repositório de notícias, acessado pela página inicial, pela página de categoria e pelos destaques.
Diagrama GoF criacional: Gemini - prompt por Giovanna Felipe Guimaraes
A IA acertou o básico do Singleton (construtor privado, atributo estático com a instância única e um método de acesso global), mas encontrei alguns problemas. Ela ligou o repositório à Noticia por composição, como se o repositório fosse dono do ciclo de vida das notícias, sendo que a notícia é criada antes, pelo Builder, e só depois é guardada. O repositório também só tem métodos de leitura, então não existe como uma notícia entrar nele. Além disso, ela usou setas de associação das páginas para o repositório, quando na verdade as páginas só chamam o método estático (dependência), e misturou getInstance nos rótulos com obterInstancia na classe.
classDiagram
direction LR
class NoticiaRepository {
- static instanciaUnica: NoticiaRepository
- List~Noticia~ noticias
- NoticiaRepository()
+ static obterInstancia(): NoticiaRepository
+ obterDestaques(): List~Noticia~
+ obterPorCategoria(categoria: String): List~Noticia~
+ obterUltimasNoticias(): List~Noticia~
}
class PaginaInicial {
+ renderizar()
}
class PaginaCategoria {
+ renderizar(categoria: String)
}
class SecaoDestaques {
+ renderizar()
}
class Noticia {
- String id
- String titulo
- String resumo
- String conteudo
- String categoria
- Date dataPublicacao
}
PaginaInicial --> NoticiaRepository : Acessa (getInstance)
PaginaCategoria --> NoticiaRepository : Acessa (getInstance)
SecaoDestaques --> NoticiaRepository : Acessa (getInstance)
NoticiaRepository "1" *-- "*" Noticia : gerencia
Figura: Diagrama de classes gerado pela IA (Fonte: Google Gemini, 2026)
A partir disso, montei o meu diagrama corrigindo esses pontos. Troquei a composição por agregação, acrescentei o método publicar() para que a notícia montada pelo Builder do João entre no repositório, mudei as setas das telas para dependência e padronizei os nomes. Também usei os atributos da Noticia que já estavam no diagrama do Builder, para os dois padrões falarem da mesma classe. Quem liga os dois padrões é a Redacao: ela pede ao NoticiaDirector para montar a notícia e depois publica o resultado no repositório.
Diagrama GoF criacional Singleton: Giovanna Felipe Guimaraes
classDiagram
direction LR
class RepositorioNoticias {
-RepositorioNoticias instancia$
-boolean liberado$
-noticias: List~Noticia~
-RepositorioNoticias()
+getInstancia()$ RepositorioNoticias
+publicar(noticia: Noticia) boolean
+listarRecentes(quantidade: int) List~Noticia~
+listarPorCategoria(categoria: String) List~Noticia~
+listarDestaques() List~Noticia~
}
class Noticia {
-titulo: String
-subtitulo: String
-categoria: String
-variante: String
-autor: Autor
-dataCriacao: Date
}
class Redacao {
+publicar(exemplo, versao: String) Noticia
}
class NoticiaDirector {
+construirPersonalizada() void
}
class Tela {
+repositorio() RepositorioNoticias
+noticias() List~Noticia~
}
class PaginaInicial {
+noticias() List~Noticia~
}
class PaginaCategoria {
-categoria: String
+noticias() List~Noticia~
}
class SecaoDestaques {
+noticias() List~Noticia~
}
<<Singleton>> RepositorioNoticias
<<abstract>> Tela
Redacao ..> NoticiaDirector : monta a notícia
Redacao ..> RepositorioNoticias : publicar()
Tela ..> RepositorioNoticias : getInstancia()
Tela <|-- PaginaInicial
Tela <|-- PaginaCategoria
Tela <|-- SecaoDestaques
RepositorioNoticias "1" o-- "*" Noticia : armazena
Figura: Diagrama de classes do Singleton do repositório de notícias (Fonte: Giovanna Guimaraes, 2026)
O construtor de RepositorioNoticias é privado, então a única forma de obter o repositório é pelo getInstancia(), que cria a instância só na primeira chamada e devolve sempre a mesma nas seguintes.
3. Abstract Factory de Comunidades
Para ter um ponto de partida, pedi ao Claude Sonnet 5 um diagrama inicial, descrevendo o cenário (famílias de tema visual, política de moderação e widget padrão por categoria de comunidade) e pedindo que a estrutura seguisse à risca os papéis clássicos do Abstract Factory (AbstractFactory, ConcreteFactory, AbstractProduct, ConcreteProduct, Client).
Diagrama GoF criacional: Claude Sonnet 5 - prompt por André Henrique de Souza Belarmino
O diagrama modela duas famílias concretas (Tecnologia e Eventos) para manter o exemplo enxuto, mas a estrutura permite acrescentar novas famílias (ex: Bem-estar) sem alterar o cliente CriadorDeComunidade — basta criar uma nova ConcreteFactory e seus três ConcreteProducts correspondentes. Os três produtos abstratos (TemaVisual, PoliticaModeracao, WidgetPadrao) foram modelados como interfaces, e não como atributos soltos em Comunidade, justamente para impedir que o código cliente misture peças de famílias diferentes. O CriadorDeComunidade é o cliente do padrão: ele recebe uma fábrica concreta (injetada conforme a categoria escolhida ao criar a comunidade) e usa exclusivamente a interface ComunidadeFactory, sem conhecer ComunidadeTecnologiaFactory ou ComunidadeEventosFactory diretamente. O resultado desse processo (ConfiguracaoComunidade) é o que acaba populando os atributos corTema e regrasModeracao já existentes na classe Comunidade da Entrega 2.
classDiagram
class ComunidadeFactory {
<<interface>>
+criarTema() TemaVisual
+criarPoliticaModeracao() PoliticaModeracao
+criarWidgetPadrao() WidgetPadrao
}
class ComunidadeTecnologiaFactory {
+criarTema() TemaVisual
+criarPoliticaModeracao() PoliticaModeracao
+criarWidgetPadrao() WidgetPadrao
}
class ComunidadeEventosFactory {
+criarTema() TemaVisual
+criarPoliticaModeracao() PoliticaModeracao
+criarWidgetPadrao() WidgetPadrao
}
class TemaVisual {
<<interface>>
-corPrimaria: String
-corSecundaria: String
-icone: String
}
class TemaTecnologia
class TemaEventos
class PoliticaModeracao {
<<interface>>
-nivelRigor: String
-exigeAprovacaoPrevia: boolean
}
class PoliticaModeracaoTecnica
class PoliticaModeracaoEventos
class WidgetPadrao {
<<interface>>
-nome: String
-posicao: String
}
class WidgetTrendingTags
class WidgetProximosEventos
class CriadorDeComunidade {
-factory: ComunidadeFactory
+CriadorDeComunidade(factory: ComunidadeFactory)
+montarConfiguracao() ConfiguracaoComunidade
}
class Comunidade {
-corTema: String
-regrasModeracao: String
+atualizarDados(descricao: String, regras: String) void
}
ComunidadeFactory <|.. ComunidadeTecnologiaFactory
ComunidadeFactory <|.. ComunidadeEventosFactory
TemaVisual <|.. TemaTecnologia
TemaVisual <|.. TemaEventos
PoliticaModeracao <|.. PoliticaModeracaoTecnica
PoliticaModeracao <|.. PoliticaModeracaoEventos
WidgetPadrao <|.. WidgetTrendingTags
WidgetPadrao <|.. WidgetProximosEventos
ComunidadeTecnologiaFactory ..> TemaTecnologia : cria
ComunidadeTecnologiaFactory ..> PoliticaModeracaoTecnica : cria
ComunidadeTecnologiaFactory ..> WidgetTrendingTags : cria
ComunidadeEventosFactory ..> TemaEventos : cria
ComunidadeEventosFactory ..> PoliticaModeracaoEventos : cria
ComunidadeEventosFactory ..> WidgetProximosEventos : cria
CriadorDeComunidade o-- ComunidadeFactory
CriadorDeComunidade ..> Comunidade : configura
Figura: Diagrama de classes do Abstract Factory aplicado a famílias de comunidade (Fonte: Claude Sonnet 5, prompt de André Henrique de Souza Belarmino, 2026)
Comparando o diagrama da IA com os slides da aula de GoFs Criacionais, encontrei pontos que precisavam ser corrigidos:
- O cliente não conhecia os produtos abstratos. Pela modelagem genérica do padrão, o
Clientusa as interfaces declaradas tanto emAbstractFactoryquanto emAbstractProduct. No diagrama da IA,CriadorDeComunidadesó se relacionava comComunidadeFactory, faltando as associações comTemaVisual,PoliticaModeracaoeWidgetPadrao. - Os produtos abstratos eram interfaces com atributos e sem comportamento. Uma interface UML não deveria ter atributos privados, e nos exemplos da aula os produtos têm comportamento (
paint(),calculateTaxes()). TransformeiComunidadeFactorye os três produtos em classes abstratas (como a aula indica: "o normal é ser Classe Abstrata") e dei a cada produto um método:aplicar(),moderar()erenderizar(). - Não ficava claro onde a fábrica concreta era escolhida. A IA apenas citou que a fábrica seria "injetada conforme a categoria". Seguindo o
defineFactory()do exemploGUIFactoryda aula, acrescentei o método estáticodefinirFactory(categoria), que é chamado a partir da área de criação de comunidade: o usuário escolhe a categoria e o método devolve a fábrica concreta correspondente. - O texto da IA citava uma classe que não aparece no diagrama. O
ConfiguracaoComunidaderetornado pormontarConfiguracao()não estava no modelo. Acrescentei essa classe, ligando-a àComunidadeda Entrega 2, cujos atributoscorTemaeregrasModeracaoela preenche.
Também considerei permitir uma comunidade "personalizada", em que o usuário escolhesse cada peça livremente. Desisti porque isso vai contra o objetivo do padrão, que é criar famílias de objetos relacionados: se cada peça pode vir de uma família diferente, a fábrica deixa de garantir a coerência. Em vez disso, mostrei a principal vantagem do padrão acrescentando duas famílias fixas, Cultura e Política, sem precisar alterar o cliente CriadorDeComunidade.
Diagrama GoF criacional Abstract Factory: André Henrique de Souza Belarmino
classDiagram
direction TB
class CriadorDeComunidade {
-factory: ComunidadeFactory
-passos: List~String~
+CriadorDeComunidade(factory: ComunidadeFactory)
+montarConfiguracao(nome: String, descricao: String) ConfiguracaoComunidade
-garantirMesmaFamilia(produtos: List) void
}
class ComunidadeFactory {
+definirFactory(categoria: String)$ ComunidadeFactory
+criarTema() TemaVisual
+criarPoliticaModeracao() PoliticaModeracao
+criarWidgetPadrao() WidgetPadrao
}
class ComunidadeTecnologiaFactory {
+criarTema() TemaVisual
+criarPoliticaModeracao() PoliticaModeracao
+criarWidgetPadrao() WidgetPadrao
}
class ComunidadeEsportesFactory {
+criarTema() TemaVisual
+criarPoliticaModeracao() PoliticaModeracao
+criarWidgetPadrao() WidgetPadrao
}
class ComunidadeCulturaFactory {
+criarTema() TemaVisual
+criarPoliticaModeracao() PoliticaModeracao
+criarWidgetPadrao() WidgetPadrao
}
class ComunidadePoliticaFactory {
+criarTema() TemaVisual
+criarPoliticaModeracao() PoliticaModeracao
+criarWidgetPadrao() WidgetPadrao
}
class TemaVisual {
#familia: String
#corPrimaria: String
#corSecundaria: String
#icone: String
#banner: String
+aplicar(pagina: HTMLElement) void
}
class TemaTecnologia
class TemaEsportes
class TemaCultura
class TemaPolitica
class PoliticaModeracao {
#familia: String
#nivelRigor: String
#exigeAprovacaoPrevia: boolean
#regra: String
+moderar(conteudo: String) boolean
}
class PoliticaModeracaoTecnica
class PoliticaModeracaoEsportes
class PoliticaModeracaoCultura
class PoliticaModeracaoPolitica
class WidgetPadrao {
#familia: String
#titulo: String
#posicao: String
#itens: List~String~
+renderizar() String
}
class WidgetTrendingTags
class WidgetPlacarAoVivo
class WidgetAgendaCultura
class WidgetPautaPolitica
class ConfiguracaoComunidade {
-nome: String
-descricao: String
-tema: TemaVisual
-moderacao: PoliticaModeracao
-widget: WidgetPadrao
}
class Comunidade {
-corTema: String
-regrasModeracao: String
+atualizarDados(descricao: String, regras: String) void
}
<<abstract>> ComunidadeFactory
<<abstract>> TemaVisual
<<abstract>> PoliticaModeracao
<<abstract>> WidgetPadrao
note for ComunidadeFactory "definirFactory() recebe a categoria escolhida
na área de criação de comunidade e devolve
a fábrica concreta correspondente"
CriadorDeComunidade o--> ComunidadeFactory : factory
CriadorDeComunidade --> TemaVisual : usa
CriadorDeComunidade --> PoliticaModeracao : usa
CriadorDeComunidade --> WidgetPadrao : usa
CriadorDeComunidade ..> ConfiguracaoComunidade : cria
ConfiguracaoComunidade ..> Comunidade : preenche corTema e regrasModeracao
ComunidadeFactory <|-- ComunidadeTecnologiaFactory
ComunidadeFactory <|-- ComunidadeEsportesFactory
ComunidadeFactory <|-- ComunidadeCulturaFactory
ComunidadeFactory <|-- ComunidadePoliticaFactory
TemaVisual <|-- TemaTecnologia
TemaVisual <|-- TemaEsportes
TemaVisual <|-- TemaCultura
TemaVisual <|-- TemaPolitica
PoliticaModeracao <|-- PoliticaModeracaoTecnica
PoliticaModeracao <|-- PoliticaModeracaoEsportes
PoliticaModeracao <|-- PoliticaModeracaoCultura
PoliticaModeracao <|-- PoliticaModeracaoPolitica
WidgetPadrao <|-- WidgetTrendingTags
WidgetPadrao <|-- WidgetPlacarAoVivo
WidgetPadrao <|-- WidgetAgendaCultura
WidgetPadrao <|-- WidgetPautaPolitica
ComunidadeTecnologiaFactory ..> TemaTecnologia : cria
ComunidadeTecnologiaFactory ..> PoliticaModeracaoTecnica : cria
ComunidadeTecnologiaFactory ..> WidgetTrendingTags : cria
ComunidadeEsportesFactory ..> TemaEsportes : cria
ComunidadeEsportesFactory ..> PoliticaModeracaoEsportes : cria
ComunidadeEsportesFactory ..> WidgetPlacarAoVivo : cria
ComunidadeCulturaFactory ..> TemaCultura : cria
ComunidadeCulturaFactory ..> PoliticaModeracaoCultura : cria
ComunidadeCulturaFactory ..> WidgetAgendaCultura : cria
ComunidadePoliticaFactory ..> TemaPolitica : cria
ComunidadePoliticaFactory ..> PoliticaModeracaoPolitica : cria
ComunidadePoliticaFactory ..> WidgetPautaPolitica : cria
Figura: Diagrama de classes do Abstract Factory de comunidades, corrigido a partir do diagrama da IA (Fonte: André Henrique de Souza Belarmino, 2026) · Ver em tela cheia
Implementação (Código & Vídeo com Padrão em Execução)
Explicar cada trecho de código, inerente aos participantes do padrão. Conferir um link para o código hospedado no repositorio do projeto. Acordar um vídeo, demonstrando o código em execução.
1. Builder de notícias
O código está em demonstracao_codigos/SubGrupo1/builder. A implementação foi feita em JavaScript puro, sem bibliotecas, e roda direto no navegador de duas formas:
- Página de notícia (
index.html): reproduz a página de notícia do protótipo no Figma. No painel do canto inferior, é possível escolher uma notícia de exemplo e trocar o builder concreto (Destaque, Compacta ou Mista). A mesma notícia é remontada na hora com outro visual, e a aba "Passos chamados pelo Director" mostra cada método do builder chamado, na ordem. - Montar notícia (
montar-noticia.html): simula a tela em que o redator cria a notícia. Ele preenche título, subtítulo, categoria, capa e autor, adiciona os blocos (texto, imagem, carrossel e tópicos) na ordem que quiser, revisa e escolhe o builder que vai montar a página final.
Cada participante do padrão ficou em um arquivo separado dentro de js/, para ficar fácil relacionar o código com o diagrama:
| Participante do padrão | Classe(s) | Arquivo |
|---|---|---|
| Builder (interface) | NoticiaBuilder |
builders.js |
| Builders concretos | NoticiaDestaqueBuilder, NoticiaCompactaBuilder, NoticiaMistaBuilder |
builders.js |
| Director | NoticiaDirector (e o roteiro: ItemRoteiro, TipoBloco) |
director.js |
| Produto | Noticia, Autor |
noticia.js |
| Partes do produto | Bloco, TextoBloco, ImagemBloco, CarrosselBloco, TopicosBloco |
blocos.js |
| Cliente | página de exemplos e página de montar notícia | cliente.js, montar-noticia.js |
1. A interface do Builder (js/builders.js)
O JavaScript não tem interface como o Java, então criei a NoticiaBuilder como uma classe que não pode ser instanciada diretamente (o construtor verifica new.target) e cujos métodos lançam erro se não forem sobrescritos. Assim, ela funciona como um contrato: todo builder concreto precisa saber executar os mesmos passos de montagem da notícia.
class NoticiaBuilder {
constructor() {
if (new.target === NoticiaBuilder) {
throw new Error("NoticiaBuilder é uma interface: use NoticiaDestaqueBuilder, NoticiaCompactaBuilder ou NoticiaMistaBuilder.");
}
}
reset() { throw new Error("reset() não implementado"); }
definirCabecalho(titulo, categoria, capa) { throw new Error("definirCabecalho() não implementado"); }
definirSubtitulo(subtitulo) { throw new Error("definirSubtitulo() não implementado"); }
definirAutor(autor) { throw new Error("definirAutor() não implementado"); }
adicionarTexto(texto) { throw new Error("adicionarTexto() não implementado"); }
adicionarImagem(img) { throw new Error("adicionarImagem() não implementado"); }
adicionarCarrossel(imgs) { throw new Error("adicionarCarrossel() não implementado"); }
adicionarTopicos(itens) { throw new Error("adicionarTopicos() não implementado"); }
definirRodape(fonte) { throw new Error("definirRodape() não implementado"); }
getResultado() { throw new Error("getResultado() não implementado"); }
}
2. Os builders concretos: mesmos passos, visuais diferentes
Os três builders recebem exatamente as mesmas chamadas, mas cada um decide como configurar cada parte da notícia. O NoticiaDestaqueBuilder coloca a capa grande ocupando toda a largura, aplica a letra capitular no primeiro parágrafo e mostra as imagens em largura total. O NoticiaCompactaBuilder usa uma capa em miniatura e coloca as imagens menores ao lado do texto. Comparando o mesmo método nos dois, dá para ver a diferença:
// NoticiaDestaqueBuilder
adicionarTexto(texto) {
this.noticia.adicionar(new TextoBloco(texto, { capitular: this.primeiroTexto }));
this.primeiroTexto = false;
}
adicionarImagem(img) {
this.noticia.adicionar(new ImagemBloco(img.url, { legenda: img.legenda, largura: 100 }));
}
// NoticiaCompactaBuilder
adicionarTexto(texto) {
this.noticia.adicionar(new TextoBloco(texto));
}
adicionarImagem(img) {
this.noticia.adicionar(new ImagemBloco(img.url, { legenda: img.legenda, largura: 40, alinhamento: "direita" }));
}
O NoticiaMistaBuilder mistura os dois: a capa fica em destaque com o título e o subtítulo escritos sobre a imagem, o primeiro parágrafo tem capitular e as imagens do corpo ficam ao lado do texto. A posição do título sobre a capa é uma decisão de apresentação, e não de conteúdo, por isso ela é passada no construtor do builder e não passa pelo Director. O construtor também recusa posições inválidas.
const PosicaoTitulo = Object.freeze({
SUPERIOR_ESQUERDA: "superior-esquerda",
SUPERIOR_DIREITA: "superior-direita",
INFERIOR_ESQUERDA: "inferior-esquerda",
INFERIOR_DIREITA: "inferior-direita",
CENTRO: "centro",
});
class NoticiaMistaBuilder extends NoticiaBuilder {
constructor(posicaoTitulo = PosicaoTitulo.INFERIOR_ESQUERDA) {
super();
if (!Object.values(PosicaoTitulo).includes(posicaoTitulo)) {
throw new Error(`Posição do título inválida: ${posicaoTitulo}`);
}
this.posicaoTitulo = posicaoTitulo;
this.reset();
}
...
}
Em todos os builders, o getResultado() entrega a notícia pronta e já chama o reset(), deixando o builder limpo para montar a próxima notícia sem herdar blocos da anterior.
getResultado() {
const resultado = this.noticia;
this.reset();
return resultado;
}
3. O Director e o roteiro do redator (js/director.js)
Essa é a parte central do por quê o builder foi usado, os blocos de uma notícia aparecem na ordem que o redator escolheu, então o Director recebe essa ordem como um roteiro, uma lista de itens, em que cada item tem um tipo (TEXTO, IMAGEM, CARROSSEL ou TOPICOS) e o seu conteúdo. O construirPersonalizada() monta primeiro o cabeçalho e depois percorre o roteiro chamando o passo correspondente do builder. O Director só conhece a interface NoticiaBuilder, então ele não sabe (e não precisa saber) qual builder concreto está usando.
construirPersonalizada(titulo, subtitulo, categoria, capa, autor, roteiro, fonte) {
this.validar(roteiro);
this.builder.reset();
this.builder.definirCabecalho(titulo, categoria, capa);
if (subtitulo) {
this.builder.definirSubtitulo(subtitulo);
}
this.builder.definirAutor(autor);
for (const item of roteiro) {
switch (item.tipo) {
case TipoBloco.TEXTO:
this.builder.adicionarTexto(item.conteudo);
break;
case TipoBloco.IMAGEM:
this.builder.adicionarImagem(item.conteudo);
break;
case TipoBloco.CARROSSEL:
this.builder.adicionarCarrossel(item.conteudo);
break;
case TipoBloco.TOPICOS:
this.builder.adicionarTopicos(item.conteudo);
break;
}
}
if (fonte) {
this.builder.definirRodape(fonte);
}
}
Antes de chamar qualquer passo, o método privado validar() confere o roteiro: ele não pode estar vazio, um texto não pode estar em branco, uma imagem precisa de URL, o carrossel precisa de pelo menos 2 imagens e a lista de tópicos não pode estar vazia. Se algo estiver errado, o Director recusa o roteiro e informa qual bloco tem problema. Na página "Montar notícia", essa mensagem aparece para o redator na etapa de revisão. O Director também oferece o construirNoticiaPadrao(), uma receita fixa para notícias simples (cabeçalho, autor e um único texto).
4. O produto: a Notícia (js/noticia.js)
O objeto Notícia guarda as informações gerais (título, subtítulo, categoria, capa, autor, rodapé) e a lista de blocos do corpo. A variante é definida pelo builder concreto que a criou e é ela que aplica o CSS de cada versão. A data de criação é gerada automaticamente no construtor, então nem o Cliente nem o Builder informam a data, como está na nota do diagrama.
class Noticia {
constructor(variante) {
this.variante = variante; // "destaque" | "compacta" | "mista", definida pelo builder concreto
this.posicaoTitulo = null; // só a versão mista usa: onde o título fica sobre a capa
this.titulo = "";
this.subtitulo = "";
this.categoria = "";
this.capa = null; // ImagemBloco
this.autor = null; // Autor
this.dataCriacao = new Date(); // gerada automaticamente: ninguém de fora define a data
this.blocos = [];
this.rodape = "";
}
adicionar(bloco) {
this.blocos.push(bloco);
}
...
}
O método renderizar() monta o HTML da página inteira (cabeçalho, autor, ações de compartilhar e salvar, capa, corpo e rodapé) e, para o corpo, apenas pede a cada bloco que se renderize.
5. As partes do produto: os blocos (js/blocos.js)
Essa é a composição entre Noticia e Bloco que a IA sugeriu na revisão do diagrama. Bloco é uma classe abstrata (também protegida com new.target), e cada tipo de bloco sabe gerar o seu próprio HTML. Os blocos recebem opções de configuração (capitular, largura, alinhamento, modo do carrossel), e são os builders concretos que escolhem essas opções. É por isso que o mesmo roteiro gera páginas diferentes.
class Bloco {
constructor() {
if (new.target === Bloco) {
throw new Error("Bloco é abstrato: use TextoBloco, ImagemBloco, CarrosselBloco ou TopicosBloco.");
}
}
renderizar() {
throw new Error("renderizar() deve ser implementado pela subclasse.");
}
}
class CarrosselBloco extends Bloco {
constructor(imagens, { modo = "slides" } = {}) {
super();
this.imagens = imagens; // [{ url, legenda }]
this.modo = modo; // "slides" (um por vez, com setas) | "miniaturas" (faixa rolável)
}
...
}
Todo texto informado pelo usuário passa pela função escaparHtml() antes de entrar no HTML, para evitar que um título ou legenda injete código na página.
6. O Cliente (js/cliente.js e js/montar-noticia.js)
O Cliente escolhe qual builder concreto vai ser usado, transforma os dados da notícia no roteiro, entrega tudo ao Director e, no final, pede o resultado ao builder. Trocar o visual da notícia é só trocar o builder passado ao Director, sem mexer em nenhuma outra linha.
const builders = {
destaque: () => new NoticiaDestaqueBuilder(),
compacta: () => new NoticiaCompactaBuilder(),
mista: () => new NoticiaMistaBuilder(seletorPosicao.value),
};
const builder = builders[versao]();
const director = new NoticiaDirector(builder);
director.construirPersonalizada(
exemplo.titulo,
exemplo.subtitulo,
exemplo.categoria,
exemplo.capa,
new Autor(exemplo.autor.nome, exemplo.autor.foto),
roteiroDoExemplo(exemplo),
exemplo.fonte
);
const noticia = builder.getResultado();
conteudo.innerHTML = noticia.renderizar();
Na página "Montar notícia", o formulário preenchido pelo redator é convertido no mesmo tipo de roteiro (List<ItemRoteiro>), respeitando a ordem em que os blocos foram adicionados, e segue exatamente o mesmo caminho até o Director.
Vídeo
Link do vídeo: https://youtu.be/PYR8mjU7FMI
2. Singleton do Repositório de Notícias
O código está em demonstracao_codigos/SubGrupo1/singleton. A implementação foi feita em JavaScript puro e pode ser executada de duas formas:
- Terminal: na pasta do padrão, executar
node demo.js(ounode demo.js --pausar, que avança uma etapa por vez ao pressionar ENTER). - Navegador: abrir o arquivo
index.html, que reproduz a Página inicial e as páginas de categoria do protótipo no Figma. No painel do Singleton (canto inferior direito), selecionar uma notícia e um builder e clicar em "Publicar no repositório"; a notícia passa a aparecer no Destaque, nas Últimas notícias e na página da categoria correspondente.
As duas versões compartilham os mesmos arquivos e reutilizam diretamente o código do Builder (pasta ../builder), sem duplicá-lo.
1. A instância única e o construtor bloqueado (js/repositorioNoticias.js)
Como o JavaScript não oferece construtores privados, como ocorre em Java, utilizei um atributo estático privado (#liberado) como mecanismo de controle: o construtor só pode ser executado quando a chamada parte do próprio getInstancia(). Qualquer tentativa externa de instanciar a classe com new RepositorioNoticias() lança uma exceção.
class RepositorioNoticias {
static #instancia = null;
static #liberado = false; // só o getInstancia() pode chamar o construtor
#noticias = [];
constructor() {
if (!RepositorioNoticias.#liberado) {
throw new Error("RepositorioNoticias é um Singleton: use RepositorioNoticias.getInstancia() em vez de new.");
}
...
}
2. O ponto global de acesso
O método estático getInstancia() cria o repositório apenas no momento da primeira solicitação (lazy initialization) e, nas chamadas seguintes, retorna sempre a mesma instância. O bloco try/finally assegura que o mecanismo de controle seja restaurado mesmo que ocorra alguma falha durante a criação do objeto.
static getInstancia() {
if (RepositorioNoticias.#instancia === null) {
RepositorioNoticias.#liberado = true;
try {
RepositorioNoticias.#instancia = new RepositorioNoticias();
} finally {
RepositorioNoticias.#liberado = false;
}
}
return RepositorioNoticias.#instancia;
}
3. Os dados que o Singleton protege
A lista de notícias é encapsulada em um atributo privado (#noticias). O método publicar() impede o cadastro de notícias duplicadas, e os métodos de consulta retornam novas listas, de modo que nenhuma tela consegue alterar o estado interno do repositório. Os destaques correspondem às notícias construídas pelo NoticiaDestaqueBuilder.
publicar(noticia) {
if (this.#noticias.some((n) => n.titulo === noticia.titulo)) {
return false;
}
this.#noticias.push(noticia);
return true;
}
listarPorCategoria(categoria) {
return this.#noticias.filter((n) => n.categoria === categoria).reverse();
}
listarDestaques() {
return this.#noticias.filter((n) => n.variante === "destaque").reverse();
}
4. Os clientes: as telas do portal (js/telas.js)
As telas não armazenam cópias das notícias nem instanciam o repositório. Todas herdam da classe abstrata Tela, que obtém o repositório por meio de getInstancia() sempre que necessário.
class Tela {
get repositorio() {
return RepositorioNoticias.getInstancia();
}
}
class PaginaCategoria extends Tela {
noticias() {
return this.repositorio.listarPorCategoria(this.categoria);
}
}
5. A ligação com o Builder (js/redacao.js)
A classe Redacao seleciona o builder concreto, delega ao NoticiaDirector a construção da notícia e publica o resultado no repositório único. Dessa forma, o Builder é responsável por como a notícia é construída, enquanto o Singleton é responsável por onde ela é armazenada.
const builder = criarBuilder();
const director = new NoticiaDirector(builder);
director.construirPersonalizada(/* título, categoria, capa, autor, roteiro... */);
const noticia = builder.getResultado();
const publicada = RepositorioNoticias.getInstancia().publicar(noticia);
Execução no terminal
A demonstração apresenta as seguintes etapas:
- As três telas solicitam o repositório: apenas a primeira chamada cria a instância, e as demais recebem a mesma referência (a comparação
===resulta emtrue). - Uma tentativa de instanciação direta com
new RepositorioNoticias()é bloqueada. - A redação publica quatro notícias construídas com os diferentes builders.
- Cada tela consulta o mesmo repositório.
- Uma nova notícia publicada passa a aparecer em todas as telas, sem necessidade de recriá-las.
- A tentativa de publicar uma notícia duplicada é recusada.
1. Três telas do portal pedem o repositório
Página inicial → getInstancia() → repositório #1 criou a instância agora
Categoria Esporte → getInstancia() → repositório #1 reaproveitou a mesma
Destaques → getInstancia() → repositório #1 reaproveitou a mesma
inicial.repositorio === esporte.repositorio → true
esporte.repositorio === destaques.repositorio → true
Instâncias criadas até aqui: 1
2. Alguém tenta criar um segundo repositório com new
✖ bloqueado: RepositorioNoticias é um Singleton: use RepositorioNoticias.getInstancia() em vez de new.
Instâncias criadas: 1
Vídeo
O vídeo apresenta o problema, a modelagem, os principais trechos do código e o padrão em execução no navegador e no terminal.
Link do vídeo: https://youtu.be/Vjo68VWZMos
3. Abstract Factory de Comunidades
O código está em demonstracao_codigos/SubGrupo1/abstract-factory. A implementação foi feita em JavaScript puro e roda direto no navegador, sem instalar nada: basta abrir o arquivo index.html.
- Página da comunidade (
index.html): reproduz a página de comunidade do protótipo no Figma, com banner, ícone, widget de destaque, últimas notícias e o formulário de publicação. No painel do canto inferior, é possível escolher uma comunidade de exemplo e trocar a fábrica concreta (Tecnologia, Esportes, Cultura ou Política). Ao trocar a fábrica, o tema, o widget e a regra de moderação mudam todos juntos, e o painel "Passos chamados na fábrica" mostra quais produtos foram criados.
Cada participante do padrão ficou em um arquivo separado dentro de js/, para ficar fácil relacionar o código com o diagrama:
| Participante do padrão | Classe(s) | Arquivo |
|---|---|---|
| AbstractFactory | ComunidadeFactory |
fabricas.js |
| ConcreteFactory | ComunidadeTecnologiaFactory, ComunidadeEsportesFactory, ComunidadeCulturaFactory, ComunidadePoliticaFactory |
fabricas.js |
| AbstractProduct | TemaVisual, PoliticaModeracao, WidgetPadrao |
produtos.js |
| ConcreteProduct | TemaTecnologia, PoliticaModeracaoEsportes, WidgetAgendaCultura etc. (um de cada produto por família) |
produtos.js |
| Client | CriadorDeComunidade (e o resultado, ConfiguracaoComunidade) |
criador.js |
| Uso na página | escolha da fábrica e montagem da comunidade | cliente.js |
1. A fábrica abstrata (js/fabricas.js)
O JavaScript não tem classes abstratas, então a ComunidadeFactory verifica new.target no construtor para não ser instanciada diretamente, e seus métodos de criação lançam erro se não forem sobrescritos. Seguindo o defineFactory() do exemplo GUIFactory da aula, ela também tem o método estático definirFactory(categoria), que recebe a categoria escolhida na criação da comunidade e devolve a fábrica concreta correspondente. No lugar de um if/else, usei um mapa de categorias, como sugere a observação do slide sobre melhorar essa comparação.
class ComunidadeFactory {
constructor() {
if (new.target === ComunidadeFactory) {
throw new Error("ComunidadeFactory é uma interface: use uma das fábricas concretas.");
}
}
static definirFactory(categoria) {
const fabricas = {
tecnologia: ComunidadeTecnologiaFactory,
esportes: ComunidadeEsportesFactory,
cultura: ComunidadeCulturaFactory,
politica: ComunidadePoliticaFactory,
};
const Fabrica = fabricas[categoria];
if (!Fabrica) {
throw new Error(`Categoria de comunidade desconhecida: ${categoria}`);
}
return new Fabrica();
}
criarTema() { throw new Error("criarTema() não implementado"); }
criarPoliticaModeracao() { throw new Error("criarPoliticaModeracao() não implementado"); }
criarWidgetPadrao() { throw new Error("criarWidgetPadrao() não implementado"); }
}
2. As fábricas concretas: uma por família
Cada fábrica concreta sobrescreve os três métodos de criação e devolve somente produtos da sua família. É isso que garante que uma comunidade de esportes nunca receba, por exemplo, a moderação de política.
class ComunidadeEsportesFactory extends ComunidadeFactory {
criarTema() { return new TemaEsportes(); }
criarPoliticaModeracao() { return new PoliticaModeracaoEsportes(); }
criarWidgetPadrao() { return new WidgetPlacarAoVivo(); }
}
class ComunidadePoliticaFactory extends ComunidadeFactory {
criarTema() { return new TemaPolitica(); }
criarPoliticaModeracao() { return new PoliticaModeracaoPolitica(); }
criarWidgetPadrao() { return new WidgetPautaPolitica(); }
}
3. Os produtos abstratos (js/produtos.js)
Os três produtos abstratos também são protegidos com new.target e cada um define um comportamento: aplicar() no tema, moderar() na política de moderação e renderizar() no widget. O tema, por exemplo, guarda as cores, o ícone e o banner da família e os aplica na página.
class TemaVisual {
constructor(familia, corPrimaria, corSecundaria, icone, banner) {
if (new.target === TemaVisual) {
throw new Error("TemaVisual é abstrata: use uma das implementações de família.");
}
...
}
aplicar(pagina) {
pagina.style.setProperty("--cor-primaria", this.corPrimaria);
pagina.style.setProperty("--cor-secundaria", this.corSecundaria);
pagina.style.setProperty("--banner", `url("${new URL(this.banner, document.baseURI).href}")`);
}
}
class PoliticaModeracao {
...
moderar(conteudo) { throw new Error("moderar() não implementado"); }
}
4. Os produtos concretos: o mesmo método, regras diferentes
Na política de moderação fica mais visível o polimorfismo: o cliente sempre chama moderar(), mas cada família implementa uma regra própria. Em Esportes, um post que fala de resultado precisa ter o placar e ofensas são recusadas. Em Política, toda afirmação precisa indicar a fonte e o autor.
class PoliticaModeracaoEsportes extends PoliticaModeracao {
moderar(conteudo) {
const ofensivo = /\b(lixo|vagabundo|idiota)\b/i.test(conteudo);
const citaResultado = /\b(venceu|perdeu|empatou|resultado)\b/i.test(conteudo);
const temPlacar = /\b\d+\s*[x×-]\s*\d+\b/.test(conteudo);
return !ofensivo && (!citaResultado || temPlacar);
}
}
class PoliticaModeracaoPolitica extends PoliticaModeracao {
moderar(conteudo) {
return /fonte:/i.test(conteudo) && /autor:/i.test(conteudo);
}
}
5. O Cliente (js/criador.js)
O CriadorDeComunidade recebe uma fábrica no construtor e usa apenas a interface da ComunidadeFactory e dos produtos abstratos. Ele não cita nenhuma classe concreta, por isso funciona com qualquer família, e acrescentar uma família nova não exige alterar este código. O resultado é uma ConfiguracaoComunidade, que reúne as três peças criadas.
class CriadorDeComunidade {
constructor(factory) {
this.factory = factory;
this.passos = [];
}
montarConfiguracao(nome, descricao) {
this.passos = [];
const tema = this.chamar("criarTema", () => this.factory.criarTema());
const moderacao = this.chamar("criarPoliticaModeracao", () => this.factory.criarPoliticaModeracao());
const widget = this.chamar("criarWidgetPadrao", () => this.factory.criarWidgetPadrao());
this.garantirMesmaFamilia(tema, moderacao, widget);
return new ConfiguracaoComunidade(nome, descricao, tema, moderacao, widget);
}
...
}
6. O uso na página (js/cliente.js)
Quando o usuário escolhe uma fábrica no painel, a página pede a fábrica concreta ao definirFactory(), entrega ao CriadorDeComunidade e usa os produtos devolvidos. Ao enviar uma publicação, ela é avaliada pelo moderar() da família atual.
criador = new CriadorDeComunidade(ComunidadeFactory.definirFactory(familiaAtual));
const config = criador.montarConfiguracao(exemploAtual.nome, exemploAtual.descricao);
conteudo.innerHTML = renderizarComunidade(config, familiaAtual);
config.tema.aplicar(conteudo);
// no envio do formulário de publicação
const aprovado = config.moderacao.moderar(texto);
Vídeo
O vídeo apresenta o diagrama gerado pela IA e as correções feitas, o diagrama final e o padrão em execução no navegador.
Link do vídeo: https://youtu.be/6jALRstWYp4
Versionamentos
| Nome do Membro | Contribuição | Data |
|---|---|---|
| Joao Pedro Lopes da Cruz | Monta estrutura inicial do documento e abre branch do grupo, acrescenta também os diagramas de uso de IA | 14/10/2026 |
| Joao Pedro Lopes da Cruz | Acrescenta autor, categoria e data de criação automática ao diagrama do Builder | 15/10/2026 |
| Joao Pedro Lopes da Cruz | Acrescenta o builder misto (título sobre a capa em 5 posições) e o subtítulo ao diagrama do Builder | 15/10/2026 |
| Joao Pedro Lopes da Cruz | Acrescenta a classe Imagem (url e legenda) e a legenda das fotos do carrossel ao diagrama do Builder | 15/10/2026 |
| Giovanna Felipe Guimaraes | Acrescenta o contexto e os diagramas (IA e próprio) do Singleton do repositório de notícias | 15/10/2026 |
| Giovanna Felipe Guimaraes | Acrescenta o código executável do Singleton (terminal e navegador) e a explicação da implementação | 15/10/2026 |
| Joao Pedro Lopes da Cruz | Acrescenta a explicação da implementação do Builder de notícias mostrando o código e adicionando o vídeo | 16/10/2026 |
| Andre Henrique de Souza Belarmino | Acrescenta a crítica ao diagrama da IA e o diagrama corrigido do Abstract Factory de comunidades | 16/10/2026 |
| Andre Henrique de Souza Belarmino | Acrescenta as lições aprendidas, o uso da IA, a metodologia do subgrupo, a explicação da implementação do Abstract Factory e o vídeo | 17/10/2026 |