terça-feira, 29 de setembro de 2009

Como refatorar um sistema para melhor aproveitar recursos de POO – Parte 1

Atualmente a programação orientada a objetos (POO) é o paradigma predominante no desenvolvimento de sistemas. Porém, apesar de todos utilizarem e saberem como funciona, nem sempre o código desenvolvido realmente utiliza o potencial da POO.

É bastante comum encontrarmos sistemas desenvolvidos com orientação a objetos mas que não passam de programas estruturados utilizando classes.

Tentarei mostrar aqui um exemplo simples de como refatorar um código utilizando alguns princípios da orientação a objetos.

O código original

O exemplo que utilizarei aqui é bastante simples, não quero me ater no programa em si, mas nos conceitos utilizados. Então, não leve muito em consideração o programa, veja o conceito para poder utilizá-lo em qualquer outro caso.

Segue abaixo o código original do programa. Como você pode notar, ele simplesmente lê várias notas, calcula a média e a exibe.

class Program
{
  static void Main(string[] args)
  {
    ProcessaMedia processaMedia = new ProcessaMedia();
    processaMedia.Executa();

    Console.ReadKey();
  }
}


public class ProcessaMedia
{
  public void Executa()
  {
    List<decimal> notas = new List<decimal>();
    bool continuaLeitura = true;
    while (continuaLeitura)
    {
      Console.Write("Nota(Digite 'S' para sair):");
      string valorDigitado = Console.ReadLine();
      if (valorDigitado.Equals("S", StringComparison.CurrentCultureIgnoreCase))
      {
        continuaLeitura = false;
      }
      else
      {
        decimal nota = decimal.Parse(valorDigitado);
        notas.Add(nota);
      }
    }

    decimal totalNotas = 0;
    int qtdNotas = notas.Count;
    foreach (decimal nota in notas)
    {
      totalNotas += nota;
    }

    decimal media = 0;
    if (qtdNotas > 0)
    {
        media = totalNotas/qtdNotas;
    }

    Console.WriteLine("Média: "+ media);
  }
}

Quais os problemas nesse código?

Quais os problemas nesse código e o que podemos fazer para melhorá-lo? Vejamos:
  1. A classe ProcessaMedia tem três responsabilidades. Ela é responsável por lêr a nota, calcular a média e exibir o resultado. O ideal é que uma classe tenha apenas uma responsabilidade;
  2. Ela está diretamente acoplada ao Console. Isso pode gerar alguns problemas, por exemplo, como você faria um teste unitário dessa função? Simplesmente não é possível.
  3. Ela não é extensível. Se, por exemplo, surgir a necessidade de dar a opção de ler as notas de um arquivo, como você faria? No código seriam feitos uma série de condições IF?
Para resolver esses problemas vamos adotar algumas medidas:
  1. Separar cada responsabilidade em uma classe;
  2. A classe CalculaMedia deverá depender de uma abstração, e não do Console;
  3. Com a implementação dos itens 1 e 2 automaticamente ganhamos a extensibilidade, nenhuma medida adicional será necessária.

Mãos na massa

No próximo post mostrarei os passos para refatorar esse código.

Apesar deste ser um exemplo simples, podemos aplicar os mesmos conceitos em muitas outras situações. Pense um pouco e você logo vai lembrar de situações semelhantes a esse exemplo.