Padrão 09 · Estruturais
Proxy
controlar a delegação ao serviço; o exercício apresenta carregamento virtual síncrono.
Começar pelo código
01 · Entenda a escolha
Os fundamentos do padrão.
09 — Proxy: Acesso controlado ao salão real
Subject Sala oferece entrar(Visitante). SalaReal registra entradas; ProxyDeProtecao implementa o mesmo contrato, consulta uma política injetada e só delega quando ela permite. A variante virtual mora separadamente na solução do exercício.
Executar
Node 26.10.0, npm 11.19.1, TypeScript 5.9.3, @types/node 26.0.0, lock independente. Dentro deste projeto:
npm ci --ignore-scripts
npm test
npm start
npm run solution
Da raiz Books:
node book-23/scripts/projetos.mjs 09 install
node book-23/scripts/projetos.mjs 09 check
node book-23/scripts/projetos.mjs 09 capture
node book-23/scripts/projetos.mjs 09 verify
O build limpa dist e compila ES2022/ESM com TypeScript estrito. Testes Node executam o JavaScript emitido. Não há importações de capítulos vizinhos; tentativas em exercise/ ficam fora do build oficial.
Fontes e papéis
src/contratos.ts: SubjectSala, visitante, decisões e resultados discriminados.src/sala-real.ts: Real Subject com histórico observável.src/proxy-protecao.ts: Protection Proxy por composição.src/politica.ts: política didática por mandato informado e normalização dos nomes.src/servico-com-controle.ts: alternativa com verificação no próprio serviço, sem um objeto real desprotegido exposto por ele.src/main.ts: negativa, permissão, contagem e bypass direto demonstrado.test/protecao.test.ts: chamadas, identidade do resultado, decisões, erros, histórico e alternativa.exercise/esolution/: Virtual Proxy com carregador síncrono injetado e seus testes.diagram.mmd: fonte lógica editável; renderização e QA visual pertencem à etapa de arte.evidence/: stdout real, testes, manifesto e execução em cópia isolada.
Contrato de entrada e política
Visitante contém nome e mandatoValido, um booleano fornecido. Nomes vazios geram erro; nomes válidos são normalizados sem modificar o pedido original. A política recebe uma cópia congelada com esses valores e devolve {permitida, motivo}. O proxy não classifica nome, ofício, título, aparência ou simpatia como merecimento moral.
Se a política nega, o resultado é {estado: 'negada', visitante, motivo} congelado e o real não é acionado. Se permite, o proxy chama a sala uma vez e devolve o mesmo objeto de resultado retornado por ela. Não fabrica número de registro nem converte um erro em sucesso. Exceções da política e da sala são propagadas; falha da política impede a delegação.
SalaReal guarda um registro sequencial local por entrada, com número, nome e estado permitida. As entradas são congeladas e a lista consultada é defensiva. O registro é um efeito local em memória, sem persistência, comprovação física ou auditoria externa. A negativa devolvida permite ao cliente documentá-la, mas o proxy não cria um diário persistente por conta própria.
Fronteira didática
mandatoValido não autentica identidade, não verifica assinatura ou documento e não representa uma fronteira de segurança. O cliente pode fornecer true. Além disso, chamar SalaReal.entrar() diretamente contorna a política: há teste e demonstração explícitos. Uma política recebida também não torna confiáveis implementações arbitrárias do serviço ou do chamador.
Controles reais de identidade, autorização, dados confiáveis e caminhos de acesso pertencem ao desenho da aplicação de produção; este modelo não os implementa nem os substitui. Seu propósito é observar a decisão de delegação e o contrato comum. Não abre portas físicas, não aciona a válvula e não garante drenagem ou segurança de um edifício. Os dados são tipados, sem validação genérica de JSON ou mecanismos de defesa para entradas externas arbitrárias.
SalaComControleProprio mostra uma alternativa simples: a verificação integra o serviço e ele não entrega ao cliente um Real Subject separado que ignore a decisão. Isso elimina o bypass específico do exemplo por aquela API; a mesma política booleana continua didática e não ganha autenticação ou garantias de produção por estar em outro lugar.
Proteção e virtualização
O proxy de proteção recebe uma sala já criada. Não é Virtual Proxy nem carregamento tardio. ProxyVirtual, separado em solution/, recebe () => Sala, carrega na primeira operação e reutiliza a instância. Ele não adiciona política de proteção, e o teste demonstra entrada sem mandato no real dessa variante.
O carregamento é estritamente síncrono. Se o carregador lança erro, a referência continua ausente e outra chamada explícita pode tentar novamente. Se a carga deu certo, mas a operação da sala falha, a instância permanece guardada e as próximas chamadas não recarregam. A primeira operação carrega antes de delegar qualquer validação ao serviço; nome inválido não é promessa de impedir a carga. Não há Promise, tratamento de corrida assíncrona, retentativa automática, invalidação ou benchmark de criação.
Custos e comparações
Proxy acrescenta uma camada, uma política de ciclo de vida ou delegação e mais um caminho que precisa ser compreendido. Quando o serviço pode aplicar seu controle de forma simples e apropriada ao contexto, uma camada separada pode não se justificar.
Decorator também pode conservar a interface, mas sua intenção é acrescentar responsabilidades ao comportamento; aqui a intenção é controlar acesso ou adiar criação. Adapter traduz interfaces incompatíveis; este Proxy mantém Sala em ambos os lados. Semelhança estrutural não torna as intenções equivalentes.
Testes, stdout e hashes vinculam execução às fontes. Não são auditoria técnica independente nem aprovação autoral, e não estabelecem segurança ou desempenho de uma integração real.
02 · Leia o mecanismo
O feitiço, linha por linha.
Comece por src/main.ts para ver o programa em uso. Explore os contratos, a implementação e os testes no seletor de arquivos.
import { politicaPorMandato } from "./politica.js";
import { ProxyDeProtecao } from "./proxy-protecao.js";
import { SalaReal } from "./sala-real.js";
import { SalaComControleProprio } from "./servico-com-controle.js";
const real = new SalaReal();
const proxy = new ProxyDeProtecao(real, politicaPorMandato);
console.log(`Nobre sem mandato: ${proxy.entrar({ nome: "Nobre", mandatoValido: false }).estado}`);
console.log(`Entradas no real após negativa: ${real.listarEntradas().length}`);
console.log(`Mecânico com mandato: ${proxy.entrar({ nome: "Mecânico", mandatoValido: true }).estado}`);
console.log(`Entradas no real após permissão: ${real.listarEntradas().length}`);
// Demonstra o limite: o real exposto não verifica a política por conta própria.
const direta = real.entrar({ nome: "Entrada lateral", mandatoValido: false });
console.log(`Acesso direto ao real sem mandato: ${direta.estado}`);
console.log(`Entradas no real após acesso direto: ${real.listarEntradas().length}`);
const integrado = new SalaComControleProprio(politicaPorMandato);
console.log(`Serviço com controle próprio sem mandato: ${integrado.entrar({ nome: "Visitante", mandatoValido: false }).estado}`);
03 · Revele as conexões
A estrutura por trás da magia.
Veja os papéis e suas relações. Amplie cada vista para ler atributos, métodos e conexões com calma.
Esta vista reúne Sala, SalaReal, ProxyDeProtecao. SalaReal realiza Sala. ProxyDeProtecao mantém referência a Sala (delega somente se permitido). ProxyDeProtecao: Negativa não chama o real. Positiva devolve o resultado real sem substituí-lo. SalaReal: Acesso direto ainda permite contornar o proxy deste modelo. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos. e3 · delega somente se permitido
Esta vista reúne Sala, ProxyDeProtecao, PoliticaAcesso. ProxyDeProtecao realiza Sala. ProxyDeProtecao depende de PoliticaAcesso (recebe função injetada). PoliticaAcesso: Função injetada: Visitante para DecisaoAcesso. Não é classe concreta. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos. e4 · recebe função injetada
Esta vista reúne Sala, Visitante, ResultadoEntrada. Sala depende de Visitante. Sala depende de ResultadoEntrada. Visitante: Booleano didático fornecido, sem autenticação de identidade. Membros exibidos são um recorte; construtores e demais membros podem estar omitidos.
04 · Faça sua escolha
Sua vez de conjurar.
Exercício — Adiar a criação não é autorizar
Em uma variante separada, implemente ProxyVirtual com o mesmo Subject Sala. Receba um carregador síncrono () => Sala, em vez de uma sala já criada. A primeira operação deve chamar o carregador; as seguintes devem reutilizar a instância retornada.
Critérios verificáveis:
- Construir o proxy não chama o carregador nem cria uma
SalaRealantecipadamente. - Após a primeira
entrar(), existe exatamente uma instância criada; após outra operação, continua uma e há duas entradas no mesmo serviço real. - Virtualização sozinha não acrescenta autorização: um
SalaRealsem controle próprio continua permitindo a entrada sem mandato. - Erro no carregador é propagado e não preenche a instância guardada; uma nova operação explícita pode tentar carregar outra vez.
- Erro na operação de uma sala já carregada não descarta a instância nem dispara uma carga extra.
- Explique a intenção de proteção versus a intenção de adiar a criação.
Compare com solution/proxy-virtual.ts e solution/virtual.test.ts; execute npm run solution. exercise/ fica fora da compilação da resposta oficial.
Resposta comentada
ProxyVirtual guarda o resultado de uma carga bem-sucedida e delega a mesma operação. Não há callback de carregamento assíncrono, Promise, coalescência de chamadas concorrentes, invalidação ou garantia de economia medida. Uma falha de carga só é tentada outra vez quando o cliente faz uma nova chamada; não existe retentativa automática dentro da mesma operação.
O objeto é carregado antes de delegar a primeira operação. Portanto, uma primeira chamada com nome inválido ainda cria a sala, que depois rejeita a entrada pelo contrato do serviço. O exercício não promete validar o pedido antes da carga.
O carregador é uma dependência injetada e deve devolver um Sala válido. Receber um objeto já pronto eliminaria a parte tardia da criação; por isso o teste cria SalaReal efetivamente dentro do carregador e verifica a ausência de instâncias antes do uso.
Compare depois da sua tentativa.
A resposta e seus testes estão na pasta solution/. O sucesso dos testes fornecidos confirma essa resposta; sua tentativa precisa estar incluída na compilação para ser avaliada.
Do estudo à prática
Experimente localmente.
Node 26.10.0 · npm 11.19.1 · TypeScript 5.9.3
Execute os comandos dentro da pasta deste projeto. O lock instala o compilador local. Os programas não são executados no navegador.
cd book-23/code/09-proxy npm ci --ignore-scripts --no-audit --no-fund npm test npm start # Depois da sua tentativa npm run solutionBaixar este projeto completo