Alura > Cursos de Programação > Cursos de Java > Conteúdos de Java > Primeiras aulas do curso Design Patterns em Java II: avançando nas boas práticas de programação

Design Patterns em Java II: avançando nas boas práticas de programação

Adapters para reutilizar dependências - Apresentação

Olá, pessoal. Sejam bem-vindos ao treinamento de design patterns com Java, meu nome é Rodrigo Ferreira e vou ser o instrutor que vai guiar vocês durante este treinamento.

Para começar, aquela discussão, o que é design patterns? Você como desenvolvedor, desenvolvedora, se você trabalha com Java em especial já deve ter esbarrado com esse termo, design patterns, que foi traduzido como padrões de projeto, embora eu prefira chamar de padrões de design de código, que seria mais relacionado ao objetivo desse termo.

No mundo Java e em outras linguagens também nos esbarramos com esse termo que são alguns padrões que utilizamos para organizar o código. Aqui coloquei um resumo, uma opinião minha do que seria um padrão de projeto.

Um design pattern nada mais é do que uma solução comum para um problema que é comum também, um problema que é recorrente, que sempre aparece quando estamos trabalhando com desenvolvimento de softwares que utilizam o paradigma da orientação a objetos.

Quando você vai desenvolver um software utilizando esse paradigma, você esbarra em alguns problemas. Por exemplo, preciso criar uma classe A, uma classe B e uma classe C. Essa classe vai ter que conversar com essa, como vou fazer essa comunicação? Esse código está duplicado, como vou extrair isso? Será que tem uma maneira mais simples, mais fácil de organizar esse código?

É justamente essa ideia. Sempre nos esbarramos com esses problemas, e são problemas comuns, que se repetem de acordo com essa mesma necessidade, esse mesmo problema.

Ao invés de sempre termos que pensar do zero em uma nova solução, ficar reinventando a roda, a ideia seria utilizar uma solução que já é comum, que já é popular entre as pessoas que trabalham com desenvolvimento de software e que já passaram por esse problema diversas vezes. Daí veio essa ideia de você criar um termo, definir um padrão que é universal, que serve sempre para resolver aquele problema específico.

Esse termo design patterns ficou famoso com a escrita desse livro chamado Design Patterns, escrito por esses quatro autores, Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides, e depois ficou conhecido como gang of four, GOF abreviado, o livro da gangue dos quatro, que são esses quatro autores, que eles meio que criaram um catálogo documentando os 23 principais padrões utilizados no paradigma de orientação a objetos.

Baseado na experiência deles, no que eles viam na comunidade, eles chegaram nesses 23 possíveis padrões e escreveram esse livro catalogando, documentando cada um desses padrões, explicando a motivação, o problema que cada um desses padrões resolve, quando utilizá-los e como você pode fazer para utilizar.

Isso se popularizou, as pessoas começaram a utilizar esse catálogo e utilizar essas soluções sempre que se esbarravam nesses problemas que foram descritos nesse livro.

Esses padrões se encaixam em três principais categorias. Tem padrões que tem mais a ver com como você cria objetos, então às vezes para criar um objeto não é só instanciar, dar um new na classe. Às vezes você tem um processo que é um pouco mais chato, você tem que envolver outras classes, parâmetros e tal, e você pode cair em algumas dificuldades.

Existem alguns padrões que são da categoria criacionais, que te ajudam justamente nisso, na criação de objetos. Tem outros padrões que te ajudam na estrutura de objetos, como vou estruturar, compor esses objetos e relacioná-los entre si.

Alguns outros padrões se encaixam nessa última categoria que eles chamaram de comportamentais, que tem a ver com comportamentos, com chamadas de métodos, e interação entre esses objetos. Você tem essas três principais categorias.

Na verdade, esse curso da Alura é o segundo curso de design patterns, eu gravei um outro curso onde falo especificamente sobre os padrões comportamentais, e nesse treinamento específico vamos focar nos padrões estruturais.

Obviamente existem 23 padrões no total, mas a ideia não é explicarmos cada um deles aqui, até porque tem alguns que são mais raros, digamos assim, que dificilmente você vai passar por essas necessidades. Eu escolhi aqui os principais, os mais comuns que nos esbarramos no dia a dia.

Da categoria estruturais, os padrões que vamos discutir e aprender neste treinamento são o padrão adapter, o decorator, composite, facade e proxy. Escolhi esses cinco padrões, que são os mais comuns, que nos esbarramos no dia a dia, e no curso vamos aprender cada um desses padrões, qual a motivação deles, qual o problema que eles vem para resolver, e como podemos aplicar cada um deles.

Espero que vocês gostem bastante deste treinamento, possam utilizar esses padrões quando fizer sentido no projeto de vocês, e vejo vocês na primeira aula, onde vamos rever o projeto que vimos no primeiro treinamento. É uma recomendação que você assista o primeiro treinamento e conheça os outros padrões comportamentais para você também conhecer esses padrões e também saber o projeto que vamos trabalhar neste curso, que é o mesmo projeto que finalizamos no curso anterior.

Vejo vocês na primeira aula, onde vamos aprender sobre o primeiro padrão, o padrão adapter. Um abraço e até lá.

Adapters para reutilizar dependências - API de registro de orçamento

Continuando nosso treinamento de design patterns, na última aula fizemos uma introdução sobre esse termo design patterns e falei sobre o projeto que vamos trabalhar no treinamento.

Vocês vão ter uma atividade para fazer o download, para importar o projeto no computador de vocês, que é um projeto que desenvolvemos no primeiro curso de design patterns, então recomendo bastante, caso você não tenha feito, que você faça esse primeiro treinamento, pois lá você vai conhecer a aplicação que vamos trabalhar aqui no curso e também já de cara vai aprender alguns dos padrões de projetos, no caso os padrões da categoria comportamental.

Nesse projeto, eles representam uma loja, e nessa loja temos a questão de orçamento, a principal classe que trabalhamos foi essa classe orçamento. O orçamento tem um valor, tem alguns itens e tem uma situação, se ele está em análise, aprovado, reprovado, enfim.

Baseado nesse orçamento fomos desenvolvendo algumas funcionalidades. Por exemplo, temos funcionalidades para fazer cálculo de desconto, preciso saber dado um orçamento qual o valor do desconto, temos várias regras para desconto, dependendo do número de itens, do valor, e utilizamos alguns padrões para organizar essa parte do código, para implementar essa funcionalidade.

Tem também a parte de impostos, temos uma calculadora de impostos e baseado no orçamento preciso saber quais são os impostos que vou aplicar em cima desse orçamento. Também utilizamos alguns padrões, criamos interfaces, polimorfismo para representar essa parte de impostos e deixar o código bastante flexível e desacoplado, e coeso também.

Por fim fizemos uma parte de pedido, que no final quando o orçamento for aprovado precisamos gerar um pedido, temos uma classe que representa o pedido, implementamos o padrão observer para disparar algumas ações após o pedido ser gerado.

Fomos implementando esse sistema, seguindo alguns problemas, que foram surgindo durante o treinamento. Esses problemas foram principalmente código duplicado, código muito extenso, código difícil de manter, código engessado onde não consigo estender, adicionar novas características.

Fomos aplicando alguns padrões de projeto para deixar o código mais limpo, mais organizado. A ideia é que nesse treinamento vamos continuar mexendo nesse projeto e vamos implementar algumas outras funcionalidades, passar por outros problemas aplicando outros padrões de projeto.

A primeira coisa que vamos fazer aqui é, quando um orçamento for finalizado, temos mais uma ação que precisamos disparar, precisamos registrar esse orçamento em uma API externa. Essa API externa é um sistema externo que vai fazer algumas ações com esse orçamento.

Para o nosso sistema não importa muito o que esse sistema externo vai fazer. A única coisa que precisamos saber é que preciso chamar essa API para fazer o registro desse orçamento. No pacote de orçamento vou criar uma nova classe, "Ctrl + N”, class, vou chamá-la de RegistroDeOrcamento.

Aqui dentro vou criar um método public void registrar, esse método recebe um parâmetro chamado orcamento. Qual a ideia? Quando eu passar esse orçamento como parâmetro preciso chamar essa API externa para ela fazer o tratamento e levo as informações desse orçamento como parâmetro.

Preciso fazer uma chamada http para a API externa. Essa é a lógica que preciso implementar aqui. Porém como faço para disparar uma requisição http para um serviço externo? Tem algumas opções no Java. Podemos utilizar, por exemplo, a classe "Url Connection", do próprio Java puro, sem usar nenhuma biblioteca.

Posso implementar isso, porém é um pouco chato, tem alguns tratamentos que são mais chatos. Eu poderia, por exemplo, utilizar a biblioteca do apache, "Http Client". Eu poderia utilizar alguma biblioteca para fazer uma chamada Rest, enfim.

Tem várias implementações. Tem várias possibilidades de fazer essa chamada a essa API externa via http. Porém aqui temos um problema. Se escrevemos o código dessa integração com essa biblioteca utilizando o protocolo http, vou misturar meu código de registro de orçamento com detalhe de infraestrutura, que seria a implementação em si da chamada com o protocolo http para essa API externa.

Isso não seria muito legal, porque futuramente eu poderia querer trocar, eu comecei com Url Connection, quero trocar para o Http Client, só que vou ter que mexer no código que tem a lógica de registro, e isso fica meio misturado, fico meio preso a uma implementação.

Quero abstrair esse conceito, essa comunicação utilizando protocolo http em um outro código separado que vai fazer essa adaptação entre meu código e essa API externa, só que aí no próximo vídeo vemos uma solução para resolver esse problema de maneira mais elegante. Vejo vocês no próximo vídeo, um abraço e até lá.

Adapters para reutilizar dependências - Aplicando o pattern Adapter

E aí, pessoal, continuando aqui então. Precisamos fazer essa mudança no código porque não quero deixar na classe de registro de orçamento o código em si que faz a chamada, toda a comunicação via http. Quero criar tipo uma abstração, deixar isso fora dessa classe e deixar o código flexível para que eu consiga trocar a maneira, a biblioteca, e fazer a chamada via http.

A ideia seria criar uma classe que vai fazer essa ponte entre nossa classe RegistroDeOrcamento e a chamada em si com o protocolo http. Ela vai fazer essa adaptação entre esses dois mundos, digamos assim.

Vou criar aqui uma nova classe, "Ctrl + N”, class, vou chamá-la de HttpAdapter, porque a ideia dela é justamente isso, fazer uma adaptação do protocolo http, e vou colocá-la no subpacote http pacote principal loja.

Essa classe, na verdade não vai ser nem uma classe, vai ser uma interface, já que quero ter múltiplas implementações, vai ser só uma interface, e ela vai ter um método void post, por exemplo, para disparar uma requisição do tipo post. Preciso de dois parâmetros, uma string com a url e os parâmetros que quero fazer nessa chamada post.

Os parâmetros podem ter vários tipos, então para simplificar vou usar um mapa, cuja chave é uma string e o valor é um object. Vou chamar de dados. Essa é a ideia da minha classe, da minha interface, ela é o adaptador de http. Ela tem um método para disparar uma requisição post e precisa receber a url e os dados. Simples assim.

Posso agora na classe RegistroDeOrcamento receber como parâmetro, ter um atributo que é justamente o adaptador, só que esse parâmetro não é um adaptador em específico, ele vai ser tipo uma interface. Ele fica flexível, posso trocar o adaptador apenas trocando a instância da classe.

Vou declarar como atributo private HttpAdapter, vou chamar de http o atributo, e vou fazer o mesmo esquema de receber como parâmetro no construtor esse adaptador. Assim que eu criar a classe RegistroDeOrcamento passo como parâmetro quem é o adaptador que a classe vai utilizar.

Aqui a ideia é pegar o orçamento e chamar o ‘http.post’, preciso passar a url e os dados. A url vou criar uma variável para representá-la, seria a url da nossa API. Vou colocar uma url fictícia só para exemplificar, então api.externa/orcamento, alguma coisa do gênero, e os dados vou criar também aqui um atributo ‘map<string’, o valor é object, pode ser qualquer coisa, é igual a, vou usar um recurso map.of para criar várias instâncias de parâmetros, chave valor.

Quais parâmetros vou enviar do orçamento? O valor, o valor vai ser objeto ‘orcamento.getValor’, e preciso também da quantidade de itens. Então, ‘”quantidadeItens”, orcamento.getQuantidadeItens’.

Faltou o nome da variável dados, ficou um colchetes, vou maximizar para facilitar a visualização. Esse é o objetivo da classe. Tenho um método registrar, ele tem a url da API e tem os dados baseado no orçamento, e ele cria esse mapa com essas duas informações, o valor, a quantidade de itens, pega o adaptador e chama o método post.

Olhando para essa classe registros de orçamento, tenho um adaptador para fazer a chamada via http, mas não sei qual o adaptador em si, como vai ser essa chamada, se vai ser utilizando a classe interna do Java, se vai ser utilizando um framework, uma biblioteca.

Para a classe de registro de orçamento não interessa isso, só preciso de um adaptador, só preciso conversar com http, mas não quero saber diretamente quais são os detalhes de implementação de infraestrutura da comunicação com o protocolo http. Estou abstraindo esse detalhe dessa classe.

Aqui só uma regra, uma validação que precisamos fazer é que só posso registrar um orçamento se ele estiver finalizado, então vou só fazer uma checagem no início, ‘if (orcamento.isFinalizado())’, se não estiver finalizado, no caso, vou jogar uma exception.

Não tem esse método isFinalizado, vamos criar esse método. O que ele vai devolver? Na classe orçamento tenho um atributo situação, que é um objeto do tipo situacaoOrcamento, que é uma classe abstrata que tem algumas implementações, preciso saber se o atributo situação instanceof finalizado.

Se for uma instância do tipo finalizada é porque ele está finalizado. E se não estiver finalizado, ‘throw new DomainException’, vamos lançar uma exception de domínio. Ele vai reclamar, porque preciso importar essa classe DomainException, e vou colocar uma mensagem “orçamento não finalizado”, algo do gênero, só para simplificar.

E está aqui implementado. Agora como faço para testar isso? Vou precisar de uma implementação desse adaptador. Vamos criar uma classe que implementa esse adaptador. A classe vai se chamar javaHttpClient, ou algo do gênero. Já vou colocar para ela implementar a interface HttpAdapter.

Obviamente sou obrigado a implementar o método post. Aqui a ideia seria disparar a requisição utilizando a API do Java. Na API do Java temos a classe url, url da API é igual a new url, preciso passar a string com a url em si que está vindo como parâmetro. Isso pode lançar exception, vou fazer um try catch, se cair aqui vou lançar um ‘throw new RuntimeException’, porque aconteceu alguma exception e não consegui disparar a requisição. Vou só mandar uma mensagem “erro ao enviar requisição http” e passo a exception que ocorreu embrulhada nessa RuntimeException.

Já tenho a uri e preciso falar que urlDaApi.openConnection, isso vai me devolver um objeto do tipo connection, vou jogar em uma variável, urlConnection, esse é o objeto que ele vai me devolver. Para eu disparar a requisição em si, preciso chamar um método, esse também pode lançar uma exception, então vou colocar um catch exception. Todas as exceptions vão ser tratadas nesse try catch.

Essa connection.connect, teria que passar os parâmetros, então só para não ficar delongando muito o vídeo com esses detalhes de infra, imagine que aqui tem o código de implementação. Aqui já tenho uma das implementações dos adaptadores. Para testar isso vamos criar uma classe de teste.

Vou criar no pacote raiz da aplicação uma classe de testesAdapter. Vou marcar para gerar o método main. Para testar precisamos ter primeiro um orçamento, então orçamento é igual a new orçamento. Para instanciar um orçamento preciso passar um valor e uma quantidade, então BigDecimal.TEN, quantidade 1.

Esse orçamento precisa estar finalizado, vou aprovar o orçamento, tem o método aprovar. Agora que ele está aprovado, orcamento.finalizar. Agora preciso instanciar aquela nossa classe ‘RegistroDeOrcamento registro = new RegistroDeOrcamento’, aqui está a questão, está o pulo do gato.

Na hora de instanciar nossa classe RegistroDeOrcamento preciso passar um adaptador de http. Posso passar um new JavaHttpClient. E aí chamo registro.registrar passando esse orçamento.

Ele vai chamar nossa classe, só não vou rodar aqui porque como essa url não existe, essa url que coloquei é fictícia, vai dar uma exception. Podemos só rodar para ver a exception acontecer. Vai dar de fato uma exception, ele vai dizer que não conseguiu chamar esse endereço API.externa.

Mas só para mostrar que funcionou, que ele fez a chamada. Agora aqui está a questão. Nosso código está bem flexível, se eu quiser criar um novo adaptador, posso criar um novo adaptador, é só criar uma nova classe que implementa essa nova classe, e aí é só implementar o método post utilizando o código específico desse novo adaptador.

Na hora de chamar, no nosso caso no método main, na hora de chamar o registro passo esse novo adaptador, então consigo trocar de adaptador na hora de chamar nossa classe RegistroDeOrcamento. Ela não está engessada com uma única implementação de integração com o protocolo http.

Esse é o padrão de projeto adapter. É um dos padrões de projeto que o objeto dele é esse, fazer uma adaptação. Tenho uma classe, no nosso caso a classe RegistroDeOrcamento, ela precisa conversar com a outra classe, no caso o protocolo http, só que tem várias maneiras distintas de conversar com essa outra classe, e não quero ficar preso a uma dessas maneiras e não quero implementar esses detalhes de infraestrutura.

Então crio uma classe no meio que vai fazer essa adaptação, essa ponte entre a minha classe e esse mundo externo, no caso. Essa classe vai funcionar como um adaptador. Ela está fazendo essa adaptação.

Utilizando interfaces e polimorfismos do jeito que fizemos deixamos nosso código bem flexível, posso trocar de adaptadores. Um exemplo de adaptador do mundo real, fazendo uma analogia, seria quando você vai ligar um cabo na tomada da sua casa.

Imagine que você comprou um celular novo, pegou o carregador dele, só que o carregador dele está naquele padrão novo brasileiro, que tem os três pinos. Só que a tomada da sua casa só tem dois pinos. E agora? Como faço para encaixar a tomada? O cabo na tomada. Vou ter que arrancar esse terceiro pino? Vou ter que mexer no meu código, no meu carregador, digamos assim?

Não, você pega um T, uma extensão, você pega um adaptador, você coloca um adaptador no meio e ele faz a ponte entre seu cabo e a tomada, a interface da tomada. Percebe? Utilizando um adaptador não temos que mexer no nosso código.

E se você trocar um dia, for para outra casa, trocar o cabo e tal, você pode trocar o adaptador. Pode trocar esse T, essa extensão, não vai precisar mexer em nenhum dos dois lados, então você consegue criar novas adaptações, criar novos adaptadores, sem ter que mexer no seu código.

Percebe? Crio novos adaptadores sem mexer no código existente. Isso te dá muito mais flexibilidade, deixa o código mais flexível. Esse que é o objetivo do padrão adapter. Pensa um pouco no projeto que você está trabalhando, estudando, se não tem uma situação dessa, que você tem duas classes, só que essa classe está dependendo de detalhes de infraestrutura demais, de muitos detalhes dessa outra classe que você possa futuramente ter que trocar.

Tenta criar um adaptador que vai fazer essa ponte para deixar seu código mais flexível. Espero que vocês tenham gostado desse padrão e vejo vocês no próximo vídeo, onde vamos continuar explorando outros problemas e conhecendo outros padrões. Um abraço e até lá.

Sobre o curso Design Patterns em Java II: avançando nas boas práticas de programação

O curso Design Patterns em Java II: avançando nas boas práticas de programação possui 76 minutos de vídeos, em um total de 38 atividades. Gostou? Conheça nossos outros cursos de Java em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Java acessando integralmente esse e outros cursos, comece hoje!

Plus

  • Acesso a TODOS os cursos da plataforma

    Mais de 1200 cursos completamente atualizados, com novos lançamentos todas as semanas, em Programação, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.

  • Alura Challenges

    Desafios temáticos para você turbinar seu portfólio. Você aprende na prática, com exercícios e projetos que simulam o dia a dia profissional.

  • Alura Cases

    Webséries exclusivas com discussões avançadas sobre arquitetura de sistemas com profissionais de grandes corporações e startups.

  • Certificado

    Emitimos certificados para atestar que você finalizou nossos cursos e formações.

  • Alura Língua (incluindo curso Inglês para Devs)

    Estude a língua inglesa com um curso 100% focado em tecnologia e expanda seus horizontes profissionais.

12X
R$85
à vista R$1.020
Matricule-se

Pro

  • Acesso a TODOS os cursos da plataforma

    Mais de 1200 cursos completamente atualizados, com novos lançamentos todas as semanas, em Programação, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.

  • Alura Challenges

    Desafios temáticos para você turbinar seu portfólio. Você aprende na prática, com exercícios e projetos que simulam o dia a dia profissional.

  • Alura Cases

    Webséries exclusivas com discussões avançadas sobre arquitetura de sistemas com profissionais de grandes corporações e startups.

  • Certificado

    Emitimos certificados para atestar que você finalizou nossos cursos e formações.

  • Alura Língua (incluindo curso Inglês para Devs)

    Estude a língua inglesa com um curso 100% focado em tecnologia e expanda seus horizontes profissionais.

12X
R$120
à vista R$1.440
Matricule-se
Conheça os Planos para Empresas

Acesso completo
durante 1 ano

Estude 24h/dia
onde e quando quiser

Novos cursos
todas as semanas