Alura > Cursos de Programação > Cursos de PHP > Conteúdos de PHP > Primeiras aulas do curso Refatoração em PHP: boas práticas no seu código

Refatoração em PHP: boas práticas no seu código

Primeiros passos - Apresentação

E aí, pessoal? Sejam muito bem-vindos à Alura, eu sou o Vinícius Dias e vou guiar vocês em um treinamento onde vamos falar um pouco sobre refatoração e nós vamos utilizar a linguagem de programação PHP para isso. O que eu espero que você saiba de PHP antes de continuar comigo?

Eu espero que você tenha, pelo menos, uma boa base de orientação a objetos, porque a maioria das técnicas de refatoração envolve orientação a objetos. Eu espero que você tenha pelo menos um pouco de familiaridade com o Composer, porque vamos utilizar o Composer para instalar uma biblioteca no meio desse caminho.

Se você conhecer sobre testes, isso é muito bom e vai te adiantar em alguns momentos, porque temos testes neste projeto. Mas, se você ainda não conhece, não se preocupe, pode fazer este treinamento e estudar sobre testes depois. E o treinamento de Composer, aqui da Alura, fala um pouco sobre buscar dados na internet, inclusive temos um outro curso específico sobre web scraping.

Nós vamos utilizar um pouco disso neste treinamento, porque refatoração é o processo de melhorar um pouco o código, e nada melhor do que melhorar o código de um código real, de um projeto real. Então eu vou utilizar aqui um projeto que inclusive está abandonado, ou seja, é um projeto relativamente antigo. Deste projeto, partindo deste projeto, nós vamos mudar, vamos modificar, vamos mexer no código.

Deixa eu te apresentar esse projeto antes mesmo de começarmos o curso para você entender o código onde vamos mexer. Dando uma olhada neste projeto, o que ele faz? Ele é um Googlecrawler, ou seja, ele vai analisando, ele pega uma página do Google que você pesquisou, por exemplo, uma busca por teste.

Ele vai retornar vários resultados e esse código vai resultado a resultado, pegando o link e a descrição desse resultado para armazenar. Em cima disso podemos fazer várias coisas, por exemplo, esse código já foi usado, em produção, para realizar análise de SEO de alguns concorrentes inclusive. Isso é bastante interessante, é um código que já foi usado em produção.

Mas, continuando, nós temos, começando aqui, um termo de busca. Ele simplesmente recebe uma string e devolve uma string, ele pode ser representado como uma string.

Aqui, nessa implementação, ele simplesmente remove espaços, ele encoda como se fosse uma URL. Esse termo de busca é utilizado pelo nosso crawler. Então aqui, no nosso "Crawler.php", nós recebemos esse termo de busca, podemos utilizar algum proxy, porque quando você faz muitas requisições para o Google, ele identifica que você está utilizando algum tipo de robô, então eu não vou deixar mais você usar sem desbloquear um captcha.

Então nós podemos utilizar alguns proxies para fazer essas requisições. Já temos até alguns proxies implementados como esse antigo.

Esses proxies não estão com a implementação atualizada, mas tudo bem. E podemos fazer requisições para mais de um domínio do Google, caso você queira fazer uma busca para um país específico, inclusive podemos passar o código do país.

Mas, enfim, com isso tudo, o que ele faz é armazenar essas informações que recebeu no construtor e buscar os resultados. Então ele faz o crawl, ou seja, ele faz o parse desse resultado, ele rasteja nesse resultado, vai fazendo o parse, e no final ele devolve uma lista de resultados.

Essa lista de resultados nada mais é do que um vetor, que é como se fosse um array, só que bem mais performático, tem treinamentos aqui sobre esse assunto na Alura também. Mas, com esse vetor, ele basicamente vai adicionando resultados. Dessa forma eu garanto que essa lista sempre terá resultados. Instâncias da classe? Resultado e nada além disso.

Então aqui temos uma URL, um título e uma descrição. Repare que esse código é antigo, ele ainda não usava nenhum o PHP 74, nem o PHP 8, então as propriedades ainda não eram tipadas, nós podemos fazer isso fora desse treinamento, vale a pena estudar sobre isso.

Mas, enfim, continuando aqui, nós temos algumas exceções que podem ser lançadas quando o resultado do Google vem de um formato inválido, que não sabemos parsear, quando o resultado em si, é inválido, ou seja, não é um resultado em texto, é uma imagem, uma sugestão do Google ou alguma coisa assim, ou quando passamos uma URL em um formato inválido.

Então esses são alguns possíveis erros que o nosso código lança em determinados momentos e aqui está a implementação dos proxies.

Aqui nós temos aquela interface que o nosso crawler espera e ela sabe fazer uma requisição e devolver uma resposta HTTP, e fazer o parse de uma URL. Aqui nós temos uma implementação sem usar proxy nenhum.

Fazendo a requisição diretamente, sem fazer nenhuma modificação, e fazendo o parse da URL do Google, do resultado do Google. Com isso já temos conhecimento suficiente para começar a modificar esse código. Para modificar, vamos acabar rodando alguns testes. De novo, se você não entende sobre testes, não tem problema, você não precisa saber agora.

Mas, basicamente, são códigos que vão executar o nosso código para garantir que o resultado é o esperado. Conforme vamos mudando o nosso código, precisaremos mudar um pouco desses testes também. Tendo feito essa introdução do código que vamos ver, esse código que você já deu uma olhada, que dá para melhorar bastante, vamos usar como referência um livro muito famoso.

"Refactoring: Improving the design of existing code". Ou seja, refatorando: melhorando o design de código existente. Esse livro tem uma segunda edição, mas eu li a primeira edição dele e ele é uma leitura um pouco difícil, vamos dizer maçante. É difícil de ler esse livro, eu falo mais disso no final.

Uma outra referência bem interessante é o "refactoring.guru". De novo, no final do treinamento eu vou deixar essas referências para você, mas para você já fica situado, se quiser pesquisar uma coisa ou outra durante o treinamento.

Mas falei bastante, já te apresentei sobre vários termos aqui, tanto te apresentei ao projeto quanto às referências utilizadas, agora vamos finalmente botar a mão na massa, começar a entender o que é essa tal de refatoração e como aplicar.

Primeiros passos - O que é refatoração

E aí, pessoal? Bem-vindos de volta. Vamos, neste vídeo, entender o que é esse processo de refatoração. Para isso, como eu comentei na introdução, vamos utilizar um projeto real, um projeto que já existe, um projeto relativamente antigo e que tem muito código a ser melhorado. Para você pegar esse projeto você vai baixar, do primeiro exercício deste treinamento, e com o Composer você vai simplesmente executar composer install.

Eu já rodei, então aqui já está tudo configurado, eu não preciso fazer de novo, mas você vai fazer essa instalação, porque tem alguns pacotes como dependência desse projeto. Mas, feito isso, estamos prontos para executar tudo aqui. Inclusive, esse projeto, embora ele seja um pouco antigo, ele tem alguns testes.

Enquanto vamos mexendo, eu posso deixar aqui os testes rodando. Repare que, por ser um projeto antigo, ele inclusive tem alguns testes incompletos, alguns testes que ele vai pular, que são testes que poderiam acabar falhando, enfim, tem alguns problemas nessa suíte de testes, mas vemos que eles estão passando.

Mas vamos lá, vamos continuar para o projeto real. Aqui, em "src", a classe principal, a classe que faz a maior parte das tarefas deste projeto, é essa class Crawler.

Nessa classe nós temos um problema que está me incomodando: eu não quero mudar a funcionalidade do meu código, mas eu quero melhorar ele. Eu quero fazer com que o meu código faça mais sentido. Então, neste ponto, o que eu quero fazer? Na hora que eu construo uma classe crawler eu não quero precisar passar tantos parâmetros, eu quero passar só o que é necessário para a funcionalidade em si, da classe como um todo.

Para a classe como um todo, nós precisamos dessa dependência de proxy. Agora, eu construí esse crawler, que faz uma busca no Google. A partir deste crawler eu quero poder fazer várias buscas no Google. Então o termo de busca, o domínio, o código do país, esses detalhes, eu não quero receber aqui. Então vamos receber no outro método, no método que efetivamente faz a busca.

Vamos mover os parâmetros daqui para aquele outro método. Vamos nessa, no método getResults(), ou seja, que recupera os resultados, vamos receber uma (SearchTermInterface $searchTerm, ), ou seja, um termo de busca, vamos receber uma string $googleDomain, e vamos receber o string $countryCode). Vou quebrar a linha, para isso ficar um pouco mais legível.

Repare, como esse código foi feito há algum tempo, ainda temos algumas anotações, coisas que talvez nem seriam necessárias, mas vamos lá. Então eu estou recebendo os parâmetros que, repare, não estão sendo usados, mas eu vou dar um "Alt + Enter", e atualizar os comentários de código. Vamos nessa, continuar.

Eu não vou mais receber esses parâmetros aqui, não vou mais receber aqui. Não preciso dessa parte $searchTera e (stripos($googleDomain, needle 'googl'.') eu vou validar no ResultList. Vamos lá, essa outra parte eu não preciso e essa outra eu também não preciso.

Eu já tenho uma diminuição no meu construtor, uma diminuição de código, eu não tenho mais lógica no meu construtor, mas ainda dá para melhorar ele. Vamos avançar, eu quero pegar o searchTerm, então vamos ver onde que eu utilizava isso, que eu vou remover. Essa parte não precisa mais ser campos da nossa classe.

Vamos ver onde eu uso elas. Se eu não me engano, eu uso na hora de pegar a URL. No getGoogleUrl(). Repare que eu estou vendo onde essa variável é usada e vou passar para lá. Então aqui eu vou passar, ele espera getGoogleUrl, eu vou passar o ($searchTerm).

Porque aqui embaixo ele não vai mais usar o search term da classe, ele vai utilizar esse parâmetro. Então getGoogleUrl(string $searchTerm). Não é uma string que ele recebe, (SearchTermInterface $searchTerm). Agora sim. Essa SearchTermInterface tem aquele método __toString.

Só para você dar uma olhada, ele tem um __toString, então ele pode ser representado como uma string. Continuando, ele também vai precisar de um Google domain, vamos receber aqui (SearchTermInterface, $searchTerm, string $googleDomain). E vamos nessa. Não preciso deste this mais, vai pegar direto o $googleDomain.

Beleza, o country code, a mesma coisa, também vamos receber o string $countryCode. E vamos utilizar aqui embaixo, diretamente, sem acessar, sem o this, da propriedade da classe.

Vamos remover também o country code, ele já foi removido, show de bola. Vamos lá, estou passando aqui o ($searchTerm), preciso passar o $googleDomain e preciso passar o $countryCode.

Agora, teoricamente, está tudo certo. Vamos ver se utilizamos isso em outro lugar? Se eu fizer a busca por "search term", não estou utilizando em nenhum outro lugar. Repare que eu tinha uma propriedade criada no construtor para eu utilizar em um único método. Isso não era muito útil, concorda comigo?

Agora que fizemos essa refatoração, o código deve se comportar da mesma forma. Então, o que é o conceito de refatoração, para finalizarmos esse vídeo? A ideia de refatorar um código é alterar o design dele, alterar a implementação, mas sem que a funcionalidade em si mude. Ou seja, o meu código, ele continua fazendo a mesma coisa, ele continua acessando o Google, ele continua recebendo os mesmos parâmetros para fazer a mesma tarefa.

Ele acessa o Google, processa uma lista de resultados e devolve. Nesse cenário específico eu fiz algo que não é tão comum, eu mudei a assinatura dos métodos. O meu construtor agora está diferente, eu recebo só um parâmetro.

Esse método agora recebe mais parâmetros. Então quando estamos refatorando, é um pouco menos comum nós mudarmos a assinatura dos métodos, mas pode acontecer. De novo, a ideia de refatorar é manter o comportamento, fazer com que as coisas funcionem como funcionavam antes, sem mudar.

Não é uma otimização de performance, nada disso, só que o design do código, a forma como ele é implementado, vamos melhorar um pouco. Vamos fazer algumas alterações para tornar isso mais legível, mais fácil de testar.

Então, nosso caso agora, a mesma classe crawler, quando eu tenho um objeto dela, eu posso fazer várias buscas, eu posso realizar várias buscas usando a mesma classe, isso é uma outra vantagem. Com essa implementação, vários dos nossos testes vão quebrar, então, no próximo vídeo, eu volto para falarmos um pouco mais de refatoração e consertar esses testes.

Primeiros passos - Importância de testes

E aí, pessoal? Bem-vindos de volta. Como nós modificamos a assinatura dos nossos métodos, precisaremos alterar o código que usa esses métodos, o nosso construtor, a nossa classe. E tem muitos lugares, então esse vídeo será um pouco demorado. Se você quiser fazer sozinho, pode pausar esse vídeo, faça tudo e depois volte, assista acelerado, para garantir que fizemos igual. Mas vamos lá, vou começar por essa aqui.

Primeiro eu terei que mudar o nome deste teste. Este teste tenta instanciar um crawler com um domínio que contém o HTTP antes, e isso tem que dar erro. Só que isso agora dará erro na hora de chamar o método get result. Será testTryingToGetResultsWithHttpOnGoogleDomainMustFail().

Dá para melhorar esse nome, mas não vamos nos preocupar tanto com isso por agora. Vou tirar a parte new SearchTerm(search Term '') e $domain. Esse será um =new Crawler e esse crawler, quando eu fizer o $crawler->getResults() desse (new SearchTerm(searchTerm: ' ') vazio, passando esse $domain);, isso tem que lançar uma exceção, por isso tem o expectException.

No debaixo, com o domínio inválido, então esse também será aquele mesmo cenário, onde o erro passará a acontecer no método getResults, então vamos nessa, $crawler->GetResults(new SearchTerm(searchTerm: ' ') vazio também, de um googleDomain: 'invalid-domain').

De novo, isso deve lançar uma exceção. Vamos ver qual é o problema aqui, esse getResults, ele está faltando o country code. Então, aqui tem um detalhe que deixamos passar.

Aqui, o countryCode era opcional, ele podia ser vazio. Então precisamos também fazer isso aqui. Só estou refazendo todo o trabalho que eu tinha feito antes. Aqui, o string $countryCode = ' ' pode ser vazio aqui também.

Corrigido esse problema, agora não temos mais erro aqui. Vamos continuar. Eu estou criando alguns mocks, eu espero que isso gere uma exceção, eu estou criando uma StreamInterface para ser a resposta.

Tenho aqui um proxy, que também será um mock. Isso aqui, moleza, só vou passar esse mock para o nosso getResults.

Teoricamente essa classe de teste já está pronta para ser executada. Vamos para o próximo, que é esse, e este dará um trabalho legal também.

Vamos nessa, esse ($searchTerm) eu não preciso aqui, eu preciso dele no getResults($searchTerm).

Deixa eu ver qual é o problema, Google domain, está faltando, então também posso ter esse Google domain vazio. Posso ter esse Google domain com o padrão, que é string $googleDomain = 'google.com'. Eu poderia dar aquele "Ctrl + Z" de novo, para conferir, mas eu me lembro que o padrão era google.com.

Então vamos lá, agora ele não precisa mais, menos um erro. Aqui estamos utilizando alguns proxies. Eu acho que este ainda está funcionando, mas eu posso simplesmente remover esse teste, porque neste treinamento não usaremos os proxies. Então eu posso apagar isso ou eu posso tentar corrigir.

Vamos tentar corrigir e ver se ele vai funcionar. Vou usar esse getResults($searchTerm), que pelo menos ele já está configurado para pular caso o proxy esteja fora, então não temos esse problema.

Esse debaixo estamos sem implementação, então este código nem será atualizado, nem precisa ser atualizado. Ele nem será executado, no caso. Ok, esse já foi.

Agora vamos no personalizado. De novo esse searchTerm vai para o getResults, getResults($serchTerm). No debaixo, a mesma coisa o searchTerm vai para o getResults e aqui vamos passar o 'google.ab' como Google domain. Perfeito, isso é um domínio válido.

Só que esse country suffix não existe, então isso terá um problema. A princípio está tudo certo, vamos executar os nossos testes, garantir que eu não fiz nenhuma besteira e que, pelo menos o que já passava antes, continua passando, não temos nenhum erro novo, nem nada do tipo.

Enquanto isso vai executando, perfeito, tudo continua passando. Eu queria levantar o ponto que é a importância da escrita de testes quando fazemos a refatoração, porque quando trabalhamos com refatorações, quando temos um código que funciona e modificamos ele precisamos de alguma forma garantir que ele continue funcionando.

Por isso eu peguei esse projeto, que é antigo, mas tem alguns testes. É interessante também vermos um cenário real, que infelizmente temos testes desatualizados, que marcamos para resolver depois, temos testes incompletos, que não conseguimos resolver na hora. Isso é um cenário real, esse é um código que realmente esteve em produção, que ele foi utilizado em cenários reais, em empresas reais.

Então aqui estamos refatorando, e vamos refatorar muito mais, um projeto real. Sem testes, essa tarefa seria muito mais difícil. Repare que quando eu fui corrigindo os testes, eu percebi enganos que eu tinha cometido, como não deixar os parâmetros com os valores padrão, como eram antes, enfim, então esse vídeo foi para ressaltar a importância de testes quando fazemos refatorações.

Sobre o curso Refatoração em PHP: boas práticas no seu código

O curso Refatoração em PHP: boas práticas no seu código possui 97 minutos de vídeos, em um total de 47 atividades. Gostou? Conheça nossos outros cursos de PHP 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 PHP acessando integralmente esse e outros cursos, comece hoje!

Plus

De
R$ 1.800
12X
R$109
à vista R$1.308
  • Acesso a TODOS os cursos da Alura

    Mais de 1500 cursos completamente atualizados, com novos lançamentos todas as semanas, emProgramaçã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.

Matricule-se

Pro

De
R$ 2.400
12X
R$149
à vista R$1.788
  • Acesso a TODOS os cursos da Alura

    Mais de 1500 cursos completamente atualizados, com novos lançamentos todas as semanas, emProgramaçã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.

  • Luri, a inteligência artificial da Alura

    Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com Luri até 100 mensagens por semana.

  • 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.

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