Específicos · Engenharia de Software
GoF: criacionais, estruturais, comportamentais
Leia primeiro o que o item descreve, decida a família (cria objeto, monta estrutura, distribui comportamento) e só então confira o nome assinado — o molde dominante da banca é descrever um padrão…
Altíssima29 itens no tópico
A ideia que organiza o assunto
Um padrão de projeto não é código, nem biblioteca, nem framework. É a descrição de um problema recorrente e do arranjo de classes e objetos que costuma resolvê-lo, escrita em nível alto o bastante para ser reimplementada de outro jeito em cada sistema. Por isso duas aplicações que usam o mesmo padrão não têm o mesmo código: têm a mesma solução de projeto.
Os vinte e três padrões do catálogo GoF se dividem por o que eles fazem, e essa divisão é o primeiro filtro de qualquer item:
- criacionais — tiram do cliente a decisão de qual classe instanciar e como instanciar. Quando um item fala em criar, instanciar, clonar, montar ou garantir instância única, a resposta está aqui.
- estruturais — compõem classes e objetos em estruturas maiores sem mexer no que cada parte faz. Quando um item fala em adaptar, envolver, simplificar, representar, compartilhar ou tratar árvore de objetos, é aqui.
- comportamentais — distribuem responsabilidade e comunicação entre objetos: quem chama quem, em que ordem, sob que algoritmo. Quando um item fala em notificar, encadear, percorrer, desfazer, variar algoritmo ou guardar estado para restaurar, é aqui.
Daí sai o método de prova deste tópico, e ele é mecânico: leia a descrição primeiro, decida que padrão é aquele, e só depois olhe o nome que o item escreveu. O molde dominante da CEBRASPE aqui é definir um padrão com precisão de manual e assinar o nome de outro — “o padrão composite atribui responsabilidades adicionais a um objeto dinamicamente” é a definição exata do decorator. Quem julga pelo nome cai; quem julga pela descrição não precisa decorar item nenhum.
Por que se usa (e o que custa)
O ganho é reuso de projeto, não de código: o padrão já resolveu, em outro sistema e em outra linguagem, a tensão entre o que hoje funciona e o que amanhã vai mudar. Ele também dá vocabulário — dizer “aqui entra um observer” substitui meia hora de desenho — e ajuda a especificar interfaces, identificando os elementos-chave e o que trafega entre os objetos.
O preço é indireção. Todo padrão troca uma dependência direta por uma camada a mais: mais classes, mais delegação, mais arquivos para entender antes de achar onde o trabalho de fato acontece. O factory method resolve a parametrização por classe de produto, mas cobra uma subclasse nova só para trocar o produto. O decorator multiplica objetos pequenos e parecidos. Aplicar padrão onde não há o problema que ele resolve é o caso clássico de solução procurando problema — e é por isso que o padrão exige contexto entendido: sem conhecer o problema recorrente e as forças em jogo, não há como escolher.
Como funciona
Os dois princípios que sustentam o catálogo. O primeiro é programar para uma interface, não para uma implementação: o cliente declara o tipo abstrato e nunca depende da classe concreta que vai chegar. O segundo é favorecer a composição de objetos sobre a herança de classe: herança fixa o comportamento em tempo de compilação e expõe o pai ao filho; composição troca a peça em tempo de execução. Quase todo padrão é uma aplicação de um dos dois — e a decomposição do sistema em objetos é influenciada por encapsulamento, granularidade, dependências, flexibilidade e desempenho, não por elegância apenas.
Os criacionais. O singleton garante uma instância única e um ponto global de acesso: depois que o objeto existe, o sistema não pode criar outro daquela classe. O factory method define uma operação de criação e deixa a subclasse escolher a classe concreta do produto. A abstract factory vai um nível acima: uma interface para criar famílias de objetos relacionados, sem nomear as classes concretas. O builder separa a construção de um objeto complexo da sua representação, de modo que o mesmo processo de construção produza representações diferentes. O prototype especifica os tipos de objetos a criar usando uma instância protótipo e cria novos por cópia dela — e a cópia aproveita o estado do original, que é justamente o ponto do padrão.
Os estruturais. O adapter converte a interface de uma classe em outra que o cliente espera, fazendo trabalhar junto o que não trabalharia. O bridge separa uma abstração da sua implementação, para que as duas variem de forma independente. O composite monta objetos em árvores parte-todo e permite ao cliente tratar objetos individuais e composições de maneira uniforme. O decorator acrescenta responsabilidades a um objeto dinamicamente, como alternativa flexível à herança. A facade oferece uma interface única e simplificada para um conjunto de interfaces de um subsistema. O flyweight compartilha estado intrínseco para suportar muitos objetos pequenos com pouco custo de memória. O proxy fornece um objeto substituto que controla o acesso a outro objeto e a ele faz referência.
Os comportamentais. O chain of responsibility desacopla remetente e receptor passando a solicitação por uma cadeia de receptores potenciais até que um a trate. O command encapsula uma requisição como objeto, o que permite parametrizar clientes, enfileirar, registrar e desfazer operações, sem que o invocador saiba como a operação é executada. O iterator dá acesso sequencial aos elementos de uma coleção sem expor sua representação interna, protegendo a coleção do acesso direto. O memento captura e externaliza o estado interno de um objeto sem violar o encapsulamento, para que ele possa ser restaurado depois. O observer define dependência um-para-muitos: mudou o estado do objeto observado, todos os dependentes são notificados e atualizados — é a mecânica do MVC entre modelo e visões. O strategy encapsula uma família de algoritmos e os torna intercambiáveis, deixando o algoritmo variar independentemente dos clientes que o usam; o efeito colateral mais cobrado é remover lógica condicional da classe. Completam o grupo state, template method, visitor, mediator e interpreter.
O que decide os itens
Padrão → família → o que resolve. É a tabela que resolve a maioria dos itens:
| padrão | família | resolve |
|---|---|---|
| singleton | criacional | uma única instância e ponto global de acesso |
| factory method | criacional | subclasse decide qual classe de produto instanciar |
| abstract factory | criacional | criar famílias de objetos relacionados |
| builder | criacional | separar construção de representação |
| prototype | criacional | criar por cópia de uma instância protótipo |
| adapter | estrutural | converter uma interface na que o cliente espera |
| bridge | estrutural | separar abstração de implementação |
| composite | estrutural | tratar parte e todo de forma uniforme (árvore) |
| decorator | estrutural | acrescentar responsabilidade dinamicamente |
| facade | estrutural | interface única e simplificada para um subsistema |
| flyweight | estrutural | compartilhar objetos para economizar memória |
| proxy | estrutural | substituto que controla o acesso a outro objeto |
| chain of responsibility | comportamental | passar a requisição por uma cadeia de receptores |
| command | comportamental | requisição como objeto: enfileirar, registrar, desfazer |
| iterator | comportamental | percorrer a coleção sem expor a representação |
| memento | comportamental | guardar e restaurar estado sem violar encapsulamento |
| observer | comportamental | notificar dependentes quando o estado muda |
| strategy | comportamental | trocar o algoritmo sem tocar no cliente |
| state | comportamental | mudar o comportamento quando o estado interno muda |
| template method | comportamental | esqueleto do algoritmo na superclasse, passos na subclasse |
Pares que a banca troca o tempo todo:
| × | quem é quem |
|---|---|
| adapter × bridge | adapter converte interface existente; bridge separa abstração de implementação desde o projeto |
| adapter × strategy | variar algoritmo independentemente do cliente é strategy |
| proxy × bridge | objeto substituto que referencia outro é proxy |
| composite × decorator | uniformidade parte-todo é composite; responsabilidade adicional dinâmica é decorator |
| composite × facade | tratar objeto e composição de modo uniforme é composite, não facade |
| facade × builder | interface simplificada é facade; construção × representação é builder |
| command × chain of responsibility | cadeia de receptores é chain; requisição como objeto é command |
| singleton × prototype | instância única é singleton; instância protótipo copiada é prototype |
| iterator × decorator | alternativa flexível à herança é decorator; acesso sequencial é iterator |
| flyweight × adapter | excesso de instâncias e consumo de memória é flyweight |
Definição × implementação. Padrão é descrição de solução em alto nível, não framework, não biblioteca, não código pronto. Dois sistemas com o mesmo padrão têm o mesmo desenho, não o mesmo código — o padrão não unifica código.
Padrão exige contexto. A escolha depende de conhecer o problema recorrente, as forças e as consequências. “Não deu tempo de entender o contexto, então aplique um padrão” é o oposto do que o catálogo prescreve.
Padrões e interfaces. Os padrões ajudam a especificar interfaces: identificam seus elementos-chave e os dados que trafegam entre objetos. A essência não está na implementação nem nos algoritmos.
Números que caem
| padrões do catálogo GoF | 23 |
| famílias (propósito) | 3: criacional, estrutural, comportamental |
| padrões criacionais | 5: singleton, factory method, abstract factory, builder, prototype |
| padrões estruturais | 7: adapter, bridge, composite, decorator, facade, flyweight, proxy |
| padrões comportamentais | 11 |
| escopo (segunda classificação do GoF) | 2: de classe e de objeto |
| instâncias garantidas pelo singleton | 1 |
Como a CEBRASPE derruba você aqui
Os 29 itens do tópico estão explicados. Sobre os 15 de gabarito Errado: troca de termo 60% (9 itens), inversão 27% (4), relação causal e escopo ampliado com 7% cada. Catorze itens são Certo.
Duas coisas que a contagem revelou sobre o recorte, e que mudam o que vale a pena decorar. A primeira: um quarto do tópico não é sobre padrão nenhum. Sete dos 29 itens discutem o que um padrão é — se contribui para especificar interfaces, se é código pronto, se unifica código entre aplicações, quando se deve adotá-lo, quais são as três famílias, o princípio de programar para a interface — e quatro desses sete são de gabarito Errado. É a fatia mais rentável do tópico por item estudado. A segunda: dos 23 padrões do catálogo, cinco não aparecem uma única vez — state, template method, visitor, mediator e interpreter. E decorator e proxy, que são dos mais cobrados, nunca aparecem escritos no enunciado: eles só comparecem como a resposta certa por trás do nome de outro padrão. Quem só decora nome errado de padrão perde metade do jogo.
A definição do catálogo com o nome de outro padrão carimbado nela — 60%, a família absolutamente dominante. A frase descreve um padrão com precisão de manual e assina outro. O singleton cujo objetivo seria “especificar os tipos de objetos a partir de uma instância de protótipo” (é prototype); o bridge que “fornece um objeto substituto, que faz referência a outro objeto” (é proxy); o composite que “atribui responsabilidades adicionais a um objeto dinamicamente” (é decorator); o facade que “separa a construção de um objeto complexo da sua representação” (é builder); o adapter cujo propósito seria “separar uma abstração de sua implementação” (é bridge) e, em outro item, no qual “um algoritmo pode variar independentemente dos clientes que o utilizam” (é strategy); o command definido como “passar uma requisição entre uma lista ou objetos encadeados” (é chain of responsibility); o iterator como “forma flexível de uso de herança para estender uma funcionalidade” (é decorator); e o “tratar, de maneira uniforme, objetos individuais e suas composições” atribuído ao facade (é composite).
E aqui está o dado que muda a tática: em 6 desses 9 itens o nome errado pertence à mesma família da descrição. Dizer “o padrão de projeto estrutural bridge” e descrever o proxy é acertar a família e errar o padrão; o mesmo vale para adapter/bridge, composite/decorator, facade/composite, singleton/prototype e command/chain. Decidir a família é o primeiro filtro, mas ele sozinho resolve apenas 3 dos 9. O que resolve todos é a palavra de assinatura da definição, e elas são poucas e fixas: protótipo → prototype; dinamicamente junto de responsabilidade adicional → decorator; encadeado, cadeia de receptores → chain of responsibility; varia independentemente dos clientes → strategy; objeto substituto → proxy; parte-todo, individuais e composições → composite; alternativa flexível à herança → decorator; construção × representação → builder. Defesa: leia a oração que define, escreva você o nome, e só então olhe a assinatura do item.
A propriedade definidora virada do avesso — 27%. Quatro itens, e em três deles o padrão nomeado está certo e é a característica que lhe dá sentido que foi negada ou girada. O prototype que cria objetos “sem aproveitar o estado do objeto copiado”, quando aproveitar o estado é o padrão inteiro — pergunte-se: se isso fosse verdade, para que serviria o padrão? Os design patterns que “não se destinam a contribuir com especificação de interfaces de objetos, pois a essência dos padrões está nos algoritmos e na implementação”, quando é exatamente ao contrário: a essência é o arranjo de classes e a intenção, e eles ajudam sim a especificar interfaces. “Trata-se de um framework ou código pronto, e não de uma definição de alto nível de como um problema comum pode ser solucionado”, com as duas pontas do “não X, mas Y” simplesmente trocadas de lugar. E a recomendação girada entre dois padrões: “o padrão Adapter é mais apropriado que o padrão Flyweight” para excesso de instâncias consumindo memória, quando é o flyweight que resolve esse problema. Defesa: em item que compara dois padrões com “mais apropriado que”, monte a ordem certa antes de ler a do enunciado — a banca costuma apenas girar os dois nomes.
O benefício esticado até o código — 7%. “O uso de design patterns leva à unificação dos códigos utilizados em diferentes aplicações que utilizem o mesmo padrão.” O que se reaproveita é a solução de projeto; duas aplicações que usam observer terminam com o mesmo desenho de colaboração e com código inteiramente distinto. Defesa: padrão padroniza desenho e vocabulário, nunca implementação.
A condição de uso invertida numa oração explicativa — 7%. “Quando o contexto não estiver claro e não houver tempo para entendê-lo, devem ser adotados padrões de projeto constantes da literatura da área.” O porquê soa razoável e carrega a mentira: padrão descreve a solução de um problema recorrente dentro de um contexto compreendido, e escolhê-lo exige entender esse contexto. Defesa: padrão, framework ou norma oferecidos como substituto da análise do problema é sempre item errado.
Erros clássicos
Julgar pelo nome antes de ler a descrição. É o erro que produz a maior parte das quedas. Leia a oração que define, decida que padrão é, e só então compare com a assinatura.
Confundir adapter, bridge e proxy. Os três são estruturais e envolvem um objeto em outro, mas a intenção difere: adapter converte uma interface que já existe e não batia; bridge é projetado de antemão para que abstração e implementação variem em paralelo; proxy mantém a mesma interface e controla o acesso.
Achar que decorator e composite são a mesma coisa. Ambos compõem objetos recursivamente. Composite existe para tratar folha e nó do mesmo jeito; decorator existe para empilhar responsabilidades sobre um objeto em tempo de execução.
Trocar factory method por abstract factory. Factory method é uma operação que a subclasse sobrescreve para decidir um produto; abstract factory é um objeto que cria uma família inteira de produtos compatíveis.
Esquecer o custo do factory method. O próprio GoF registra a desvantagem: pode ser preciso criar uma subclasse nova apenas para mudar a classe do produto.
Tratar singleton como boa prática neutra. Instância única é estado global compartilhado: dificulta substituir a dependência por dublê e quebra o isolamento dos testes de unidade.
Confundir flyweight com cache ou com pool genérico. O flyweight separa estado intrínseco (compartilhável, guardado no objeto) de extrínseco (passado pelo cliente a cada operação). É essa separação que permite muitos objetos com pouca memória.
LidoPraticado