Pizzaria Polimórfica

No meu treinamento de Java básico existe um tópico denominado ‘recursos avançados’ no qual eu ensino polimorfismo aos participantes. Este tópico em especial tem sido uma experiência muito interessante, uma vez que eu venho aprimorando vários diferentes modelos de explicação e exercícios baseados no próprio feedback retornados pelos alunos. Na verdade, eu tenho buscado nestes anos aprimorar uma melhor forma de apresentar o conteúdo com o objetivo de fazer com que o aluno concretize efetivamente tanto a sintaxe e principalmente a semântica deste poderoso recurso da orientação objetos.  E justamente hoje é o que eu gostaria de estar escrevendo neste artigo, abordando de uma forma prática, rápida e conceitual todos os detalhes relativamente importantes envolvidos dentro deste contexto.

Polimorfismo é o recurso que habilita um objeto a se comportar de maneiras variáveis ao longo de sua existência dentro do programa orientado a objetos. O termo polimorfismo é originário do grego e significa “muitas formas”. Tecnicamente falando, o polimorfismo é o nome dado para a implementação que permite que referências de tipos de superclasses mais abstratas apontem concretamente para instâncias de objetos de classes-filhas. Assim, é possível tratar várias hierarquias de tipos de maneira homogênea. Os benéficos que justificam o seu uso são clareza, poder de manutenção e flexibilidade.

O conteúdo será apresentado em um exercício 100% prático, no qual iremos criar um contexto adicionado outros fatores e elementos que estarão gradualmente gerando o conhecimento.  É assim que eu espero que aconteça com você leitor.

Vamos imaginar que somos responsáveis por implementar um sistema que controla a fabricação e venda de um simples restaurante de pizza de calabresa. Dado o contexto, poderíamos então escrever um projeto bem modesto de automatização.

Versão 1




A classe PizzaCalabreza implementa os comportamentos necessários para podermos fabricar, assar e cobrar o determinado valor pela pizza naquele restaurante. A classe Forno assume a responsabilidade de fabricar fisicamente a pizza, executando os passos corretos que prepara, assa e cobra a pizza. A classe Programa simplesmente é usada para possuir o método padrão que inicia todo programa desktop JSE. Execute o programa e veja seguinte saída ser apresentada no console:

molho, queijo, calabreza, cebola e tomate
15 minutos
R$ 12,00

Vamos imaginar que o programa esta funcionando com sucesso até que o proprietário do restaurante decide aumentar o cardápio acrescentando uma nova pizza.  Conseqüentemente fomos requisitados para alterar o programa que suporte o controle agora de pizza sabor napolitana.

Versão 2

Baseado nos novos requisitos, facilmente adicionamos uma nova classe no projeto chamada de PizzaNapolitana responsável por implementar os detalhes do próprio sabor em particular.

E conseqüentemente alteramos o programa para executar a pizza com o novo sabor e deixar tudo funcionando com a nova pizza agora.

Ao tentar executar novamente o sistema, notaremos um erro de compilação no momento de fabricar a pizza de napolitana. Veja que existe um pedaço da linha grifado em vermelho dizendo que existe um erro naquele lugar.  É muito comum nos esbarrarmos em diversos problemas na manutenção no qual temos que identificar a sua natureza e tomar decisões sobre eles. Eu costumo dizer que para se tomar a decisão correta em sistemas OO, nos temos que visualizar as coisas em duas diferentes perspectivas:

Técnica – consiste em levantar o fato em aspectos relacionado com a sintaxe de programação.
Conceitual – consiste em levantar o fato em aspectos relacionado com a abstração da modelagem no qual esta sendo automatizada.

Aplicando estas duas visões no nosso exemplo, teríamos algo assim:

Técnica – O erro de compilação esta acontecendo por que o a método fabricar da classe Forno somente recebe objetos da classe PizzaCalabresa. E neste caso estamos passando um objeto errado.

Conceitual – Existe um erro neste projeto OO por que construímos um forno que somente assa pizzas do sabor de calabresa. E agora estamos querendo colocar dentro deste forno uma pizza do sabor no qual ele não foi construído para assar.

É importante pontuar que precisamos entender a parte conceitual do que estamos implementando por que é ela que vai nos dar direção de como e onde intervir no projeto.  No caso do nosso exemplo, esta claro que precisamos que um forno tenha capacidade de assar qualquer sabor de pizza e que isso é com acontece no mundo real. Sem mais demoras, nos iremos mudar a classe Forno capacitando-a de assar então esse novo sabor de pizza. O passo mais lógico é aqui fazer uma sobrecarga de método, acrescentando outro que fabrica o sabor da nova pizza proposta.

Depois disso, já podemos voltar a executar que o sistema voltara a funcionar, agora fabricando os dois sabores de pizza. Execute o programa novamente e veja seguinte saída ser apresentada no console:

molho, queijo, calabreza, cebola e tomate
15 minutos
R$ 12,00
molho, pressunto, queijo, tomate e oregano
19 minutos
R$ 18,00

 

Projeto de Classes

Este é ponto tanto esperado no qual sempre fazemos uma análise de todo o código até aqui escrito, verificando assim para qual direção o projeto esta se deslocando. No exemplo até aqui proposto, poderíamos afirmar que o projeto esta no mesmo caminho do famoso navio Titanic. Ou seja, no fundo do mar da inflexibilidade! Vejamos por que:

Duplicação de Código – veja que copiamos e colamos o mesmo método de fabricar da pizza de calabresa para a napolitana, apenas mudando o tipo do parâmetro. Fizemos isso por que precisávamos fazer a classe Forno fabricar a outra pizza.

Falta de Clareza – veja que implementamos métodos com nomes diferentes nas classes de pizzas que correspondem ao mesmo comportamento de ambas. E nos dois métodos fabricar da classe Forno acabamos tendo que corrigir de acordo.

Baixa Manutenção – veja que quando for solicitado para se adicionar outro sabor de pizza, teremos que repetir os passos de duplicação de código para fazer o forno assar o novo sabor. Neste caminho, todas as vezes que acrescentarmos uma nova pizza, o Forno terá que ser alterado para suportar a nova estrutura. Imagine agora qual será o tamanho da classe Forno quando tivermos uns 40 sabores de pizza? Sim, conterá 40 diferentes métodos chamados fabricar que será responsável por assar cada pizza. Se isso não fosse suficiente, imagine agora que depois destes 40 tortuosos sabores implementando, testado e funcionando, nos tivéssemos que alterar o modelo de fabricação de pizza de todas as pizzas do sistema? Um simples exemplo disso seria se o proprietário nos requisitasse para acrescentar uma determinada quantidade de borda recheada com catupiri, no qual cada uma dos sabores teria uma quantidade especifica. Ou seja, precisaríamos então alterar os 40 métodos para todas as pizzas.

Este é um típico cenário no qual pode ser aplicado o polimorfismo para corrigir estes problemas.  Não podemos esquecer justamente os benefícios do polimorfismo que é a clareza, poder de manutenção e flexibilidade que são os opostos dos problemas encontrados.  A questão chave é que os responsáveis pelo sistema é quem devem estar constantemente atentos para identificar os cenários e prontos então para tomar o posicionamento correto na infra-estrutura de classes. Chegado o ápice do nosso exercício, vamos evoluir o projeto aplicando este tal de polimorfismo.

Versão 3

O primeiro passo é definir uma hierarquia “paternal” com o intuito de gerar comportamento genérico para todas as supostas pizzas do projeto. No exemplo decidi usar uma interface ao invés de uma classe abstrata ou concreta, que também poderia ser utilizado.

Nesta interface foi definido os três métodos necessários para todas as pizzas do projeto. Os próximos passos são criar as pizzas herdando desta interface, subrescrevendo os métodos pendentes e definir neles os comportamentos específicos de cada pizza em questão.

Próximo passo é considerado o mais importante. É nele que o “plin-plin” do polimorfismo acontece. Iremos alterar a classe Forno para se comportar polimorficamente, dando a ela a capacidade de assar qualquer tipo de pizza.

É muito importante entendermos que o método fabricar da classe Forno usa uma referencia polimórfica que é justamente o mecanismo chave de execução. A referencia polimórfica pode apontar para qualquer objeto que seje filho da interface Pizza . Ou seja, trocando a explicação, isso que dizer que este método tem a capacidade de receber parâmetros variáveis para todos os objetos de classes que sejam obrigatoriamente classes filhas da interface Pizza.

Para finalizar, alteramos a classe do programa, agora executando o nosso suposto exemplo.

Execute o programa pela ultima vez e veja a saída apresentada é a mesma da versão 2. Ou seja, o projeto continuou fazendo a mesma coisa, porem estamos com um projeto OOP flexível. Fazendo uma análise final, vejamos os pontos anteriormente analisados:

Duplicação de Código – não houve duplicação de código por que o único método da classe Forno é capaz de receber e executar polimorficamente qualquer outro objeto filho de Pizza.

Falta de Clareza – não houve falta de clareza por que a interface pizza estabeleceu uma padronização nos nomes dos métodos herdados e corretamente sobrescritos nas classes pizzas filhas.

Baixa Manutenção – veja que quando for solicitado para se adicionar outros sabores de pizzas, não será necessário alterar nada da classe Forno. Simplesmente teremos que fazer a nova classe seje filha da interface Pizza e sobrepor os comportamentos em especifico daquele determinado novo sabor. E o melhor de tudo seria se fossemos requisitados para alterar o modelo de fabricação de pizza, como no exemplo anteriormente citado. Neste novo projeto, qualquer alteração no método fabricar ira propagar a alteração para todos os sabores de pizza no projeto! Ou seja, mexeu uma vez, alterou simplesmente tudo!

Conclusão

Eu costumo dizer nos meus cursos que é OOP é a implementação da visão do mundo real que vivemos. E neste caso, o polimorfismo é o recurso utilizado para implementar os relacionamentos genéricos e variáveis que determinados objetos precisam possuir com outros objetos. Veja o caso do nosso exemplo, o forno é um objeto que precisa assar N tipos de diferentes objetos pizzas. E com isso, nos usamos o recurso do polimorfismo para expressar sintaticamente na linguagem de programação este tipo de relacionamento. O caso vai se repetir inúmeras vezes dentro de outros contextos, com outros escopos de objetos.

Todas as especificações, tecnologias, componentes e frameworks encontrados no mercado do mundo Java são construídos e elaborados usando em alguma parte o esquema de polimorfismo. Isso acontece por que eles necessitam ter em alguma de suas partes lacunas a ser preenchidas pela própria aplicação, justamente por serem usados em uma variação de  diferentes de soluções. Resumindo, dominar este conceito vai ter ajudar a entender e trabalhar com as potências do mundo Java.

Gostaria de Aprender Mais?

Lançamos um curso EAD exclusivamente sobre polimorfismo – Java SE Polimorfismo – Projeto e Design Orientados a Objetos. Te aguardo por la!

Por hoje eu termino aqui meus amigos, o artigo fica aberto para comentários gerais ou quaisquer outras dúvidas.  Aquele grande abraço e até a próxima😉 .

“O SENHOR será Rei sobre toda a terra; naquele dia, um só será o SENHOR, e um só será o seu nome.” Zacarias 14:9

8 pensamentos sobre “Pizzaria Polimórfica

  1. Sensacional, estava lendo umas apostilas e outros sites sobre Design Patterns, mas este foi o que deixou bem claro como funciona !

  2. SENSACIONAL!!! BRAVO!!! BRILHANTE!!! GENIAL!!!
    Meu irmão, tu és bom nisso! Eu nunca havia entendido com clareza o tal de polimorfismo, agora acho que vai, rs!
    Muito obrigado por compartilhar conosco seu conhecimento, que seu retorno seja dobrado!
    Sucesso, abração!

  3. Gente! Que maravilha! Entendi finalmente Polimorfismo, depois de várias pesquisas e lendo outros materiais. Parabéns!

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s