quinta-feira, 2 de outubro de 2014

Obtenção de Requisitos

A primeira etapa para escrever um software é a garantir que ele faça o que foi pedido, o que não é uma tarefa fácil. Como garantir que o cliente está ciente de tudo que ele precisa? Como saber se o programador entendeu tudo aquilo que o cliente solicitou? Para isso existem os bons requisitos, que garantem que aquilo que será entregue aquilo que foi pedido.
A primeira coisa a fazer é entender aquilo que o cliente quer e a melhor maneira disso acontecer é deixá-lo falar e prestar atenção no que o sistema precisa fazer.
A segunda é criar uma lista de requisitos, ou seja, transformar as palavras do cliente em um conjunto básico de requisitos.

Lista de requisitos: 
Além da lista de requisitos, é necessário entender como o software será utilizado, pois nem sempre o cliente saberá o que ele quer. Portanto fazer perguntas para o cliente é fundamental para que o produto seja entregue fazendo o que foi solicitado.

Planejar o erro:
Nem sempre o usuário vai utilizar o programa da maneira como ele foi planejado para ser usado, portanto é importante tentar prever os erros que podem acontecer e acrescentar soluções para eles.

Casos de uso:
Uma boa ajuda na hora de obter os requisitos é escrever uma lista de tarefas que o sistema tem que cumprir, assim fica mais fácil visualizar as falhas que o sistema pode ter e adiantar as soluções. Um caso de uso é uma descrição do que o sistema deve fazer para atingir seu objetivo, cada caso de uso oferece mais de uma situação que conduz como o sistema deve interagir. Um bom caso de uso é dividido em três partes:
  1. Valor evidente: Todo caso de uso deve ter uma valor evidente para o sistema, se ele não atingir o objetivo não será útil.
  2. Início e fim: Todo caso de uso deve ter algo que o inicie e o termine.
  3. Iniciador externo: Casos de uso são disparados por iniciadores externos, ou seja, qualquer coisa fora do sistema.
Verificar os requisistos para os casos de uso:
Após obter um conjunto ininical de requisitos e casos de uso, é bom verificar se todos os requisistos abordam tudo que o sistema deve fazer.

Código e teste:
Após o levantamento dos casos de uso, dos requisistos é hora de programar, testar o código e entregar para o cliente pra que ele avalie aquilo que foi feito.

Referência:
MCLAUGHLIN, B; POLLICE, G.; WEST, D. Use a cabeça! Análise e Projeto Orientado a Objeto. Alta books, 2007.

quinta-feira, 25 de setembro de 2014

Refatoração de Software

Refatoração de Software

Refatoração nada mais é do que alterar um sistema de software de modo que essa alteração não comprometa o comportamento externo do código, sendo assim é uma maneira disciplinada de aperfeiçoar o código onde as chances de falhas são minimizadas. Então, quando você refatora um código você está melhorando o projeto deste após o mesmo ter sido escrito. O propósito da refatoração é tornar o software legível a ponto que qualquer pessoa entende-lo, apenas estas mudanças são consideradas refatorações.

Kent Beck utiliza uma metáfora, denominada de os dois chapéus, onde diz que quando você desenvolve um sistema você utiliza basicamente duas atividades distintas: adicionar funções e refatorá-las. Neste desenvolvimento você geralmente se descobre trocando de chapéu, onde você começa adicionando uma nova função e após troca de chapéu e refatora esta funcionalidade.

Porque você deve refatorar?

- Melhora o projeto do software: Um sistema não refatorado normalmente necessita de mais códigos para as mesmas funções, sendo esses replicados diversas vezes.

-Torna o software legível: Ao desenvolver um sistema geralmente após um tempo outros programadores fazem alterações nesse código, então faz muita diferença o programador usar uma semana para entender o código e fazer a modificação ao invés de utilizar horas se o código fosse legível.

-Ajuda a encontrar falhas: Ao refatorar um código, você clareia certas suposições que havia feito até chegar ao ponto de não conseguir evitar de encontrar falhas.

-Ajuda a desenvolver mais rápido: Sem um bom projeto você progride rapidamente durante um certo tempo, porém ao longo de um projeto mau estruturado, você acaba perdendo mais tempo procurando e consertando falhas do que adicionando novas funcionalidades.

Quando você deve refatorar?

-Acrescentando novas funções: Um motivo que conduz a refatoração é quando você tem dificuldade em adicionar novas funcionalidades. Quando você precisa pensar muito tempo para saber o que o código faz é hora de refatorá-lo.

-Precisa consertar falhas: Quando você é notificado de uma falha é o sinal de que o código precisa ser refatorado, já que esse não estava claro o suficiente para detectar a falha.

-Enquanto revisa o código: As revisões de código regulares facilitam o entendimento de outros desenvolvedores em aspectos de um sistema grande e também a transmissão do conhecimento dos mais para os menos experientes.

Porque a refatoração funciona?

Programas que são difíceis de ler, que tem lógica duplicada, que para incluir novas funcionalidades requer modificação no código existente e com lógicas condicionais complexas, são difíceis de modificar.

Quando você não deve refatorar?

Há vezes em que o código está tão confuso que você até poderia refatorá-lo, porém é mais fácil reescrevê-lo. Outras vezes você se encontra muito perto do prazo final da entrega do software, então acaba adiando a refatoração.

Exemplo de refatoração:

Código não refatorado.:

public class StringUtil{
 private StringUtil(){}
 public static String us(String a){
  if(a==null){
   throw new NullPointerException("O campo não pode ser nulo");
  }
  char[] c = a.toCharArray();
  for (int i=0; i < a.length; i++){
   if((a[i] >= 97 && a[i] <= 122) || a[i]==231){
     a[i] -= 32;
   }
  }
 }
}

Código refatorado.:


public class StringUtil{


  private static final String EXCEPTION_MESSAGE = "O campo não pode ser nulo";
  private static final int DIF_MIN_MAIUS = 32;

  private StringUtil(){}

 /**
  * Converte uma String Minuscula para Maiuscula
  *
  *
  * @param str - A String em minusculo
  * @return Retorna uma nova String em maiusculo
  * @throw Lanca uma excecao quando o campo eh nulo
 */

 public static String upperString(String str){

  if(str == null) throw new NullPointerException(EXCEPTION_MESSAGE);

  char[] chars = str.toCharArray();

  for(int i = 0;i < chars.length;i++) chars[i] = if((chars[i] >= 97 && chars[i] <= 122) || chars[i]==231) chars[i] -= DIF_MIN_MAIUS;   

  return new String(chars);
}

Você pode notar que agora o código contém as seguintes alterações:

- O código está comentado, o que possibilita saber qual a função do método.

- O código possui duas variáveis contantes (EXCEPTION_MESSAGE,DIF_MIN_MAIUS) para não utilizar números ou símbolos mágicos.

- O código se torna legível, pois ao colocar o nome do método de 'upperString' ao invés de 'us', o nome da variável passada como parâmetro 'str' ao invés de 'a' e a variável do array 'chars' ao invés d 'c' possibilitam saber direto o que o método faz.

- O código evita '{}' desnecessárias.

Referência: Refatoração, de Martin Fowler.

Nestas Eleições, escolha em sobrepor o toString!


Bom, garanto que você esta se questionando o porquê deste titulo bizarro. Tentarei esclarecer esta questão...

Ao contrario de muitos candidatos, o toString fará e mostrará o que você quer,(claro desde que seja programado para isto), mas se você não o sobrepor, ele só fará e mostrará o que já lhe vem programado a fazer, mostrará só um monte de coisas que você não entederá, códigos e símbolos muito estranhos, que vem da classe object.

Agora tentarei descrever de uma forma um pouco mais técnica o porquê se deve sempre sobrepor o toString.

Embora java.lang.Object forneça uma implementação do método toString, geralmente a string que ele retorna não é a que o usúario de sua classe vai querer ver. Ela é composta pelo nome da classe seguindo de uma "arroba(@)" e da representação do código de hash em hexadecimal sem sinal, por exemplo, "PhoneNumber@163b91". Ela deve ser uma representação concisa, mas não é tão facil de entender e ler se for comparado a "(53)3232-3232".

Fornecer uma boa implementação de toString tornará sua classe mais agradável de usar, o método toString é chamado automaticamente quando um objeto é passado para println, printf, para o operador de concatenação de strings ou para assert, ou exibido por um depurador. Para ser considerado prático, o método toString deve retornar todas as informações interessantes contidas no objeto, quando implementa-lo deve-se especificar o formato do valor de retorno na documentação.

A vantagem de especificar o formato é que ele serve como uma representação do objeto que é padrão, clara e legível por humanos.

A desvantagem de especificar o formato do valor de retorno, é que uma vez especificado você ficará preso a ele indefinidamente, supondo que sua classe seja muito usada. Se você alterar a representação em uma versão futura, invalidára seus códigos e dados e eles reclamarão.

Ao não especificar um formato, você preservará a flexiblidade de inclusão de informações ou aperfeiçoamento do formato em uma versão subsequente.

Decidindo ou não especificar o formato, não se esqueça em documentar claramente suas intenções.


Exemplo sem sobrepor o toString

public class Cliente {
    private String nome;
    public Cliente(String nome) {
        this.nome = nome;
    } 

     //@Override
     //public String toString() {   
     //return "O Melhor Jogador do TADS eh = " + nome;   
     //}

    public static void main(String[] args) {
        Cliente cliente = new Cliente("Falcãozinho Gaucho!");
        System.out.println(cliente);
    }
}

//Resposta com toString de Object (Cliente@1509443)


Exemplo sobrepondo o toString

public class Cliente {
    private String nome;
    public Cliente(String nome) {
        this.nome = nome;
    }     
     @Override
     public String toString() {   
     return "O Melhor Jogador do TADS eh = " + nome;   
     }
    public static void main(String[] args) {
        Cliente cliente = new Cliente("Falcãozinho Gaucho!");
        System.out.println(cliente);
    }
}


//Resposta com toString de sobrescrito (O Melhor Jogador do TADS eh = Falcãozinho Gaucho!)


Referência: Java Efetivo, de Joshua Bloch