A técnica Clean Code surgiu em 2008, quando Robert Cecil Martins lançou o livro Clean Code: A Handbook of Agile Software Craftsmanship, que é referência no assunto. O autor, aliás, está entre as 17 pessoas que assinaram o Manifesto Ágil, que é a base para diversas metodologias ágeis, como o SCRUM e FDD — Feature-Driven Development.

Adotar essa técnica proporciona benefícios às pessoas programadoras, pois facilita o entendimento, a manutenção do código fonte e a realização dos testes necessários para verificar se a lógica está correta, bem como se o programa cumpre com a sua função.

Entretanto, é preciso seguir uma série de orientações para garantir que o código esteja no padrão Clean Code. Para ajudar você a entender como a técnica funciona, preparamos este post que contém os seguintes tópicos:

Quer aprender mais sobre Clean Code? Continue conosco e boa leitura!

O que é Clean Code?

Uma das atividades das pessoas que desenvolvem softwares é realizar as manutenções em sistemas. Para isso, é preciso fazer a leitura e o entendimento de inúmeros códigos fontes de programas desenvolvidos por outras pessoas. Essa atividade pode ser muito mais fácil de se executar caso o desenvolvimento seja feito com base em boas práticas de programação.

Clean Code, que significa código limpo, é uma técnica que estabelece um conjunto de boas práticas e orientações sobre como desenvolver códigos que sejam facilmente entendidos, escritos e mantidos pelas pessoas desenvolvedoras.

O objetivo do clean code é garantir o desenvolvimento de aplicações com códigos de qualidade e que possam ser facilmente reutilizados.

Clean Code: para que serve e por que usar?

Códigos mal escritos são aqueles que não foram pensados para facilitar a manutenção, o entendimento e o seu reaproveitamento.

O problema de realizar a manutenção de software que contém código mal escrito é que uma pequena alteração pode impactar no funcionamento de todo o sistema. Caso isso aconteça, é necessário realizar inúmeras outras alterações para conseguir implementar a mudança desejada.

Entretanto, nem sempre isso é possível — o que faz com que a equipe perca muito tempo no entendimento desses códigos. Por isso, o desenvolvimento com base nas regras de código limpo ajuda a evitar problemas desse tipo, pois ao adotar boas práticas, os códigos desenvolvidos terão características semelhantes, o que ajuda na manutenção do sistema e na produtividade da equipe de desenvolvimento.

Em muitos cenários, quando analisamos a quantidade de esforços que terão de ser aplicados para realizar a alteração necessária ou até mesmo para outras alterações que poderão surgir no futuro, chegamos à conclusão que é mais fácil desenvolver toda a aplicação novamente.

As 25 principais regras do Clean Code!

Para desenvolver código limpo é preciso seguir algumas orientações e implementar boas práticas de programação. Confira algumas delas.

1. Utilizar nomenclatura clara e intuitiva

A nomenclatura é utilizada em diferentes situações em uma aplicação, como para atribuir nomes a variáveis, funções, classes, parâmetros e, até mesmo, os nomes dos arquivos. Portanto, é importante utilizar nomes que tenham relação com a finalidade do código. Uma função para exibir um alerta na tela, por exemplo, pode se chamar exibirMensagem().

Dessa forma, qualquer pessoa que tenha acesso ao código entenderá o objetivo dessa função. O mesmo princípio vale para os outros itens utilizados no código que mencionamos acima. Além disso, o ideal é que, ao atribuir nomes às variáveis, eles sejam substantivos, enquanto que as funções devem ser nomeadas com verbos.

2. Seguir os padrões utilizados no código  

Os padrões de nomenclatura podem conter variações, prefira utilizar nomes que facilitem o entendimento. Alguns projetos podem utilizar o nome de uma variável iniciada em minúscula e, se for um nome composto, ter a inicial em maiúscula, como “nomePessoa”. Já em outros projetos, a nomenclatura pode ser algo como “nomepessoa”.

O importante é seguir o modelo já iniciado, caso seja uma manutenção de código, ou utilizar o mesmo padrão em todo o projeto se for um novo sistema. O mesmo vale para os nomes de funções, arquivos, classes etc. Dessa forma, haverá uma padronização desses elementos, que poderão ser facilmente identificados por outras pessoas programadoras.

3. Manter os dados de configuração separados do código fonte

Os dados de configuração, como strings de conexão com o banco de dados, devem ser adicionados em um arquivo separado do código fonte. Uma boa alternativa é armazenar esse conteúdo em arquivo no formato JSON e deixá-lo na pasta raiz da aplicação. Isso permite, por exemplo, alterar a configuração do banco com facilidade sem a necessidade de modificar o código da aplicação.

4. Evitar repetições excessivas

Prefira desenvolver códigos curtos e que tenham uma única função específica, ou seja, retorne apenas um determinado valor. O objetivo é evitar que ele seja repetitivo e confuso.

Portanto, evite estruturas de repetições aninhadas, como diversos “ifs” seguidos, pois eles aumentam a complexidade do código. Uma forma de prevenir esses problemas é utilizando princípios de programação orientada a objetos, como o encapsulamento, que contribui para deixar o código mais limpo e funcional.

No mais, também é importante evitar a duplicidade de código. Isso significa que no sistema não deve haver trechos diferentes que desempenhem a mesma função.

5. Ter cuidado com o uso de comentários no código

Adicionar comentários em um código fonte é uma prática comum. Entretanto, ela deve ser evitada, especialmente se for para indicar uma ação à pessoa programadora, como uma série de alterações a fazer ou alguma falha do código.

Além disso, há riscos em utilizar comentários em excesso, pois dificilmente eles serão alterados quando houver uma manutenção do código. Isso significa que um comentário pode conter informações sobre determinado trecho do algoritmo que não corresponde mais ao que a função executa. Por isso, eles devem ser utilizados com moderação e alterados sempre que houver necessidade.

6. Realizar o tratamento de erros

Um dos problemas dos códigos mal escritos é a negligência com os erros da aplicação. Muitas falhas acontecem e podem causar a interrupção do sistema. Além de não serem previstas, a mensagem exibida à pessoa usuária da aplicação nem sempre será a mais adequada, o que prejudica a usabilidade do software.

Portanto, é importante realizar o tratamento de erros tanto para garantir o bom funcionamento do software, quanto para exibir mensagens esclarecedoras sobre o problema encontrado.

7. Executar testes limpos

A técnica Clean Code também deve ser aplicada aos procedimentos de testes do código fonte. Para isso, eles devem ser realizados com pouco código, ou seja, nada de testar toda a aplicação de uma vez. É preciso depurar pequenos blocos, que devem ser independentes.

Dessa forma, haverá a evolução dos testes sem que a parte já testada apresente um novo erro em função de novos trechos testados. Além disso, é preciso que exista um ambiente de testes para que seja possível validar o código quantas vezes for necessário. O ideal é que eles retornem verdadeiro ou falso. Dessa forma, fica mais fácil identificar quais códigos apresentam problemas.

8. KISS: mantenha as coisas simples!

É o primeiro princípio de usabilidade do design do produto, mas também é usado como princípio de codificação.

Existem inúmeras maneiras de descrever algo complexo de maneira precisa e curta e isso não se trata apenas de simplificar as coisas: você deve ser capaz de dar um passo para trás e ver o que você acabou de fazer da perspectiva de alguém que está vendo pela primeira vez. Quais seriam as dúvidas dessa pessoa? O que ela precisa saber antes de chegar à compreensão que você chegou? 

Em desenvolvimento, o princípio KISS se aplica de diversas formas, mas principalmente no design de código. Pense que o código que você escreve e o código que você realmente executa são bem diferentes, dependendo da linguagem e do compilador ou interpretador usado.

Todos os programas e scripts que você escreve são para pessoas, como você, que lerão e farão manutenção no seu código futuramente. Portanto, é sua responsabilidade torná-lo o mais fácil e rápido possível de ler. Seu objetivo de design de código, portanto, deve ser oferecer a melhor experiência possível para a pessoa usuária. 

9. Use a regra do escoteiro! 

Pessoas escoteiras têm uma regra em relação ao acampamento: devem deixá-lo mais limpo do que o encontraram antes. Com isso, eles podem garantir que não estão causando danos à estrutura, pelo menos no que diz respeito à limpeza do local, mesmo que o trabalho seja constante e que às vezes elas encontrem locais menos limpos do que o que deixaram antes.

Em software, manter o código limpo também é um desafio constante e as equipes de software devem decidir se, quando e como vão manter a limpeza de seu código.

Com o tempo, a qualidade do código-fonte subjacente de um sistema tende a se degradar e acumula trabalho, o que chamamos de “dívida técnica”. O “pagamento” dessa dívida é feito por meio de refatoração, necessária para manter o código em um estado interessante.

No geral, basta se perguntar: quando faz sentido gastar tempo trabalhando para melhorar o design da base de código? 

Algumas equipes adotam a abordagem de encerrar todo o trabalho de agregação de valor e simplesmente tentar limpar a base de código por uma semana ou outro período de tempo. 

A Regra das Pessoas Escoteiras, por outro lado, sugere uma abordagem alternativa, que é simplesmente tentar garantir que, a cada confirmação, você deixe o código melhor do que o encontrou.

Esse princípio de clean code faz parte da melhoria contínua e, com ele, as equipes podem melhorar a qualidade de seu código ao longo do tempo, enquanto continuam a entregar valor aos seus clientes e partes interessadas.

10. Mantenha dados de configuração em alto nível

Já que estamos usando os dados de configuração em muitos lugares, faz sentido que eles estejam em um nível alto, ou seja, na altura da nossa vista. Assim, eles também serão mais fáceis de mudar.

11. Use polimorfismo no lugar de “IFs”

Usar IF ou condicional, se refere a uma tomada de decisão, o que de fato aumenta a complexidade da aplicação e por isso devemos evitar o uso demasiado dele. Quando acontecer isso, escolha usar polimorfismo sdo que ficar tomando decisões em todo o método que vai criar.

12. Use mult-thread

A programação paralela (multithreading) é um conceito amplo. Ele pode descrever muitos tipos de processos executados na mesma máquina ou em máquinas diferentes.

Multithreading refere-se especificamente à execução concorrente de mais de um conjunto sequencial (thread) de instruções.

A programação multithread é a programação de vários threads de execução simultânea. Esses threads podem ser executados em um único processador. Ou pode haver vários threads em execução em vários núcleos de processador.

13. Separe os códigos mult-thread

É recomendado deixar o que é assíncrono separado do síncrono, dessa forma a gente não força um método a ser ou não ser assíncrono por causa de outra parte de código.

14. Utilize Async em métodos assíncronos!

Ao escrever código assíncrono em clean code, é normal ter retornos de chamada. Para corrigir esse problema e tornar o código mais legível, você pode tentar usar uma promessa e, depois, o Async.

Normalmente, ao lidar com código assíncrono, você normalmente está tentando obter algumas informações de um banco de dados que leva x tempo para ser concluído. Como na imagem a seguir:

Código assíncrono

Para imitar uma chamada para um banco de dados, vamos fazer duas promessas e torná-las assíncronas usando setTimeout em ambas:

Promessa no código
Chamada assíncrona

Explicando o código acima, primeiro usamos a promessa firstName passando o argumento de 1. Para ter acesso ao valor, você tem que usar o método .then que recebe um retorno de chamada onfullfilled. O argumento será o valor que devolve a promessa.

No nosso caso, firstteremos um valor da string de Brandon, que passamos para a próxima promessa de fullName. Então fullName também terá um método .then que terá outro retorno de chamada.

Este é um código muito simples, provavelmente o código que você escreverá pode exigir mais aninhamento e será mais complexo. O que async e await nos permitem fazer é basicamente ler código assíncrono como código síncrono. 

Usando async e await, o código agora é mais curto e mais fácil de seguir. Isso não parece mais legal? Agora vamos destrinchar e entender o que está acontecendo.

Começando com a primeira metade da dupla mortal async.

Ao colocar essa palavra-chave na frente de uma função ou função anônima em nosso exemplo, isso garante que a função sempre retornará uma promessa. Async também é necessário na frente da função, caso contrário await não funcionará, este é um requisito do mecanismo JavaScript.

Com await, sempre que você tem uma função que retorna uma promessa, você pode aguardar o resultado e obter o valor real. Para o nosso exemplo, a variável de first tornou-se a string de Brandon e full tornou-se a string de Brandon Briones. Agora digamos que esquecemos de colocar await na frente de fullName, o que seria full então?

Como mostrado acima, a variável full seria apenas a promessa real não resolvida. Mesmo que pareça síncrono, ainda é assíncrono. 

Enquanto estiver na função, se await estiver presente, ele interrompe a função e diz que a próxima linha de código não será executada, a menos que essa promessa seja resolvida. 

Isso também não significa que se torne um bloqueio. Assim que o mecanismo JavaScript atingir o await, ele sairá dessa função e verá o que mais pode ser executado. Uma vez que a promessa seja resolvida, o passo voltará para essa função e concluirá sua execução.

15. Evite configurações desnecessárias

Não deixe configurações no sistema, por falta de definição por parte de alguém. Isso faz o código ficar poluído e deixa tudo mais complexo desnecessariamente.

16. Utilize injeção de dependência

É impossível prever como o código ficará no futuro enquanto estiver sendo desenvolvido. Nos casos em que vários desenvolvedores trabalham no mesmo projeto, pode não ser fácil identificar as dependências de uma determinada classe. Isso ocorre porque eles podem ter sido codificados. Isso pode levar a uma série de erros e outros problemas.

A solução é injeção de dependência e qui estão três boas razões para usá-la:

1. Para melhorar a manutenibilidade

A injeção de dependência é uma técnica que melhora a manutenibilidade do código separando a construção de uma classe de seu uso.

Uma classe não cria suas dependências. Em vez disso, eles são fornecidos externamente, geralmente como argumentos para o construtor ou passados ​​para um método.

2. Para melhorar a legibilidade

A baixa legibilidade do código é um dos problemas mais prevalentes encontrados na engenharia de software. A injeção de dependência é uma maneira eficaz de melhorar a legibilidade, garantindo que as partes do sistema sejam desacopladas. 

Isso torna o código mais fácil de ler porque separa uma classe e sua funcionalidade de objetos relacionados, tornando o código mais leve e simples.

3. Para melhorar a testabilidade

A testabilidade do código é sempre uma grande preocupação para os programadores. Quando o código é mal testado, pode levar a erros no ambiente de produção. A injeção de dependência pode ser usada para facilitar o teste de unidade removendo as dependências difíceis de substituir. 

17. Aplique a Lei de Demeter

A Lei de Deméter é uma diretriz de design com o objetivo de criar um acoplamento frouxo entre objetos. Também é conhecido como o princípio do menor conhecimento. Esta é a lei dos métodos: quanto menos um método souber sobre outros métodos de objetos, menos acoplamento você terá.

As Regras Deméter:

  1. Um método de um objeto pode chamar métodos desse objeto;
  2. Um método pode chamar um método de seus parâmetros;
  3. Um método pode chamar um método de qualquer objeto criado ou instanciado por esse método;
  4. Um método pode chamar um método de objetos que são dependências do objeto de métodos;
  5. Um método pode chamar um método de uma variável global que é acessível pelo objeto do método.

Bem, isso significa que um método deve ser capaz de chamar métodos em um objeto que ele tenha disponível de acordo com essas regras. Mas você só pode chamar um método de um nível de profundidade. Você não deve aninhar chamadas de método porque então você está sabendo muito. Muito conhecimento neste caso significa muito acoplamento em seu código.

As principais vantagens de satisfazer a Lei de Deméter são as seguintes:

  • As dependências entre classes e acoplamentos são reduzidas;
  • Reutilize as aulas com facilidade;
  • O código é mais fácil de testar;
  • O código é mais sustentável e flexível a mudanças.

Neste exemplo você pode ver o método ruim e bom:

< ?php
classe Classe1
{
    privado $class2 ;
    função pública __construct ( Class2 $class2 ) 
    {
        $this – > classe2 = $class2 ;
    }
    função pública badMethod () 
    {
        return $this – > class2 ->badMethod () – > methodClass3 () ;
    }
    função pública goodMethod () 
    {
        return $this – > class2 ->goodMethod () ;
    }
}
classe Classe2
{
    privado $class3 ;
    função pública __construct ( Class3 $ class3 ) 
    {
        $this – > classe3 = $class3 ;
    }
    função pública badMethod () 
    {
        return $ this- > class3 ;
    }
    função pública goodMethod () 
    {
        return $this – > class3 ->methodClass3 () ;
    }
}
classe Classe3
{
    função pública methodClass3 () 
    {
      // Código para este método
        retorna verdadeiro ;
    }
}
$class3 = new Class3 () ;
$class2 = new Class2 ( $class3 ) ;
$class1 = new Class1 ( $class2 ) ;
if ( $class1->methodBad ()) { 
    echo ‘O método ruim era verdade!’ ;
}
if ( $class1->methodGood ()) { 
    echo ‘O bom método era verdade!’ ;
}

Você não deve esperar que Class1 chame um método de Class3. Class1 não deve ter conhecimento sobre métodos Class3 porque não é passado diretamente como parâmetro, não é uma dependência direta ou é instanciado por Class1. Em vez disso, você deve lidar com qualquer lógica para Class3 na própria Class2 .

18. Utilize variáveis auto-explicativas

Anteriormente, já discutimos que preferimos o código autoexplicativo. Por exemplo, usando nomes bem escolhidos para variáveis ​​e métodos. Ou extraindo algum código para uma variável ou método nomeado corretamente para torná-lo mais legível. Veja, por exemplo, este fragmento de código:

int timeout = 5; // timeout in seconds

Infelizmente, o comentário só será visível nesta linha, mas não nos locais onde a variável é utilizada. Quando atualizamos o nome, podemos nos livrar do comentário e deixar a unidade clara para todos os lugares em que usamos esse tempo limite:

int timeoutInSeconds = 5;

Às vezes uma pequena melhoria no código pode tornar um comentário obsoleto e, ao mesmo tempo, melhorar a legibilidade do código.

20. Evite condicionais negativas com “!” 

Os negativos são um pouco mais difíceis de entender do que os positivos. Portanto, quando possível, as condicionais devem ser expressas como positivas. Por exemplo:

if (buffer.shouldCompact())

é preferível a:

 if (!buffer.shouldNotCompact())

21. Escolha nomes descritivos para classes, variáveis e métodos

Escolher bons nomes para suas classes, métodos, variáveis ​​e afins é essencial. Mas o que exatamente é um bom nome? Embora existam algumas diretrizes geralmente aceitas, não há consenso. Por isso, vamos pegar algumas dicas do livro “Clean Code”, de Robert C. Martin.

0. Use nomes autoexplicativos;

1. Use abreviações apenas quando forem amplamente conhecidas;

2. Escolha clareza em vez de brevidade;

3. Use convenções amplamente aceitas (na maioria das vezes);

4. Não use notação húngara;

5. Atenha-se ao estilo de codificação do idioma/framework/projeto;

6. Os nomes dos métodos devem começar com um verbo;

7. Os nomes das classes devem ser substantivos;

8. Os nomes das propriedades devem ser substantivos ou frases adjetivas (específicas de C#);

9. Use nomes pronunciáveis/pesquisáveis.

22. Não utilize prefixos ou caracteres com o tipo da variável, classe ou método!

É uma má ideia usar um prefixo para saber o tipo de variável. Hoje em dia os IDEs são poderosos. Além disso:

  • Não prefixe p para ponteiro, ch para caractere.
  • Não prefixe interfaces com I, enums com E etc.
  • Não prefixe os campos com i_ ou m_ ou s_.

Exemplo:

caractere **ppc; //Ruim

char **argumentValues ​​//Bom

23. Declare as variáveis próximas de seu uso

Declare a variável local o mais próximo de onde ela é usada e remova o desnecessário uma vez. Variáveis ​​locais declaradas em algum lugar diferente dificultam a leitura do código.

Exemplo:

Abaixo estão as variações de aRandomFunction que faz um monte de operações (puladas por pontos) e na conclusão retorna “Hello” junto com a data.

Código com variações de aRandomFunction

Os nomes das variáveis ​​locais e a data são declarados no topo das funções.

O primeiro uso das funções está em algum lugar entre a função e o último uso está na instrução return.

Variações de aRandomFunction na instrução return

Aqui as variáveis ​​de nome e data são movidas para onde são usadas pela primeira vez.

Digamos que após a movimentação percebemos que não utilizamos as variáveis ​​entre o código, apenas a utilizamos durante o retorno.

Então, devemos movê-lo para perto da instrução return:

Variações de aRandomFunction na posição de return

24. Agrupe as funções similares

Quando seus projetos começarem a ficar maiores, suas classes provavelmente terão muitas variáveis. Primeiro, você deve manter todas as suas declarações de variáveis ​​no topo da página, ou pelo menos todas juntas em algum lugar – isso acelera qualquer tipo de busca quando você precisa encontrar algo.

Em segundo lugar, embora estejam todos juntos, muitas vezes ajuda a organizá-los de tal forma que os torna ainda mais fáceis de compreender.

Por exemplo, agrupá-los todos pelo que são é um bom caminho a percorrer. É bem provável que você tenha vários tipos do mesmo objeto, então mantenha-os todos juntos em grupos, e então talvez tenha uma seção para os diversos abaixo.

25. Não quebre a indentação

A formatação do código é sobre comunicação, e a comunicação é a primeira ordem de negócios para o desenvolvedor competente.

O tipo de codificação e a legibilidade estabelecem precedentes que continuam a influenciar a manutenção e a extensibilidade mesmo após a modificação do código original.

Ao formatar o código, você deve considerar os cenários abaixo.

  • Separe os conceitos verticalmente;
  • O código relacionado deve aparecer verticalmente denso;
  • Declare variáveis ​​próximas ao seu uso;
  • As funções dependentes devem ser fechadas;
  • Funções semelhantes devem ser fechadas;
  • Coloque as funções em uma direção descendente;
  • Mantenha as linhas curtas;
  • Não use alinhamento horizontal;
  • Use espaço em branco para associar coisas relacionadas e desassociar fracamente relacionadas;
  • Não quebre o recuo.

Confira um exemplo de Clean code na prática!

1. Atribuir um valor à mesma coisa condicionalmente usando operadores ternários.

❌ 
a > b ? foo = 'apple' : foo = 'ball';

✔️ 
foo = a > b ? 'apple' : 'ball';

2. Atribuir condicionalmente o mesmo valor a uma propriedade de objeto específica.

❌ 
c > d ? a.foo = 'apple' : a.bar = 'apple';

✔️ 
a = { [c > d ? 'foo' : 'bar']: 'apple' };

3. Exportando várias variáveis

❌
export const foo;
export const bar;
export const kip;

✔️
export const foo, bar, kip;

4. Declarar e atribuir variáveis ​​das propriedades do objeto.

❌ 
const a = foo.x, b = foo.y;

✔️
const { ['x']: a, ['y']: b } = foo;

5. Declarar e atribuir variáveis ​​de índices de array.

❌ 
let a = foo[0], b = foo[1];

✔️
let [a, b] = foo;

6. Use operadores lógicos para condicionais simples.

❌ 
if (foo) {
  doSomething();
}

✔️
foo && doSomething();

Resumindo as regras de clean code

Simplifique e explique conceitos

Tente codificar o mais simples possível, desdobre seus conceitos, nomeie as coisas corretamente e explique com comentários onde o próprio código não pode (na maioria das vezes, pode e deve!).

Use nomes descritivos

Por mais fácil que pareça, tentar encontrar nomes que indiquem o verdadeiro significado de algo pode ser muito desafiador. Você pode tentar isso dizendo a alguém um nome e deixá-los assumir o que eles pensam que é. Não tenha medo de nomes longos de variáveis ​​ou funções. Como dito anteriormente, a máquina não se importa e não usará seu nome de qualquer maneira.

Desconstruir problemas em menores

Algo que faz mágica na legibilidade do seu código é desconstruir o problema e resolver apenas um simples com cada função. Tente refatorar seu código e resolver um único problema por vez, mas com o máximo de precisão e legibilidade.

Experimente  a depuração do pato de borracha

Você já tentou explicar seu código para um pato burro? Não? Tente! Ao fazer isso, você pode não apenas encontrar bugs, mas também encontrar lacunas na compreensão do código atual por você ou por outra pessoa.

Saiba mais sobre o Código Limpo

Muitos princípios e ideias de código limpo se baseiam no princípio KISS. Ao aplicar esses conceitos, você pode achar mais fácil identificar a solução mais simples.

Conclusão

Utilizar Clean Code no desenvolvimento de software é importante para que o código seja compreendido com mais facilidade por outras pessoas desenvolvedoras e seja possível realizar manutenções mais rápidas e eficientes. Para isso, basta adotar as práticas relacionadas acima e obter os benefícios de sua utilização.Gostou do nosso conteúdo sobre o que é e por que usar código limpo? Veja também nosso guia inicial sobre TDD e saiba como funciona o desenvolvimento orientado a testes!

0 Shares:
Deixe um comentário
Você também pode gostar