terça-feira, 2 de dezembro de 2014

Força Senha

Componente para medir a força da senha

Este post irá exibir como implementar um medidor de força de senha utilizando o padrão de projeto Template Method, tendo como regra para o cálculo o valor do desvio padrão entre os caracteres de acordo com a tabela ASCII.

Bom, para começar vamos desenvolver uma classe abstrata Senha, que será nossa super classe

/**
 * Classe abstrata para medir a forca da senha
 */
public abstract class Senha {
 
 private Forca forcaSenha;
 private Pattern patter = this.getPatter();
 private Matcher matcher;
 private int frequencia;
 private int tamanhoInicial;
 private int tamanhoFinal;
 private Double desvioPadrao;

 public int getTamanhoInicial() {
  return tamanhoInicial;
 }

 public void setTamanhoInicial(int tamanhoInicial) {
  this.tamanhoInicial = tamanhoInicial;
 }

 public int getTamanhoFinal() {
  return tamanhoFinal;
 }

 public void setTamanhoFinal(int tamanhoFinal) {
  this.tamanhoFinal = tamanhoFinal;
 }
        
        
      /**
       * Metodo para testar a forca da senha digitada
       * 
       * @param senha - recebe uma string senha
       * @return forcaSenha - retorna uma constante do objeto Forca
       * @throws tamanho - lanca uma excecao se o tamanho da senha for menor que o valor minimo
       * ou maior que o valor maxio
       * @throws caracter - lanca uma excecao se algum caracter digitado for diferente da regra
       */
 protected Forca testaSenha(String senha) {

  matcher = patter.matcher(senha);
                
                frequencia = 0;
  while (matcher.find()) {

   frequencia++;

  }

  if (senha.length() > tamanhoFinal || senha.length() < tamanhoInicial) {
   
   throw new RuntimeException(this.getExceptionTamanho(tamanhoInicial, tamanhoFinal));

  } else if (senha.length() != frequencia) {

   throw new RuntimeException (this.getExceptionNome() + this.getNome());

  } else {

   desvioPadrao = this.getDesvioPadrao(frequencia, senha);
  }

  if (desvioPadrao <= this.getDesvioPadraoMuitoFraco()) {
   forcaSenha = Forca.MuitoFraca;
  } else if (desvioPadrao > this.getDesvioPadraoMuitoFraco()
    && desvioPadrao <= this.getDesvioPadraoFraco()) {
   forcaSenha = Forca.Fraca;
  } else if (desvioPadrao > this.getDesvioPadraoFraco()
    && desvioPadrao <= this.getDesvioPadraoMedio()) {
   forcaSenha = Forca.Media;
  } else if (desvioPadrao > this.getDesvioPadraoMedio()
    && desvioPadrao <= this.getDesvioPadraoForte()) {
   forcaSenha = Forca.Forte;
  } else if (desvioPadrao > this.getDesvioPadraoForte()) {
   forcaSenha = Forca.MuitoForte;
  }

  return forcaSenha;
 }

Agora temos que importar o Matcher e o Pattern do java.util, que são expressões regulares utilizadas para procurar em uma string os caracteres de um determinado intervalo. Também vamos criar os métodos abstratos, os quais serão responsáveis por obter as informações do objeto que está em questão e a classe enum Forca, para obter as constantes de força da senha.


import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * Classe abstrata para medir a forca da senha
 */
public abstract class Senha {
 

       /**
        * Classe enum Forca com as constantes de forca da senha
        */
 public enum Forca {MuitoFraca, Fraca, Media, Forte,MuitoForte }
 

 
 private Forca forcaSenha;
 private Pattern patter = this.getPatter();
 private Matcher matcher;
 private int frequencia;
 private int tamanhoInicial;
 private int tamanhoFinal;
 private Double desvioPadrao;

 public int getTamanhoInicial() {
  return tamanhoInicial;
 }

 public void setTamanhoInicial(int tamanhoInicial) {
  this.tamanhoInicial = tamanhoInicial;
 }

 public int getTamanhoFinal() {
  return tamanhoFinal;
 }

 public void setTamanhoFinal(int tamanhoFinal) {
  this.tamanhoFinal = tamanhoFinal;
 }
        
        
      /**
       * Metodo para testar a forca da senha digitada
       * 
       * @param senha - recebe uma string senha
       * @return forcaSenha - retorna uma constante do objeto Forca
       * @throws tamanho - lanca uma excecao se o tamanho da senha for menor que o valor minimo
       * ou maior que o valor maxio
       * @throws caracter - lanca uma excecao se algum caracter digitado for diferente da regra
       */
 protected Forca testaSenha(String senha) {

  matcher = patter.matcher(senha);
                
                frequencia = 0;
  while (matcher.find()) {

   frequencia++;

  }

  if (senha.length() > tamanhoFinal || senha.length() < tamanhoInicial) {
   
   throw new RuntimeException(this.getExceptionTamanho(tamanhoInicial, tamanhoFinal));

  } else if (senha.length() != frequencia) {

   throw new RuntimeException (this.getExceptionNome() + this.getNome());

  } else {

   desvioPadrao = this.getDesvioPadrao(frequencia, senha);
  }

  if (desvioPadrao <= this.getDesvioPadraoMuitoFraco()) {
   forcaSenha = Forca.MuitoFraca;
  } else if (desvioPadrao > this.getDesvioPadraoMuitoFraco()
    && desvioPadrao <= this.getDesvioPadraoFraco()) {
   forcaSenha = Forca.Fraca;
  } else if (desvioPadrao > this.getDesvioPadraoFraco()
    && desvioPadrao <= this.getDesvioPadraoMedio()) {
   forcaSenha = Forca.Media;
  } else if (desvioPadrao > this.getDesvioPadraoMedio()
    && desvioPadrao <= this.getDesvioPadraoForte()) {
   forcaSenha = Forca.Forte;
  } else if (desvioPadrao > this.getDesvioPadraoForte()) {
   forcaSenha = Forca.MuitoForte;
  }

  return forcaSenha;
 }
        

       /**
        * Metodo abstrato para obter a string, da subclasse, a ser lancada no exception
        * para quando o usuario digita um caracter diferente do definido pela subclasse
        * 
        * @return retorna uma string com a exception
        */

 protected abstract String getExceptionNome();

      /**
        * Metodo abstrato para obter a string, da subclasse, a ser lancada no exception
        * para quando a senha digitada excede o tamanho definido como maximo e minimo
        * 
        * @param tamanhoInicialException - inteiro com o tamanho minimo de caracteres que a 
        * senha deve conter
        * @param tamanhoFinalException - inteiro com o tamanho maximo de caracteres que a senha
        * deve conter
        * @return retorna uma string com a exception
        */

 protected abstract String getExceptionTamanho(int tamanhoInicialException, int tamanhoFinalException);
 
        
      /**
        * Metodo abstrato para obter o calculo do desvio padrao, da subclasse, para verificar 
        * a forca da senha de acordo com os valores definidos pelo programador
        * 
        * @param frequenciDesvioPadrao - inteiro com a quantidade de caracteres digitados
        * na senha
        * @param senha - string com a senha digitada pelo usuario
        * @return retorna um valor double com o calculo do desvio padrao
        */
 protected abstract double getDesvioPadrao(int frequenciaDesvioPadrao, String senha);

        
      /**
        * Metodo abstrato para obter a string, da subclasse, a ser lancada no exception
        * contendo o nome dos caracteres que devem obrigatoriamento compor a senha
        * 
        * @return retorna uma string com o nome dos caracteres
        */
 protected abstract String getNome();
        
        
      /**
        * Metodo abstrato para obter o Pattern, da subclasse, com o intervalo de caracteres
        * que devem compor a senha
        * 
        * @return retorna o pattern com o intervalo de caracteres
        */
 protected abstract Pattern getPatter();
        
        
      /**
        * Metodo abstrato para obter, da subclasse, o valor definido pelo programador 
        * para quando a senha eh muito fraca
        * 
        * @return retorna um double com o valor definido como muito fraco
        */
 protected abstract double getDesvioPadraoMuitoFraco();
        
        
      /**
        * Metodo abstrato para obter, da subclasse, o valor definido pelo programador 
        * para quando a senha eh fraca
        * 
        * @return retorna um double com o valor definido como fraco
        */
 protected abstract double getDesvioPadraoFraco();
        
        
      /**
        * Metodo abstrato para obter, da subclasse, o valor definido pelo programador 
        * para quando a senha eh muito media
        * 
        * @return retorna um double com o valor definido como medio
        */
 protected abstract double getDesvioPadraoMedio();
        
        
      /**
        * Metodo abstrato para obter, da subclasse, o valor definido pelo programador 
        * para quando a senha eh forte
        * 
        * @return retorna um double com o valor definido como muito forte
        */
 protected abstract double getDesvioPadraoForte();
        
        
      /**
        * Metodo abstrato para setar, na subclasse, o valor definido pelo programador 
        * para quando a senha eh muito fraca
        * 
        * @param getDesvioPadraoMuitoFraco - double com o valor que o programador
        * ira definir para o desvio padrao muito fraco
        * @return retorna um double com o valor definido como muito fraco
        */
 protected abstract void setDesvioPadraoMuitoFraco(double getDesvioPadraoMuitoFraco);
        
        
      /**
        * Metodo abstrato para setar, na subclasse, o valor definido pelo programador 
        * para quando a senha eh fraca
        * 
        * @param getDesvioPadraoFraco - double com o valor que o programador
        * ira definir para o desvio padrao fraco
        * @return retorna um double com o valor definido como fraco
        */
 protected abstract void setDesvioPadraoFraco(double getDesvioPadraoFraco);
        
        
      /**
        * Metodo abstrato para setar, na subclasse, o valor definido pelo programador 
        * para quando a senha eh media
        * 
        * @param getDesvioPadraoMedio - double com o valor que o programador
        * ira definir para o desvio padrao medio
        * @return retorna um double com o valor definido como medio
        */
 protected abstract void setDesvioPadraoMedio(double getDesvioPadraoMedio);
        
        
      /**
        * Metodo abstrato para setar, na subclasse, o valor definido pelo programador 
        * para quando a senha eh forte
        * 
        * @param getDesvioPadraoForte - double com o valor que o programador
        * ira definir para o desvio padrao forte
        * @return retorna um double com o valor definido como forte
        */
 protected abstract void setDesvioPadraoForte(double getDesvioPadraoForte);
 

}

Com a nossa super classe criada, agora vamos criar duas subclasses, a senhaCaracteres que será para senha com apenas caracteres (maiúsculo e minúsculo) e a senhaNumeros que será para senha com apenas números.


package componente;

import java.util.regex.Pattern;

public class SenhaCaracter extends Senha {

 private double desvioPadrao;
 private double desvioPadraoForte;
 private double desvioPadraoMedio;
 private double desvioPadraoFraco;
 private double desvioPadraoMuitoFraco;

 @Override
 protected Pattern getPatter() {
  return Pattern.compile(".??[a-zA-Z]");
 }

 @Override
 protected String getNome() {
  return "caracteres minusculos e maiusculos";
 }

 @Override
 protected double getDesvioPadrao(int frequencia, String senha) {

  Estatistica estatistica = new Estatistica();
  
  char[] chars = senha.toCharArray();
  
  estatistica.setArray(chars);  
  desvioPadrao = estatistica.getDesvioPadrao();
  System.out.println(desvioPadrao);
  
  senha = new String(chars);
  
  return desvioPadrao;
 }

 @Override
 protected double getDesvioPadraoMuitoFraco() {
  return desvioPadraoMuitoFraco;
 }

 protected void setDesvioPadraoMuitoFraco(double desvioPadraoMuitoFraco) {
  this.desvioPadraoMuitoFraco = desvioPadraoMuitoFraco;
 }

 @Override
 protected double getDesvioPadraoFraco() {
  return desvioPadraoFraco;
 }

 protected void setDesvioPadraoFraco(double desvioPadraoFraco) {
  this.desvioPadraoFraco = desvioPadraoFraco;
 }

 @Override
 protected double getDesvioPadraoMedio() {
  return desvioPadraoMedio;
 }

 protected void setDesvioPadraoMedio(double desvioPadraoMedio) {
  this.desvioPadraoMedio = desvioPadraoMedio; 
 }

 @Override
 protected double getDesvioPadraoForte() {
  return desvioPadraoForte;
 }

 protected void setDesvioPadraoForte(double desvioPadraoForte) {
  this.desvioPadraoForte = desvioPadraoForte;  
 }

 @Override
 protected String getExceptionNome() {
  return ("A senha deve conter apenas caracteres: ");
 }

 @Override
 protected String getExceptionTamanho(int tamanhoInicial, int tamanhoFinal) {
  return ("A senha deve conter no minimo " + tamanhoInicial
    + " e no maximo " + tamanhoFinal + " caracteres!");
 }
 

 

 
}


package componente;

import java.util.regex.Pattern;


public class SenhaNumeros extends Senha{

 private double desvioPadrao;
 private double desvioPadraoForte;
 private double desvioPadraoMedio;
 private double desvioPadraoFraco;
 private double desvioPadraoMuitoFraco;

 @Override
 protected Pattern getPatter() {
  return Pattern.compile(".??[0-9]");
 }

 @Override
 protected String getNome() {
  return "numeros";
 }

 @Override
 protected double getDesvioPadrao(int frequencia, String senha) {
  
  Estatistica estatistica = new Estatistica();
  
  char[] chars = senha.toCharArray();
  
  estatistica.setArray(chars);  
  desvioPadrao = estatistica.getDesvioPadrao();
  System.out.println(desvioPadrao);
  
  senha = new String(chars);
  
  return desvioPadrao;
 }

 @Override
 protected double getDesvioPadraoMuitoFraco() {
  return desvioPadraoMuitoFraco;
 }

 protected void setDesvioPadraoMuitoFraco(double desvioPadraoMuitoFraco) {
  this.desvioPadraoMuitoFraco = desvioPadraoMuitoFraco;
  
 }

 @Override
 protected double getDesvioPadraoFraco() {
  return desvioPadraoFraco;
 }

 protected void setDesvioPadraoFraco(double desvioPadraoFraco) {
  this.desvioPadraoFraco = desvioPadraoFraco;
 }

 @Override
 protected double getDesvioPadraoMedio() {
  return desvioPadraoMedio;
 }

 protected void setDesvioPadraoMedio(double desvioPadraoMedio) {
  this.desvioPadraoMedio = desvioPadraoMedio; 
 }

 @Override
 protected double getDesvioPadraoForte() {
  return desvioPadraoForte;
 }

 protected void setDesvioPadraoForte(double desvioPadraoForte) {
  this.desvioPadraoForte = desvioPadraoForte;  
 }

 @Override
 protected String getExceptionNome() {
  return ("A senha deve conter apenas caracteres: ");
 }

 @Override
 protected String getExceptionTamanho(int tamanhoInicial, int tamanhoFinal) {
  return ("A senha deve conter no minimo " + tamanhoInicial
    + " e no maximo " + tamanhoFinal + " caracteres!");
 }

 
}


Por último, criaremos a nossa classe Estatistica que terá os métodos para calcular o desvio padrão e a classe Exception.


package componente;

import java.util.Arrays;

public class Estatistica {

 private char array[];

 public double getPearson() {

  return (getDesvioPadrao() / getMediaAritmetica()) * 100;

 }

 public double getMediaAritmetica() {

  double total = 0;

  for (int counter = 0; counter < array.length; counter++)

   total += array[counter];

  return total / array.length;

 }

 public double getSomaDosElementos() {

  double total = 0;

  for (int counter = 0; counter < array.length; counter++)

   total += array[counter];

  return total;

 }

 public double getSomaDosElementosAoQuadrado() {

  double total = 0;

  for (int counter = 0; counter < array.length; counter++)

   total += Math.pow(array[counter], 2);

  return total;

 }

 public double getMediaAritmetica(double array[]) {

  double total = 0;

  for (int counter = 0; counter < array.length; counter++)

   total += array[counter];

  return total / array.length;

 }

 public double getSomaDosElementos(double array[]) {

  double total = 0;

  for (int counter = 0; counter < array.length; counter++)

   total += array[counter];

  return total;

 }

 public void ordenar() {

  Arrays.sort(array);

 }

 public void imprimeArray() {

  System.out.print("\nElementos do Array: ");

  for (int count = 0; count < array.length; count++)

   System.out.print(array[count] + " ");

 }

 public double getVariancia() {

  double p1 = 1 / Double.valueOf(array.length - 1);

  double p2 = getSomaDosElementosAoQuadrado()

  - (Math.pow(getSomaDosElementos(), 2) / Double

  .valueOf(array.length));

  return p1 * p2;

 }

 public double getDesvioPadrao() {

  return Math.sqrt(getVariancia());

 }

 public double getMediana() {

  this.ordenar();

  int tipo = array.length % 2;

  if (tipo == 1) {

   return array[((array.length + 1) / 2) - 1];

  } else {

   int m = array.length / 2;

   return (array[m - 1] + array[m]) / 2;

  }

 }

 public void setArray(char[] array) {

  this.array = array;

 }

}


/**
  * Classe Exception extendendo a classe RuntimeException
*/
public class Exception extends RuntimeException {

 private static final long serialVersionUID = 1L;

 public Exception(String mensagem, Exception e) {
  super(mensagem, e);
 }

 public Exception(String mensagem) {
  super(mensagem);
 }
}

Pronto!! Agora só testar, instanciando e setando o tamanho inicial e final que a senha deve conter e o desvio padrão para a regra do medidor.


package Negocio;

public class Main {


 public static void main(String[] args) {
  
  
  
  String senha = "cA"; 
  
/*  
  
  Senha todos = new SenhaTodosCaracteres();
  Senha numeros = new SenhaNumeros();
  Senha minusculo = new SenhaCaracterMinusculo();
  Senha maiusculo = new SenhaCaracterMaiusculo();
  Senha caracter = new SenhaCaracter();
  Senha caracterEspecial = new SenhaCaracterEspecial();
  
  todos.setTamanhoInicial(2);
  todos.setTamanhoFinal(5);
  
  numeros.setTamanhoInicial(2);
  numeros.setTamanhoFinal(10);
  
  minusculo.setTamanhoInicial(10);
  minusculo.setTamanhoFinal(20);
  
  maiusculo.setTamanhoInicial(20);
  maiusculo.setTamanhoFinal(20);
  
  caracter.setTamanhoInicial(5);
  caracter.setTamanhoFinal(15);
  
  caracterEspecial.setTamanhoInicial(5);
  caracterEspecial.setTamanhoFinal(15);
  
  numeros.setDesvioPadraoMuitoFraco(1);
  numeros.setDesvioPadraoFraco(3);
  numeros.setDesvioPadraoMedio(5);
  numeros.setDesvioPadraoForte(8);
  
    
  System.out.println(numeros.testaSenha(senha));
  System.out.println(minusculo.testaSenha(senha));
  System.out.println(maiusculo.testaSenha(senha));
  System.out.println(caracter.testaSenha(senha));
  System.out.println(caracterEspecial.testaSenha(senha));
    
  
*/ 
  Senha numeros = new SenhaCaracter();
  
  
  numeros.setTamanhoInicial(2);
  numeros.setTamanhoFinal(10);
  numeros.setDesvioPadraoMuitoFraco(1);
  numeros.setDesvioPadraoFraco(3);
  numeros.setDesvioPadraoMedio(5);
  numeros.setDesvioPadraoForte(8);
  
  
  System.out.println(numeros.testaSenha(senha));
 }

}


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

Desenvolvimento e Otimização de um Projeto Baseados em Testes

Desenvolvimento e Otimização de um Projeto Baseados em Testes

O desenvolvimento baseado por testes deve seguir 3 regras simples:

  1. Não escrever o código de produção até que voce tenha escrito um teste de unidade que falhe.
  2. Não escrever mais um teste de unidade do que suficiente para falhar ou não compile.
  3. Não escrever mais que qualquer código de produção para passar no teste falho.

O objetivo é construir ciclos curtos, alternando as etapas de desenvolvimento. O conjunto de testes geram um impedimento para o desenvolvimento. Indica onde o programa apresenta falhas, nos permitindo criarmos funções para corrigir o programa ou para melhorarmos a sua eficiência.

Ao desenvolver um software baseado por testes ele deve ser desacoplado do seu ambiente para ser testado. Isso gera uma documentação importante para a empresa contratante e desenvolvedora.

Testes de Aceitação, além de necessários são fundamentais para que se verifique se os requisitos do cliente estão sendo cumpridos, mas são insuficientes como ferramentas de verificação do software.

Arquitetura do Projeto

Para desenvolver um projeto de software, é necessário ter uma equipe definida, com o modelo a seguir. Usando o modelo por testes pois como o programa de software a ser desenvolvido é de funções matemáticas, pode-se testa-lo desacoplado do seu ambiente e buscá-lo otimizá-lo.

A programação por pares pode ser uma excelente escolha, pois um programador desenvolve e o outro pode corrigir os erros e otimizar as funções do software.


                        +-------------+
                        | Matemática  |
                     1  | Aplicada    |
                        +-------------+             
         
     +-----------+      +-------------+    +-------------+
     | Algebra   |      | Geometria   |    | Estatística |
     +-----------+      +-------------+    +-------------+
  1A | Funções   |   1B |Trigonometria| 1C | Matemática  |
     | Calculos  |      |             |    | Financeira  |
     +-----------+      +-------------+    +-------------+
     
     
                        +-------------+
                        | Interface   |
                      2 | Usuário     |
                        +-------------+

Um exemplo de teste apresenta abaixo. Desenvolver funções matemáticas: O código mostra o código de números n primos, podendo ser implementados em linguagem Java, C ou C++.

Esse código quando o programador olha ele não sabe as funções de cada parâmetro e variável do código e tem que testa-lo, não sendo uma boa prática de programação.

 while(p>0)
 { 
  d=2;
  while((n%d)!=0)
  {
   d+=1;
  }
  if(d==n)
  { printf("n: %d ", n); 
   p--;
  }
  n++;
      
 }
 if (n==p)
 printf("n: %d ", n);

O código ao ser Testado mostra n números primos, em uma função desacoplada do projeto, mas pode ser refatorado e otimizado.

public int numeroPrimo() {
 for(primo=2; (numero>0);primo++)
 { for(quantidade=2;((p%quantidade)!=0);quantidade++)
  { if(d==p)
   { printf("primo: %quantidade ", primo); 
    numero--;
   }
  }
  if (quantidade == numero)
  printf("numero: %primo ", numero);
 }
 return numeroPrimo()
 }

O código refatorado apresenta uma maior otimização, o programador, ou equipe de desenvolvimento após testar o programa deve otimizar seu códigos usando refatoração ou outros métodos necessários conforme a plataforma utilizada ou a exigência da empresa, os nomes dos métodos e variáveis devem ser descritos na forma que qualquer outro programador, desenvolvedor veja o código fonte e entenda o programa. Sendo que implantado através de métodos permite a utilização através da chamada de métodos, sem repetir códigos e utilizá-lo em partes posteriores do projeto.

Obedecendo o novo paradigma que é a orientação orientada a objetos.

MARTIN. Micah, MARTIN Robert C.; Princípios, Padrões e Práticas Ágeis em C#. Editora Bookman,2011.

sábado, 7 de junho de 2014

Programação com shell script

Este artigo destina-se aos que já conhecem o básico de lógica de programação e que estejam interessados em aprender shell script.

O shell script é uma linguagem de programação utilizada, principalmente, para automatizar tarefas administrativas em sistemas operacionais Unix-like.

Existem vários interpretadores para os scripts em shell, o mais utilizado em distribuições GNU/Linux é o bash. Além dele, outras opções de interpretadores são o sh, csh e o ksh.

Nesse artigo, a minha ideia é trabalhar apenas com shell script interpretado pelo bash (durante o artigo vou chamar apenas de shell script).

Para iniciar, vou usar o famoso: "Olá mundo!" (nesse caso: "ola user"). Com um editor de texto, criei o arquivo "teste.sh" com o seguinte conteúdo:

 
#!/bin/bash
echo "ola $USER";
 

Esse script simplesmente cumprimenta o usuário. Nele utilizei o comando "echo", que imprime na saída padrão ( nesse caso o monitor ), e a variável de ambiente "$USER", que guarda o usuário logado.

A primeira linha desse script, "#!/bin/bash", indica o interpretador que deve executar os comandos que estão no arquivo.

Poderia ser outro interpretador shell (como por exemplo o #!/bin/sh), ou até mesmo não ser especificado nenhum interpretador. Nesse caso, o sistema vai utilizar o interpretador que estiver na variável "$SHELL" (interpretador padrão).

Para testar este script, é necessário que ele tenha permissão de execução no sistema, para isso, supondo que o arquivo foi salvo no diretório atual, vou rodar no terminal o comando abaixo:


chmod +x teste.sh
 

e depois para executar o script vou usar o comando:


./teste.sh
 

Para executar esse script, eu especifiquei o caminho até o arquivo "teste.sh" (o "./" representa o diretório atual), isso foi necessário porque quando é digitado um comando (ou um script shell) no terminal, se não especificar o caminho até o arquivo, o sistema busca por ele nos diretórios que aparecem na variável "$PATH".

Se não quiser especificar o caminho para executar seus scripts, você pode colocá-los em um dos diretórios que aparecem na "$PATH" ou configurar o diretório onde eles estão nessa variável. ( exemplo de como fazer isso aqui ).

Assim como outras linguagens de programação, shell script possui estruturas de controle do tipo: if, case, while e for. A sintaxe dessas estruturas é a seguinte:

If
#!/bin/bash
nome="paulo";
if [ $nome != "paulo" ] ; then

  echo "vc não é o paulo!";
fi
 
Case
#!/bin/bash
i=1;
case $i in 

 1)
    echo "é 1";
 ;;
 
 2)
    echo "é 2";
 ;;

 *) 
    echo "não eh 1 nem 2";
 ;;
esac

While
#!/bin/bash
i=1;
while [ $i -le 10 ] ; do 

  echo $i;
  i=$(( $i + 1 ));
done

For
#!/bin/bash
for i in $( seq 10 ) ; do 

  echo $i;
done 

Shell script tem algumas características que não são comuns em outras linguagens. Por exemplo, os espaçamentos que normalmente são permitidos em outras linguagens, podem causar erros em shell script.

Quando é criada uma variável, não deve haver espaços entre o nome da variável, o sinal de igual e o valor atribuído:

#!/bin/bash
i=1;   # ok, vai funcionar
j = 2; # não vai funcionar

Observações:

1 - Perceba que ao criar a variável, não usei o "$". Este símbolo é usado apenas para recuperar o valor da variável.
2 - O que está após o símbolo "#" é considerado comentário e não é executado.
3 - É possível utilizar o comando read para ler da entrada padrão (geralmente o teclado) e criar variáveis especificadas pelo usuário em tempo de execução.

Outra particularidade do shell script é que operações matemáticas não são realizadas diretamente, é necessário utilizar algum comando para realizar o calculo, por exemplo:

#!/bin/bash
i=1;   
j=$(( $i + 1 ));    # ok
j=$( expr $i + 2 ); # ok
j=$i + 1;           # não vai funcionar como esperado

Nesse exemplo foi utilizado o comando "$( expr $i + 2 )", basicamente trata-se de um sub-shell, o que estiver dentro de "$( )" é executado à parte e o resultado( o que seria impresso na saída padrão) é atribuído para variável. Também seria possível utilizar o comando "` expr $i + 2 `" para ter o mesmo resultado.

Outro detalhe importante que foi visto nos exemplos anteriores são os operadores usados nas estruturas de controle dentro do comando "[ ]", que na verdade é um açúcar sintático para o comando "test", veja alguns exemplos de operadores:

Para Numeros
-lt É menor que (LessThan)
-gt É maior que (GreaterThan)
-le É menor ou igual (LessEqual)
-ge É maior ou igual (GreaterEqual)
-eq É igual (EQual)
-ne É diferente (NotEqual)
Para Strings
= É igual
!= É diferente
Testes logicos
! Negação
-a and ( geralmente && )
-o or( geralmente || )
Shell script também tem funções, elas seguem a seguinte sintaxe:
#!/bin/bash

function somar() {

  echo $(( $1 + $2 ));  
}

somar 1 2

As funções em shell script são chamadas da mesma forma que um comando, elas recebem parâmetros sem a necessidade de especificá-los na declaração da função. Os parâmetros são recuperados através de variáveis especais, como a "$1" e a "$2" utilizadas no exemplo acima. Veja outras dessas variáveis especiais:

$0 O nome da função
$1 O primeiro parâmetro
$2 O segundo parâmetro
$numero O parâmetro da posição numero
$# A quantidade de parâmetros
$* Todos os parâmetros

Com essas variáveis, posso melhorar o script anterior de forma a não limitar o número de parâmetros a serem somados:

#!/bin/bash

function somar() {

  resultado=0;
  for i in $* ; do

    resultado=$(( $resultado + $i ));
  done

  echo $resultado;
}

somar 1 2 3 4 5

Uma observação importante sobre as funções em shell script é que apesar de possuir a instrução "return", essa instrução é utilizada de uma forma diferente.

Em shell script funções são como os comandos, os comandos tem um código de retorno que indica se o comando foi executado corretamente ou não( isso é testado com a variável "$?" ), portanto as funções também tem esse código de retorno, que é retornado com a instrução "return" :)

Para conseguir armazenar o retorno de função em uma variável costumo usar o sub-shell:

#!/bin/bash

function somar() {

  resultado=0;
  for i in $* ; do

    resultado=$(( $resultado + $i ));
  done

  echo $resultado;
}

#usando o sub-shell é possível 
#armazenar o retorno da função 
total=$( somar 1 2 3 4 5 );

A grande utilidade das funções, é que elas organizam e garantem a reutilização de código. Uma possibilidade interessante que o shell script permite, é incluir um arquivo com funções dentro de outro (parecido com o require e include do PHP).

Por exemplo, posso criar varias funções que estejam relacionadas e colocá-las no mesmo arquivo e depois, quando for necessário, uso a instrução "source" para incluir essas funções em outro arquivo.

Supondo que a função "somar" está no arquivo "matemática.sh", posso usá-la em outro arquivo da seguinte maneira:

#!/bin/bash

source "matematica.sh";

total=$( somar 1 2 3 4 5 );

Além de funções e estruturas de controle, Shell Script também possui arrays. Os arrays têm a seguinte sintaxe:

#!/bin/bash

dias=( "domingo" "segunda" "terca" "quarta" "quinta" "sexta" "sabado" );

echo ${dias[1]}; # imprime segunda

nomes[0]="paulo";
nomes[1]="ana";
nomes[2]="carlos";

echo ${nomes[1]}; 

# para retornar a quantidade de itens
echo ${#nomes[@]}  # imprime 3

Perceba que é possível criar arrays de duas formas diferentes, e que, para retornar o valor de uma posição do array, a sintaxe é diferente de uma variável comum.

Além disso, na última linha é feita a leitura da quantidade de itens (posições ocupadas) do array.

Vou terminar deixando um script com o que foi visto nesse artigo, se quiser saber mais sobre Shell Script veja os links mais abaixo. Até mais... :)

#!/bin/bash
function organiza() {

 array=($@);
 i=0;
 while [ $i -lt ${#array[@]} ]; do

    j=$(($i + 1));
    while [ $j -lt ${#array[@]} ]; do
   
       if [ ${array[$j]} -lt ${array[$i]} ]; then 
   
          aux=${array[$j]};
          array[$j]=${array[$i]};
          array[$i]=$aux;   
       fi 
       j=$(($j + 1));
    done
    i=$(($i + 1));
 done

 for n in ${array[@]}; do

    echo "$n";
 done
}

organiza 2 9 4 5 6 1 

Mais Informações:
http://thobias.org/doc/shell_bd.html
http://thobias.org/doc/cgi_shell.html
Paulo Dias | blog: http://blog.prmsistemas.com/

JavaScript - Programação orientada a objetos

Introdução

A linguagem JavaScript, geralmente, é usada de forma procedural, alguns provavelmente acreditam que essa seja a única forma (paradigma) possível, mas também, podemos programar de forma orientada a objetos (OO) em JavaScript.

A programação OO em JavaScript é um pouco diferente do que estamos acostumados. Diferente de Java, PHP, Ruby e outras, JavaScript não têm uma definição para uma classe, mas possui um recurso chamado Prototype (não confundir com o Framework), que podemos utilizar para programar de forma OO.

Vamos a um exemplo simples. Vou criar uma "classe" Pessoa. Pode parecer estranho, mas acredite, podemos escrever uma "classe" desse jeito:

function Pessoa( nome, idade ) {
 
    this.nome = nome;
    this.idade = idade;
 
    this.digaOi = function() {
 
        alert( "oi" );
    }
}

Sim, você deve ter percebido que isso é uma função, mas podemos criar uma instância dela através do operador new (como nas outras linguagens) e simular uma classe, assim:

var eu = new Pessoa( "paulo", "28" ); 

E agora, que temos uma instância de "Pessoa", podemos ter acesso aos atributos e métodos de um jeito bem intuitivo:

alert( eu.nome );
eu.digaOi();

Com isso já temos uma "classe" e um objeto.

Encapsulamento

O encapsulamento é um do principais pontos da programação orientada a objetos (OO).Basicamente, encapsular significa separar um programa em partes, protegendo o acesso direto aos atributos de uma classe.

Para isso, geralmente, são utilizados modificadores de acesso e métodos específicos para a manipulação destes atributos (set / get).

Em JavaScript não temos estes modificadores de acesso, mas podemos criar encapsulamento assim:

function Objeto() {
 
    var atributoPrivado;
 
    this.setAtributoPrivado = setAtributoPrivado;
    this.getAtributoPrivado = getAtributoPrivado;
 
    function setAtributoPrivado( valor ) {
 
        atributoPrivado = valor;
    }
 
    function getAtributoPrivado() {
 
        return atributoPrivado;
    }
}
 
var objeto = new Objeto();
 
objeto.setAtributoPrivado( "teste" );
 
alert( objeto.getAtributoPrivado() );

Neste exemplo, usei "var" e "this" para simular os modificadores de acesso privado e público.

Com isso já podemos considerar nosso código encapsulado.

Herança

Herança é a habilidade de uma classe reutilizar (herdar) as características (atributos) e comportamentos (métodos) de outra. A herança é geralmente caracterizada usando a relação "é um".

Exemplo: Cachorro é um Animal

Cachorro herda as características e comportamentos de um animal.

Vejamos um exemplo sobre como usar herança em JavaScript. Primeiro criamos duas "classes" (super e sub-classe):
function VeiculoTerrestre() {
 
    this.andar = function() {
 
        alert("andando");
    }
 
    this.parar = function() {
 
        alert("parando");
    }
}
 
function Carro() {
 
    this.ligarFarois = function() {
 
        alert("ligando os farois");
    }
}

Carro é um VeiculoTerrestre - Por isso é interessante herdar as características e comportamentos de VeiculoTerrestre em Carro.

JavaScript não implementa uma palavra chave (extends) para indicar que uma classe herda de outra, mas podemos utilizar herança através do prototype assim:

Carro.prototype = new VeiculoTerrestre;
 
var carro = new Carro();

carro.andar();
carro.parar();
carro.ligarFarois();

Neste exemplo, usamos o prototype para criar a herança entre as "classes" VeiculoTerreste e Carro.

O prototype é propriedade que nos permite adicionar características e comportamentos para as "classes", após sua definição, assim podemos reutilizar toda a definição de uma classe em outra.

Mais do prototype

Utilizamos o prototype para criar herança colocando as funcionalidades de "VeiculoTerrestre" em "Carro". O que pode ter passado desapercebido, é que através do prototype podemos criar novas características para as "classes" já definidas pelo JavaScript.

Como exemplo, vamos colocar um novo método na "classe" String do JavaScript.

Geralmente, usamos a função parseInt para converter String para inteiros, vamos deixar essa conversão mais OO (Orientada a Objetos), colocando um método parseInt em String, assim:

String.prototype.parseInt = function() {
 
    return parseInt( this );
}
 
alert( "10".parseInt() )
É um exemplo simples (talvez inútil), mas a ideia é essa...

Conclusão

Termino esse artigo por aqui. Espero que seja útil. Qualquer correção, dúvida ou sugestão, basta comentar. Paulo Dias | blog: http://blog.prmsistemas.com/

terça-feira, 6 de maio de 2014

Os maus cheiros do projeto

Os maus cheiros do projeto.

         Percebe-se que o software está com cheiro de podre ou de algo estragado, quando ocorre os seguintes sintomas:

         Entre os sete sintomas existentes a rigidez acontece quando se torna difícil alterar o projeto, mesmo de maneira simples, ou seja, o projeto se torna rígido quando tentamos mudar algo simples e temos que mudar quase todo o projeto.

        Outro sintoma é a fragilidade, ela ocorre quando o projeto fica fácil de estragar, o programa estraga em muitos lugares quando se faz uma única alteração e quanto mais se tenta consertar mais problemas aparecem.

         A imobilidade, também é um sintoma de mau cheiro, o projeto se trona difícil de ser reutilizado, ele contém partes que poderiam ser uteis em outros sistemas, mas é muito arriscado e trabalhoso separar estas partes do sistema operacional.

         A viscosidade é um mau cheiro que faz com que se torne difícil fazer a coisa certa, quando os métodos do projeto são mais difíceis de usar do que as soluções malfeitas. Sendo assim, torna-se mais fácil fazer a coisa errada do que a certa.

         Com a complexidade desnecessária o projeto tem o mau cheiro quando ele tem elementos excessivos, contendo construções que nunca são utilizados, podendo deixar o software pesado, complexo e difícil de entender.

         Outro mau cheiro é a repetição desnecessária, pois é fácil copiar e colar códigos, mas quando ocorre erro no mesmo é mais complicada a correção, pois cada cópia pode torna-se diferente da outra e por causa disto ocorrem diferentes erros.

         A opacidade também é um mau cheiro do código, pois se refere à dificuldade de compreensão de um módulo. Isto ocorre quando o código é escrito de maneira inteligível ou enrolada, se tornando cada vez mais difícil a sua compreensão.


Abaixo, exemplo de um programa em C# pra impressão;

public class Copier
{
 public static void Copy()
 {
  int c;
  while((c = keyboard.Read())!= -1)
   Printer.writer(c));
 }
} 


Abaixo, exemplo de Mau Cheiro do programa pra impressão, pois se teve que incluir mais algumas funções;

public class Copier
{
 //lembre de reiniciar estes flags
 public static bool ptFlag = false; //para impressora
 public static boll punchFlag = false; //para a fita
 public static void Copy()
 {
 int c;
 while((c = (ptFlag?PaperTape.Read():keyboard.Read()))!= -1)
  punchFlag?PaperTape.Punch(c):Printer.writer(c);
 }
}


Abaixo, exemplo Correto de como deveria ficar o programa para impressão;

public interface Reader
{
int Read();
}
public class KeiboardReader:Reader
{
 public int Read(){ return Keyboard.Read();}
} 
public class Copier
{ 
 public static Reader reader = new KeyboardReader();
 public static void Copy()
 {
  int c;
  while((c = (reader.Read()))!= -1)
   Printer.writer(c);
 }
}



           Mas existe solução ou prevenção contra o mau cheiro do software, o correto é sempre manter o projeto do sistema o mais limpo e simples possível, e sempre testar sua flexibilidade e sua fácil alteração caso no futuro seja necessário acrescentar ou alterar algo no programa. Desta forma o software ficará com uma rápida execução e com uma visualização elogiável.




Referência bibliográfica

MARTIN. Robert C., MARTIN Micah, Princípios, padrões e práticas ágeis em C#. Editora BOOKMAN. 2011. (p.121 -133).

Padrão Money

Resolvi escrever sobre este tópico pois trata-se de um assunto de extrema relevância e que muitos, assim como eu tem dúvidas quando se trata de trabalhar com valores monetários, principalmente quando estamos lidado com dinheiro de terceiros. O problema mais sutil é com o arredondamento. Cálculos monetários muitas vezes são arredondados para a menor unidade da moeda com isso é fácil perder alguns centavos devido aos erros de arredondamento.

Enigma de Matt Foemmel

Suponha que eu tenha que alocar 5 centavos entre duas contas: 70% para uma e 30% para outra. O cálculo matemático resulta em 3,5 centavos e 1,5. Não importa como será arredondado os valores, ou tu ganharás 1 centavo ou perderás 1 centavo.

Como funciona

A idéia básica é ter uma classe Dinheiro com campos para o valor e a moeda corrente. Você pode armazenar a quantidade como um tipo inteiro ou tipo decimal fixo. O tipo decimal é mais fácil para algumas manipulações, o integral para outras. Deve-se evitar completamente qualquer tipo de ponto flutuante, pois isso introduzirá o tipo de problemas de arredondamento cuja finalidade de Dinheiro é evitar.

A solução favorita de Martin Fowler foi essa: ter uma função de alocação na classe Dinheiro. O parâmetro para o alocador é uma lista de números, representando a proporção a ser alocada. O alocador retorna uma lista de objetos dinheiro, garantindo que nenhum centavo seja perdido espalhando-os pelos objetos dinheiro alocados de um modo que, de fora, pareça pseudo-randômico.
Com os números em ponto flutuante descartados, a escolha fica entre inteiros e decimais de ponto fixo, o que em Java se resume a BigDecimal, BigInteger e Long.
class Dinheiro{
   private long quantia;
   private Currency moeda;
}
Usar o long nos permite ter expressões matemáticas mais legíveis. Porém se o número ficar muito grande, ocorrerá um erro de estouro (overflow). A conclusão que se chega é que é necessário a criação de vários construtores para cada tipo numérico:
class Dinheiro{
   public Dinheiro(long quantia, Currency moeda){
      this.moeda = moeda;
      this.quantia = quantia * fatorCentavos( );
   }

   public static final int[ ] centavos = new int[ ] {1,10,100,1000};

   private int fatorCentavos( ){
      return centavos[moeda.getDefaultFractionDigits( )]
   }
}
Diferentes moedas tem diferentes quantias fracionárias. A classe Currency lhe informará o numero de dígitos fracionários em uma classe. O Dinheiro é um Objeto Valor*, então deve ter suas operações de igualdade e código hash sobrescritas para serem baseadas na moeda corrente e na quantia. Se você quiser alocar uma importância em dinheiro para muitos destinatários e não quiser perder centavos, irá precisar de um método de alocação.
class Dinheiro{
   public Dinheiro[ ] alocar (long[ ] razoes){
      long total = 0;
      for(int i = 0; i ;razoes; i++) total += razoes;
      long resto = quantia;
      Dinheiro[ ] resultados = new Dinheiro[razoes.length];
      for(int i = 0; i; resultados.length; i++){
         resultados[ i ] = novoDinheiro(quantia * razoes[ i ] / total);
         resto -= resultados[ i ].quantia;
      }
      for(int i = 0; i; resto; i++)resultados[ i ].quantia++;
      return resultados;
   }
}
Pode-se utilizar isso para resolver o Enigma de Foemmel
class Dinheiro{
   public void testeAlocar( ){
   long[ ] alocacao = {3,7};
   dinheiro[ ]resultado = dinheiro.dolares(0.05).alocar(alocacao);
   equals(Dinheiro.dolares(0.02), resultado[0]);
   equals(Dinheiro.dolares(0.03), resultado[1]);
   }
}
Com isso, uma classe Dinheiro pode ser utilizada para quase todo o cálculo numérico em ambientes OO. A razão principal é encapsular a manipulação do comportamento de arredondamento, o que ajuda a reduzir os problemas de erros de arredondamento. Outra razão para uso de Dinheiro é tornar muito mais fácil o trabalho com diversas moedas. A objeção mais comum a Dinheiro é o desempenho, raramente tenha-se ouvido falar de casos em que ele fez qualquer diferença perceptível, e mesmo nesses casos o encapsulamento, muitas vezes, torna mais fácil a otimização.

*Objeto Valor(Value Object):Um objeto pequeno e simples, como dinheiro ou um intervalo de datas, cuja igualdade não é baseada em identidade.

Referências: Padrões de Arquitetura de Aplicações Corporativas - Martin Fowler - páginas 455 a 457.

Nomes Significativos == Uma Boa Prática



O Código limpo, é como comparar um pintor e sua obra, não basta conhecer cores, texturas e pinceis, para garantir uma pintura magnífica. Pode-se ensinar a mecânica, colocar no papel todos os princípios necessários para um Código limpo e mesmo assim, não teríamos garantias do resultado esperado. Aprender a criar códigos limpos é uma tarefa árdua e requer mais do que o simples conhecimento dos princípios e padrões, requer treino, trabalho, prática e a agonia em pagar o preço de ter tomado decisões erradas.
Neste artigo, vamos apresentar alguns princípios e sugestões, e a intensão é direcionar o raciocínio para um bom projeto e não para um bom programa. Dentro desse contexto, há duas vertentes para se obter habilidade profissional: conhecimento e trabalho.

Devemos adquirir o conhecimento dos princípios, padrões, práticas e heurísticas que um profissional habilidoso sabe, e também esmiuçar esse conhecimento com seus dedos, olhos e corpo por meio de trabalho árduo e da prática. 

O CÓDIGO

Código é a linguagem na qual expressamos nossos requisitos, podemos criar linguagens que possam expressar mais facilmente nossas intensões, podemos criar ferramentas que nos ajudem a analisar a sintaxe e as estruturas formais, mas jamais eliminaremos a precisão necessária, ou seja, sempre teremos um código.

BOAS PRÁTICAS

Nomes Significativos

Há nomes por todos os lados em um software. Nomeamos nossas variáveis, funções, parâmetros, classes e pacotes, assim como o arquivo-fonte e os diretórios que os possui. Nomeamos também nossos arquivos jar e war e ear. Então, nomear é parte importantíssima de nosso código, o que nos obriga a termos muito cuidado nesta prática, seguem abaixo, algumas regras simples para criação de bons nomes: 

Use Nomes que Revelem seu Propósito

Parece fácil, mas escolher bons nomes é coisa séria, leva tempo, mas economiza muito mais, preocupe-se com seus nomes, tenha carinho e cuidado e troque-os quando encontrar melhores, pois aqueles que lerem seu código (inclusive você mesmo) ficarão agradecidos. 

O nome de uma variável, função ou classe deve responder a todas as grandes questões. Ele deve lhe dizer porque existe, o que faz e como é usado. Se um nome requer um comentário, então ele não revela seu propósito.
 int t; // tempo decorrido em dias 
O nome t não revela nada, não dá ideia de tempo decorrido, nem de dias. Devemos escolher um nome que especifique seu uso para medida e a unidade usada.

int tempoDecorridoEmDias; ou elapseTimeInDays;
int diasAposCriacao; ou daysSinceCreation;
Usar nomes que revelem seu proposito facilita bastante o entendimento e a alteração de um código

Evite Informações Erradas

Como comentamos antes, criar nomes não é uma tarefa fácil, então, os programadores tem que tomar cuidado para não induzir a uma interpretação errônea ou diferente daquilo que desejávamos. Por exemplo: Não se refira a um grupo de contas como accountList , a menos que realmente seja uma list . A palavra list (lista) significa algo específico em um código. Se o que armazena as contas não for uma list de verdade, poderá confundir os outros. Portanto, accountGroup ou bunchOfAccounts, ou grupoDeContas, seria melhor. Cuide também para não usar nomes muito parecidos. Fica difícil perceber a pequena diferença entre XYZControllerForEfficientHandlingOfStrings em um módulo e XYZControllerForEfficientStorageOfStrings em outro. Ambas as palavras são muito semelhantes.

Faça Distinções Significativas

Como não é possível usar o mesmo nome para referir-se a duas coisas diferentes num mesmo escopo, você pode decidir alterar o nome de maneira arbitrária. Se os nomes precisam ser diferentes, então também devem ter significados distintos.

Use Nomes Pronunciáveis

O ser humano é bom com as palavras. Uma parte considerável do seu cérebro é responsável pelo conceito das palavras. E, por definição, as palavras são pronunciáveis. Seria um desperdício, não tirar proveito disso. Sendo assim, crie nomes pronunciáveis.

Para ilustrar essa ideia, segue uma exemplo: variável genymdhms (generation date, year, mounth, day, hour, minute e second) não seria melhor escrita assim: generationTimestamp?

Use Nomes Passíveis de Busca

Nomes de uma só letra ou números possuem um problema em particular por não ser fácil localizá-los ao longo de um texto. Pode-se usar facilmente o grep para MAX_CLASSES_PER_STUDENT , mas buscar o numero 7 poderia ser mais complicado. Nomes, definições de constante e várias outras expressões que possuam tal numero, aparecerão na sua pesquisa. Da mesma forma, o nome “e” é uma escolha ruim para qualquer variável que necessite ser buscada.

Nomes de Classes

Classes e objetos devem ter nomes com substantivo(s), como cliente, paginaWiki, Conta e analiseEndereco. Evite palavras como Gerente, Processador, Dados ou Info no nome de uma classe, que também não deve ser um verbo

Nomes de Métodos

Os nomes de métodos devem ter verbos, como postarPagamento, excluirPagina ou salvar. Devem-se nomear métodos de acesso, alteração e autenticação, segundo seus valores e adicionar os prefixos get, set ou is de acordo com o padrão javabean.
string name = employee.getName();
custumer.setName(“mike”);
if (paycheck.isPosted())...
Quando os construtores estiver sobrecarregados, use métodos factory estáticos com nomes que descrevam os parâmetros. Por exemplo:
Complex fulcrumPoint = Complex.FromRealNumber(23,0);
É melhor do que
Complex fulcrumPoint = new Complex (23,0);
Para forçar o uso, torne os construtores correspondentes como privados.

Não de uma de Espertinho

Não use gírias ou códigos que somente pessoas ligadas a você ou de sua região, possam entender, nem piadas ou palavrões.

Selecione uma Palavra por Conceito

Escolha uma palavra por conceito abstrato e fique com ela. Por exemplo, é confuso ter pegar, recuperar e obter como métodos equivalentes de classes diferentes. Da mesma forma, é confuso ter um controlador, e um gerenciador e um driver no mesmo código-fonte.

Um léxico consistente é uma grande vantagem aos programadores que precisem usar seu código.

Não Faça Trocadilhos

Evite usar a mesma palavra para dois propósitos. Usar o mesmo termo para duas ideias diferentes é basicamente um trocadilho.

Se você seguir a regra “uma palavra por conceito”, você pode acabar ficando com muitas classes que possuam, por exemplo, um método add. Contanto que as listas de parâmetros e os valores retornados dos diversos métodos add , sejam semanticamente equivalentes, tudo bem.

Use Nomes a Partir do Domínio da Solução

Lembre-se de que serão programadores que lerão seu código. Portanto, pode usar termos de informática, nomes de algoritmo, nomes de padrões termos matemáticos, etc.

Use Nomes de Domínios do Problema

Quando não houver uma solução para programadores, use o nome do domínio do problema. Pelo menos o programador que fizer a manutenção do seu código poderá perguntar a um especialista em tal domínio o que o nome significa.

Distinguir os conceitos do domínio do problema dos do domínio da solução é parte da tarefa de um bom programador.

Adicione um Contexto Significativo

Há poucos nomes que são significativos por si só, a maioria não é. Por conta disso, você precisa usar nomes que façam parte do contexto para o leitor. Para isso, você os coloca em classes, funções, e namespaces bem nomeados. Se nada disso funcionar, então talvez como ultimo recurso, seja necessário adicionar prefixos ao nome.

Imagine que você tenha varias variáveis chamadas nome, sobreNome, rua, numeroCasa, cidade, estado, cep. Vistas juntas, fica bem claro que elas formam um endereço. Pode-se adicionar um prefixo para um contexto: endNome, endSobreNome, endEstado, etc. Pelo menos, os leitores entenderam que essas variáveis fazem parte de uma estrutura maior

Não Adicione Contextos Desnecessários

Nomes curtos geralmente são melhores contanto que sejam claros. Não adicione mais contexto a um nome do que o necessário.

Os nomes accountAddress e custumerAddress estão bons para instancias de classe Endereço, mas seriam ruins para nomes de classes. Se precisar diferenciar entre endereços de MAC, endereço de portas e endereços de WEB, uma ideia seria usar EnderecoDeCorrespondenciam MAC e URI. Os nomes resultantes são mais precisos, motivo pelo qual existe a tarefa de se atribuir nomes.

CONCLUSÃO

O mais difícil sobre escolher bons nomes é a necessidade de se possuir boas habilidades de descrição, essa é uma habilidade que se aprende e não de aprimoramento técnico, gerencial ou empresarial. Como consequência, muitas pessoas nessa área não aprendem muito bem. Siga algumas dessas regras e note que você melhorou a legibilidade e a clareza de seu código. Se você estiver fazendo a manutenção no código de alguém, use ferramentas de refatoração para ajudar a resolver essas questões. Em pouco tempo valerá a pena, e continuará valendo a longo prazo.

REFERÊNCIAS

Livro - Código Limpo: Habilidades Práticas do Agile Software
Autor - Robert C. Martin

quinta-feira, 1 de maio de 2014

Padrão de Projeto Flyweight

O padrão de projeto Flyweight, ou "peso-mosca", é um padrão estrutural que tem por finalidade usar de compartilhamento de objetos de granularidade fina, isto é, objetos que são iguais exceto por pequenos detalhes. Este padrão usa as informações comuns a diversos objetos em apenas um componente, o resto dos dados únicos são utilizados como parâmetros de métodos, reduzindo o número de objetos em uma aplicação, e assim agilizando o processamento da execução destes muitos objetos repetidos.
Para um bom resultado do uso deste padrão,use-o quando todas as afirmativas seguintes forem verdadeiras:

- A aplicação usa um grande número de objetos;
- Custos de armazenamento são grandes por causa da grande quantidade de objetos;
- A maior parte do estado dos objetos podem ser extrínseco (informações que dependem e variam com o contexto do flyweight, e que por esse motivo nao podem ser compartilhados);
- Muitos grupos de objetos podem ser substituídos por poucos objetos que possam ser compartilhados (estado extrínseco deve ser removido para outro lugar);
- A aplicação não depende da identidade do objeto;

Ainda deve-se pensar sobre o ponto fraco do padrão, que é dependendo da quantidade e da organização dos objetos a serem compartilhados, pode haver um grande custo para procura deles. Então ao utilizar o padrão deve ser analisado qual a prioridade no projeto, espaço ou tempo de execução.
Os participantes deste padrão seriam:

- Cliente : computa ou armazena o estado extrínseco do flyweight, assim como mantém a referência para ele;
- Flyweight: declara uma interface por onde pode receber e atuar sobre estados extrínsecos;
- FlyweightFactory: cria e gerencia objetos flyweights e garante que eles sejam compartilhados corretamente. Quando um cliente solicita um flyweight, um objeto FlyweightFactory fornece a ele, ou cria se caso ele não exista.
- ConcretesFlyweights (no exemplo do processador de texto seria o caractere em si): implementa a interface Flyweight e armazena só os estados intrísecos (compartilháveis).

Um bom exemplo da utilização é no desenvolvimento de jogos, afinal são usadas várias imagens que representam as entidades que compõe o jogo (cenário, jogador, inimigo..).
Isso causa duplicação de informação.Por exemplo, quando aparecem vários inimigos iguais no jogo, o mesmo conjunto de imagens é criado repeditas vezes.
Para exemplificar isso em código, começamos criando uma classe Imagem (informação intríseco):
  public class Imagem {
    protected String NomedaImagem;

    public Imagem(String imagem) {
        NomedaImagem = imagem;
    }

    public void desenharImagem() {
      System.out.println(NomedaImagem + " desenhada!");
    }
    }

Entao criar a classe Ponto (informação extrínseco):
  public class Ponto {
    public int x, y;

    public Ponto(int x, int y) {
        this.x = x;
        this.y = y;
    }
    }
Uma classe Flyweight será feita para armazenar o método de desenho da imagem em um determinado ponto:
 public abstract class SpriteFlyweight {
    public abstract void desenharImagem(Ponto ponto);
    }
Após a criação da classe Flyweight, criamos uma classe Flyweight Concreta, que seria a implementação da operação de fato:
 public class Sprite extends SpriteFlyweight {
    protected Imagem imagem;

    public Sprite(String nomeDaImagem) {
        imagem = new Imagem(nomeDaImagem);
    }

    @Override
    public void desenharImagem(Ponto ponto) {
        imagem.desenharImagem();
        System.out.println("No ponto (" + ponto.x + "," + ponto.y + ")!");
    }
    }
  
A classe fábrica guardará todas as imagens que serão compartilhadas e terá um método para pega-las, tendo assim todo acesso as imagens centralizado nessa classe:
 public class FlyweightFactory {

    protected ArrayList flyweights;

    public enum Sprites {
        JOGADOR, INIMIGO_1, INIMIGO_2, CENARIO_1
    }

    public FlyweightFactory() {
        flyweights = new ArrayList();
        flyweights.add(new Sprite("jogador.png"));
        flyweights.add(new Sprite("inimigo1.png"));
        flyweights.add(new Sprite("inimigo2.png"));
        flyweights.add(new Sprite("cenario1.png"));
    }

    public SpriteFlyweight getFlyweight(Sprites jogador) {
        switch (jogador) {
        case JOGADOR:
            return flyweights.get(0);
        case INIMIGO_1:
            return flyweights.get(1);
        case INIMIGO_2:
            return flyweights.get(2);
        default:
            return flyweights.get(3);
        }
    }
    }
 
E utilizando esse padrão:
 public static void main(String[] args) {
    FlyweightFactory factory = new FlyweightFactory();

    factory.getFlyweight(Sprites.JOGADOR).desenharImagem(new Ponto(10, 10));

    factory.getFlyweight(Sprites.INIMIGO_1).desenharImagem(
            new Ponto(100, 10));
    factory.getFlyweight(Sprites.INIMIGO_1).desenharImagem(
            new Ponto(120, 10));
    factory.getFlyweight(Sprites.INIMIGO_1).desenharImagem(
            new Ponto(140, 10));
    factory.getFlyweight(Sprites.INIMIGO_2).desenharImagem(
            new Ponto(60, 10));
    factory.getFlyweight(Sprites.INIMIGO_2).desenharImagem(
            new Ponto(50, 10));
    }
Pode ser implementado algo para eliminar um objeto nao usado mais, liberando ele da memória e diminuindo espaço.

Referências :
Livro - Padroes de Projetos - Solucoes Reutilizaveis - Gamma Erich
Site - http://brizeno.wordpress.com/category/padroes-de-projeto/flyweight/

domingo, 23 de março de 2014

Vulnerabilidade CSRF em roteadores Asus e TP-Link

Essa vulnerabilidade foi descrita por Bogdan Calin e pode afetar vários modelos dessas e de outras marcas. Apesar de ter sido encontrada no final do ano de 2012, essa vulnerabilidade provavelmente não foi corrigida, tendo em vista que raramente é feita a atualização do firmware desses aparelhos.

Apartir dessa vulnerabilidade é possível realizar um ataque que pode alterar o servidor DNS utilizado pelo roteador da rede interna de uma vítima. O ataque ocorre através da interface de controle web do roteador vulnerável, que permite que os servidores de DNS sejam alterados através de uma requisição do tipo GET. Obviamente, essa interface de controle exige dados de autenticação para que a requisição seja bem sucedida, mas infelizmente a maioria dos usuários não alteram os dados de login e senha fornecidos pelo fabricante do aparelho. Isso permite que seja montada uma url de ataque parecida com essa:

http://admin:admin@192.168.0.1/start_apply.htm?wan_dns1_x=6.6.6.6&wan_dns2_x=6.6.6.6

Obs.: Perceba que nesse ataque está sendo suposto que o usuário e a senha de acesso ao aparelho são a string 'admin'

Utilizando essa técnica é necessário apenas que a vítima clique em um link contendo a url de ataque para que tenha seu servidor de DNS alterado para um que esteja sobre o controle do atacante. Entre outras possibilidades, isso permitiria redirecionar os clientes do roteador afetado para páginas falsas( fakes ) e, apartir dessas, coletar dados de autenticação das vítimas.

É claro que dependendo da vítima, o link poderia ser reconhecido como algo malicioso e portanto talvez não fosse clicado, mas Bogdan utilizou os princípios do CSRF para tornar a requisição da url de ataque independente do acionamento(clique) da vítima. Basicamente, ele inseriu a url de ataque na propriedade src de uma imagem, dessa forma o próprio navegador faz a requisição sem que a vítima perceba. Isso também permitiu aumentar as chances de sucesso do ataque, pois foi possível realizar várias requisições diferentes, podendo assim fornecer diversos dados de login na esperança de que algum esteja correto e altere o roteador da vítima. Segue um exemplo do que Bogdan poderia ter feito:


<img src="http://admin:admin@192.168.0.1/start_apply.htm?wan_dns1_x=6.6.6.6&wan_dns2_x=6.6.6.6" /> 
<img src="http://admin:qwerty@192.168.0.1/start_apply.htm?wan_dns1_x=6.6.6.6&wan_dns2_x=6.6.6.6" /> 
<img src="http://admin:123123@192.168.0.1/start_apply.htm?wan_dns1_x=6.6.6.6&wan_dns2_x=6.6.6.6" /> 
<img src="http://admin:12345678@192.168.0.1/start_apply.htm?wan_dns1_x=6.6.6.6&wan_dns2_x=6.6.6.6" />
<img src="http://admin:master@192.168.0.1/start_apply.htm?wan_dns1_x=6.6.6.6&wan_dns2_x=6.6.6.6" /> 
<img src="http://admin:password@192.168.0.1/start_apply.htm?wan_dns1_x=6.6.6.6&wan_dns2_x=6.6.6.6" /> 

Dessa forma ainda seria necessário fornecer um link para que a vítima clique, e só depois entre em um página com essas "imagens" no código html, mas nesse caso o link fornecido é muito mais inocente do que a url de ataque. Com essa adaptação qualquer usuário idependente do conhecimento pode ser vítima desse ataque, afinal quem não clicaria em um simples .html?

Referências: https://www.acunetix.com/blog/web-security-zone/the-email-that-hacks-you/

domingo, 9 de fevereiro de 2014

Introdução básica a Linguagem Python

Marcos Vinicius Scholl    marcos.vinicius.scholl@gmail.com
Introdução básica a Linguagem Python

Bom, após eu fazer propaganda da linguagem me foi solicitado que eu fizesse um Post no Blogue. Comei a faze-lo, mas me vi não podendo deixar de citar algumas coisas e quando me dei por conta, havia ficado um pouco extenso. Decidi colocar mais algumas características e exemplos e acabei fazendo um mini tutorial de introdução a linguagem!

Espero que gostem, se eu puder levar um minimo de conhecimento para alguém, já me sentirei feliz e com objetivo concluído, hehe

Em dezembro fui apresentado a esta linguagem ao qual nunca havia tido contato mas agora posso dizer, é uma linguagem impressionantemente "simples" e poderosa.
Segue abaixo o Tutorial que esta disponível para download por quem quiser.

Qualquer duvida que estiver ao meu alcance, segue acima meu email e hangout.

Introdução básica a linguagem Python by viny_scholl




Marcos Vinicius Scholl:
Graduando em Tecnologia em Análise e Desenvolvimento de Sistemas - TADS 
Universidade Federal do Rio Grande - FURG 
Instituto Federal de Educação, Ciência e Tecnologia do Rio Grande do Sul IFRS - Campus Rio Grande 
Rua Eng. Alfredo Huch, 475 - Rio Grande, RS - Brasil - 96201-460 

Sobre o autor:
Marcos Vinicius Scholl estuda na área de Tecnologia de Informação. É entusiasta na área de tecnologia. Gremista de paixão e anticolorado por prazer!

Introdução básica a Linguagem Python

Instituto Federal de Educação, Ciência e Tecnologia do Rio Grande do Sul
Campus Rio Grande
Curso Superior de Tecnologia em Análise de Desenvolvimento de Sistemas


Marcos Vinicius Scholl    marcos.vinicius.scholl@gmail.com
Introdução básica a Linguagem Python

http://pt.scribd.com/doc/205919501/Introducao-basica-a-linguagem-Python

O que é Python?

Python é uma linguagem de programação escrita em C, a sua implementação original também é conhecida como CPython. Python tem alguns aspectos que a tornam especial:
- É uma linguagem interpretada. Não necessita de compilação, o fonte é executado direto pelo interpretador.
- Dinamicamente tipada. (Não necessita de declaração de variável).
- Não há delimitadores de bloco ( { } ), isto é feito apenas pela Indentação.
- Tipagem forte. Verifica se a operação é válida e não faz correção automática entre os tipos incompatíveis.
- Trabalha com operadores de alto nível: string, lista, tupla, dicionário, arquivo, classes
- É orientada a objetos: Afinal, tudo em Python é um objeto.


Por que Python?

Bom, eu também me fazia esta pergunta antes de ter qualquer contato com a linguagem. Agora, vejo o porquê, mas fica aqui alguns tópicos encontrados em um site:

Dado que existe uma grande diversidade de linguagens diferentes, por que aprender Python é interessante ou mesmo importante? Na minha opinião, a linguagem combina um conjunto único de vantagens:
? Os conceitos fundamentais da linguagem são simples de entender.
? A sintaxe da linguagem é clara e fácil de aprender; o código produzido é normalmente curto e legível.
? Os tipos pré-definidos incluídos em Python são poderosos, e ainda assim simples de usar.
? A linguagem possui um interpretador de comandos interativo que permite aprender e testar rapidamente trechos de código.
? Python ´e expressivo, com abstrações de alto nível. Na grande maioria dos casos, um programa em Python será muito mais curto que seu correspondente escrito em outra linguagem. Isto também faz com o ciclo de desenvolvimento seja rápido e apresente potencial de defeitos reduzido – menos código, menos oportunidade para errar.
? Existe suporte para uma diversidade grande de bibliotecas externas. Ou seja, pode-se fazer em Python qualquer tipo de programa, mesmo que utilize gráficos, funções matemáticas complexas, ou uma determinada base de dados SQL.
? É possível escrever extensões a Python em C e C++ quando é necessário desempenho máximo, ou quando for desejável fazer interface com alguma ferramenta que possua biblioteca apenas nestas linguagens.
? Python permite que o programa execute inalterado em múltiplas plataformas; em outras palavras, a sua aplicação feita para Linux normalmente funcionará sem problemas em Windows e em outros sistemas onde existir um interpretador Python.
? Python é pouco punitivo: em geral, “tudo pode” e há poucas restrições arbitrárias. Esta propriedade acaba por tornar prazeroso o aprendizado e uso da linguagem.
? Python é livre: além do interpretador ser distribuído como software livre (e portanto, gratuitamente), pode ser usado para criar qualquer tipo de software — proprietário ou livre. O projeto e implementação da linguagem é discutido aberta e diariamente em uma lista de correio eletrônico, e qualquer um é bem-vindo para propor alterações por meio de um processo simples e pouco burocrático.
Bom, vamos ao que interessa, Como funciona o Python!

Sintaxe:

NÃO NECESSITA DE ; haha
Comentários são feitos com o marcador (#), e comentário em bloco por (""").


Comando Print:

a = 21
print "Minha idade é", a 
print "Minha idade é %d !!" %a 
print 'Minha idade é', a, "!!" 
>>>
Minha idade é 21
Minha idade é 21 !!
Minha idade é 21 !!


No caso de formatação no print, pode ser utilizado os mesmos tipos de C, como o (%.2f) para float, porém todo o %(marcador, ex: %d, %f,...) que houver no print tem de existir um % para acesso a variável.
Por isso no segundo exemplo acima é necessário o %a.


Tipagem Dinâmica:

Python não define tipos para variável, como int, float, String, double, complex, bool mas trabalha com todos eles.

a = 1
type(a)  #função que retorna o tipo associado a uma variável
>>>


print a
>>>
1
 
a = "teste"
print a
>>>
teste

Nota que o mesmo a que foi primeiramente atribuído um int, em seguida foi atribuído uma String? Mas como isso é possível? Em C e Java isto é impensável, mas como Python é tipado dinamicamente, a variável assume o tipo de acordo com seu valor.

Exemplo de criação de variáveis

a = 1 # valor inteiro
preco = 10.99 # valor ponto flutuante, ou float.
t = True # valor booleano
i = 4+3j # valor complexo / Só pelo nome ainda nem sequer mandei um print pra testar como funciona, hehe

a = 'Arena'
b = ' > '
c = ' Beira Rio'
print a + b + c
>>>
Arena > Beira Rio 


Estamos trabalhando com String certo? Como podemos saber quais métodos estão disponíveis para este tipo de dado e que o Python nos prove?
Com a função dir(),ela analisa a nossa variável e nos retorna os métodos do tipo pertinente.
dir(a)
>>>
['__add__', '__class__', '__contains__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__getslice__', '__gt__', '__hash__', '__init__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_formatter_field_name_split', '_formatter_parser', 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit', 'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

Métodos reservados são os que começam com __, por exemplo o método '__add__', que é chamado toda vez que usamos o (+).
Atribuindo caracteres da string a variáveis:
a, b, c, d = "TADS"
print a
print b
print c
print d
>>>
T
A
D
S

Multiplicação de uma String, isso é impensável em Java mas em Python ele realmente multiplica, mas o valor desta variável:

var = "TADS "
print var*5
>>>
TADS TADS TADS TADS TADS

Atribuição de variável;

O Python por ser criado em cima do C, carrega suas características.
Se fizermos duas variáveis, e dissermos que a segunda é igual a primeira, não temos duas alocações de memória, e sim dois apontamentos para o mesmo endereço de memória.
Descobrimos a posição na memória da variável através da função id().
A função retorna a identidade do objeto que é a garantia de ser único e constante para este objeto durante sua vida. Dois objetos com vida no programa ou execução não sobrepostos podem ter o mesmo id().
Ex:
a = 100
b = a
c = a+1
print id(a)
>>>
1533140
print id(b)
>>>
1533140
print c
1>>>
11533128

Nota que o id da variável a e b tem o mesmo valor, pois apontam para a mesma posição de memória. (Ahh, como é bom não lidar com ponteiros! :D)

Fortemente Tipada:

Uma vez atribuída a uma variável um valor inteiro, ela não pode ser somada com uma variável com uma String. Em JavaScript isto até funciona, pois a linguagem é fracamente tipada e então faz conversão automática mas em Python não.
Ex:
a = 'Grêmio fundado em '
b = 1903
print a+b
>>>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Se quisermos uma string concatenada com estas duas variáveis devemos fazer uma conversão com cast.
No caso de inteiro para String é usado str().

print a + str(b)
>>>
Grêmio fundado em 1903

Se fosse uma soma e tivéssemos um valor em string, seria usado int().

Uma coisa curiosa que o Python oferece é a troca de valor entre variáveis.
Vejam só.
Sendo a = 10 e b = 20 faça a troca de valores entre a e b.
Obs: Não vale fazer isso:
  print 'a = %d \nb = %d' %(b, a) 

Você que está lendo instintivamente pensou, vou fazer uma variável auxiliar!
c = a
a = b
b = c
print a, b
>>>
20 10

Funciona? Sim! É a maneira correta e eu vou aceitar isso? NÃO ("- Vocês já sabem quem eu imitei! haha")

Em Python a simples inversão de valores entre variáveis se dá desta forma:
a, b = b, a;
print 'a = %d \nb = %d' %(a, b)
>>>
a = 20
b = 10
Fácil, não?

Entrada de Dados:

. Ok, trabalhar com entrada de dados em Python é bem infinitamente mais fácil que Java e é mais fácil que C.
Temos que chamar o método ( input(texto_de_entrada_no_console))
Vamos ler do teclado um texto qualquer:
txt = input('Entre com alguma coisa')
print txt
>>>
o_que_digitamos

Se quisermos tratar esta entrada para um tipo especifico é só adicionar o cast de conversão:
Ex:
idade = int(input('Entre com sua idade')
print 'Tu nasceu em %d' %(2014 - idade)
>>>
1993 #só p testar, pois se tu ainda não fez aniversário em 2014, ele vai te dar um ano de nascimento a mais! :/

Lembre-se. Python é fortemente indentado. Se tiver erro de indentação, ele nem executa.
print a
     print b
     ^
     Erro de indentação

Blocos de Código:

Todo bloco de código são delimitados pelo uso da indentação, e sempre a linha anterior ao bloco deve terminar com (:).

Ex:
#imprime impares
for i in range(0,100):
        if i % 2:
                print i

Controle de Fluxo:

Conjunto de instruções com um condicional pelo menos.
if :
    
elif :
    
else:
    

Ex:
temp = 10
if temp < 0:
    print 'Congelando...'
elif 0 <= temp <= 20:
    print "Frio"
elif 20 <= temp <= 30:
    print 'De Boa'
else:
    print "Muito Quente"
>>>
Frio

Percebam que independe do print se usarmos (') ou (").

Laços:

For:
for  in :
    
    continue
    break
else:
    

A clausula break interrompe o laço e continue passa para a próxima iteração. O código dentro do else é executado ao final do laço, a não ser que o laço tenha sido interrompido por break.
Exemplo:
# Soma de 0 a 99
s = 0
for x in range(1, 100):
    s = s + x
print s
>>>
4950


A função range(m, n, p),é muito útil em laços, pois retorna uma lista de inteiros, começando em m e menores que n, em passos de comprimento p, que podem ser usados como sequência para o laço.

Tipos de dados de Alto Nível:

Além de tipos básicos como (int, float, boolean,..) Python possuem determinados tipos que merecem atenção:

String:

String é uma sequência imutável que armazena uma sequência de caracteres. Pode ser tanto delimitada com aspas duplas quanto com aspas simples. Se for delimitada com aspas simples, podemos usar aspas duplas como parte integrante da string.
a = "Exemplo de String"
b = 'Exemplo com "Aspas Duplas"'

#Se quisermos criar uma string com múltiplos caracteres o Python nos prove um delimitador aspas triplas, (""")
bairrista = """...
Mostremos valor constância
Nesta ímpia e injusta guerra
Sirvam nossas façanhas
De modelo a toda Terra
..."""
 
print bairrista
>>>
...
Mostremos valor constância
Nesta ímpia e injusta guerra
Sirvam nossas façanhas
De modelo a toda Terra
...

E também, como todos já estiveram em ED1 ou AAP sabem, como toda sequência a String pode ser indexada dividida ou acessada usando colchetes
teste = "Instituto Federal"
print teste[0]
>>>
I
 
print teste[16]
>>>
l
 
print teste[-1]
>>>
l
 
print teste[:11]
>>>
Instituto F
 
print teste[-6:-1]
>>>
edera


Percebam que como o Python trabalha com String e Lista como se fosse lista Circular a posição da string -1 e a 16 que é a última, são a mesma letra!
Com isso, é possível trabalhar de inúmeras formas com a string, no caso acima foi pedido para mostrar de moonwalker(traz pra frente) da Posição -6 até a -1 o que continha a variável.


Listas:

Como uma sequência de valores, vetor em outras linguagem. Os dados são acessados por um índice numérico iniciado em 0.
A lista do Python pode armazenar qualquer tipo de valor, são mutáveis, podendo ser alteradas a qualquer momento.

Ex:
lista = ["t", "A", "d", "S", 9, 2, 2014, "Dalhe     Grêmio", "Saldo:", 0.50]
print lista[0]
>>>
t
 
print lista[7]
>>>
Dalhe Grêmio
 
print lista[0],lista[1], lista[2], lista[3]
>>>
t A d S

lista.append("teta")
print lista[10]
>>>
teta


Percebam que como é uma lista, alguns métodos já são pré-existentes em Python.

Podemos percorrer a lista inteira com um Iterador sobre ela.
for item in lista:
 print item
>>>
t
A
d
S
9
2
2014
Dalhe Grêmio
Saldo:
0.5
teta


Listas podem incluir outras listas internamente.
Algumas outras Operações com lista são:
# Incluindo
lista.append('Robin')
 
# Removendo
lista.remove('teta')
 
# Ordena a lista
lista.sort()
 
# Inverte a lista
lista.reverse()
 
# Imprime numerado
for i, item in enumerate(lista):
    print i + 1, '=>', item


A lista pode ser acessada o seu último elemento sem saber a sua posição, somente é necessário indicar que é o elemento anterior ao primeiro, como se fosse uma lista circular.
# Trocando o último elemento
lista[-1] = 'Batman'


A lista possui o método pop() que facilita a implementação de filas e pilhas:

# A lista vazia é avaliada como falsa
while lista:
   # Em filas, o primeiro item é o primeiro a sair
   # pop(0) remove e retorna o primeiro item
   print 'Saiu', lista.pop(0), ', faltam', len(lista)
 
# Mais itens na lista
lista += ["AAP", 'Ed1', 'Ed2']


As operações de ordenação sort() e inversão reverse()são realizadas na própria lista, sendo assim, não geram novas listas.

Tuplas:

As Tuplas também são sequencias de elementos arbitrários, se comportam como as listas mas tem uma exceção importante. São imutáveis. Uma vez criadas não podem ser alteradas.

Sintaxe:
tupla = (a, b, ..., z)
Os parênteses são opcionais.

Particularidade: tupla com apenas um elemento é representada como:
t1 = (a,)

Os elementos de uma tupla podem ser referenciados da mesma forma que os elementos de uma lista:
primeiro_elemento = tupla[0]


Listas podem ser convertidas em Tuplas:
tupla = tuple(lista)


E Tuplas podem ser convertidas em listas:
lista = list(tupla)


Embora a tupla possa conter elementos mutáveis, esses elementos não podem sofrer atribuição, pois isto modificaria a referência ao objeto.
Exemplo:

tupla = ([1, 2], 4)
tupla[0].append(3)
print tupla
>>>
([1, 2, 3], 4)
 
tupla[0].append("TADS")
print tupla
>>>
([1, 2, 3, 'TADS'], 4)


As Tuplas são mais eficientes do que as listas convencionais, pois consomem menos recursos computacionais (memória), por serem estruturas mais simples, tal como as String imutáveis em relação às String mutáveis.

Outros tipos de sequências


O Python provê entre os builtins também:
set: sequência mutável unívoca (sem repetições) não ordenada.
frozenset: sequência imutável unívoca não ordenada.
Os dois tipos implementam operações de conjuntos, tais como: união, interseção e diferença.

OBS: Quando uma lista é convertida para set, as repetições são descartadas.


Dicionário:

Os dicionários são sequencias que podem utilizar índices de tipos variados, basta que estes índices sejam imutáveis (números, tuplas e strings,..). Dicionários são conhecidos em outras linguagens como arrays associativos ou hashes.

Um dicionário é uma lista de associações compostas por uma chave única e estruturas correspondentes.

Dicionários são mutáveis, tais como as listas.

Estrutura Dicionário:
[[FOTO Dicionário]]
autor = {"nome" : "Marcos", "sobrenome" : "Scholl", "idade": 21}
print autor["nome"]
>>>
Marcos
 
print autor["sobrenome"]
>>>
Scholl
 
print autor["idade"]
>>>
21


O dicionário do Python não fornece garantia de que as chaves estarão ordenadas.

Exemplo de dicionário:
dic = {'nome': ['Angus Young', 'Malcolm Young', 'Phil Rudd', 'Cliff Williams', 'Brian Johnson'], 'banda': 'AC/DC'}
 
#Acessando elementos:
print dic['nome']
>>>
['Angus Young', 'Malcolm Young', 'Phil Rudd', 'Cliff Williams', 'Brian Johnson']
 
#Adicionando elementos:
dic['album'] = 'Back in Black'

Apagando um elemento do dicionário:
del dic['album']


Obtendo os itens, chaves e valores:
itens = dic.items()
chaves = dic.keys()
valores = dic.values()

dic.keys()
>>> 
['banda', 'nome']

Nota que ele Ordenou as chaves.
dic.values()
>>>
['AC/DC', ['Angus Young', 'Malcolm Young', 'Phil Rudd', 'Cliff Williams', 'Brian Johnson']]



Matriz Esparsa:


Bom, matriz creio que todos já conhecem o seu funcionamento, nada mais é que um vetor de duas dimensões. Possui a característica de Linhas e Colunas.

Agora, uma matriz esparsa, sabes o que é?
Segundo uma definição da Wikipedia : http://pt.wikipedia.org/wiki/Matriz_esparsa

Uma matriz é dita esparsa quando possui uma grande quantidade de elementos que valem zero (ou não presentes, ou não necessários).

A matriz esparsa é implementada através de um conjunto de listas ligadas que apontam para elementos diferentes de zero. De forma que os elementos que possuem valor zero não são armazenados.

Ou seja, em breve definição Matriz esparsa é uma estrutura que só armazena os valores que existem na matriz.

Uma boa alternativa é usarmos um Dicionário. Para as chaves, nós podemos usar Tuplas que contêm os números da linha e a coluna.

No caso, achei um bom exemplo de matriz esparsa em Python utilizando Dicionário e Tuplas.
Cada tupla representa uma posição na matriz

Matriz esparsa implementada com dicionário

dim = 6, 12 #Define as características da matriz
mat = {} #define a matriz
mat[3, 7] = 3   # Instancia valores em pontos aleatórios da matriz
mat[4, 6] = 5
mat[6, 3] = 7
mat[5, 4] = 6
mat[2, 9] = 4
mat[1, 0] = 9
for lin in range(dim[0]):
        for col in range(dim[1]):
        # Método get(chave, valor) retorna o valor da chave no dicionário ou se a chave não existir, retorna o segundo argumento
                print mat.get((lin, col), 0),
        print
Saída:
0 0 0 0 0 0 0 0 0 0 0 0
9 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 4 0 0
0 0 0 0 0 0 0 3 0 0 0 0
0 0 0 0 0 0 5 0 0 0 0 0
0 0 0 0 6 0 0 0 0 0 0 0


Nota que muitos valores nesta matriz são dispensáveis, nesta matriz somente nos importa 6 valores. E estes 6 valores são a quem corresponde a matriz esparsa!!

Exemplo de Criação da Matriz Esparsa, através de uma String!


# Matriz em forma de string
matriz = '''
0 0 0 0 0 0 0 0 0 0 0 0
0 1 0 0 0 1 0 1 1 1 1 0
0 1 1 0 1 1 0 1 0 0 0 0
0 1 0 1 0 1 0 1 1 1 1 0
0 1 0 0 0 1 0 0 0 0 1 0
0 1 0 0 0 1 0 1 1 1 1 0'''
 
mat = {}
 
# Quebra a matriz em linhas
for lin, linha in enumerate(matriz.splitlines()):
  # Quebra a linha em colunas
  for col, coluna in enumerate(linha.split()):
   coluna = int(coluna)
   # Coloca a coluna no resultado,
   # se for diferente de zero
   if coluna:
             mat[lin, col] = coluna
print mat
# Soma um nas dimensões pois a contagem começa em zero
print 'Tamanho da matriz completa:', (lin + 1) * (col + 1)
print 'Tamanho da matriz esparsa:', len(mat)
>>>
{(4, 7): 1, (4, 8): 1, (2, 8): 1, (6, 9): 1, (2, 1): 1, (3, 7): 1, (5, 1): 1, (2, 5): 1, (4, 9): 1, (5, 5): 1, (2, 9): 1, (6, 10): 1, (4, 1): 1, (4, 10): 1, (3, 2): 1, (4, 5): 1, (2, 10): 1, (6, 7): 1, (6, 5): 1, (3, 5): 1, (2, 7): 1, (5, 10): 1, (6, 8): 1, (6, 1): 1, (3, 1): 1, (4, 3): 1, (3, 4): 1}
 
Tamanho da matriz completa: 84
Tamanho da matriz esparsa: 27


Percebam que temos desenhado na matriz esparsa um MS, Marcos Scholl! :D
A matriz esparsa é todos estes pontos que possuem 1.

Matrizes esparsas têm aplicações em problemas de engenharia, física (por exemplo, o método das malhas para resolução de circuitos elétricos ou sistemas de equações lineares). Também têm aplicação em computação: armazenamento de dados (e.g., planilhas eletrônicas)


Funções:

Funções são blocas de código com um nome que podem receber parâmetros pré-determinados.
É a mesma definição de método de Java. Elas podem ou não retornar objetos.


Sintaxe:
def nome_funcao(parametro1, parametro2):
        
        return valor 

Ex:
semestre = {'3º': ['AAP', 'ED1', 'Redes', 'RAD1'], '4º': ['ED2','ES2','SI','ED2']}
 
def imprime_semestre(self):
    for materias in self.items():
            print materias
 
imprime_semestre(semestre)
>>>
('3º', ['AAP', 'ED1', 'Redes', 'RAD1'])
('4º', ['ED2', 'ES2', 'SI', 'ED2'])


Percebam duas coisas, a primeira é o funcionamento da função que não tem retorno, se tivesse retorno ela só funcionária se passado à uma variável auxiliar.

Outra coisa é o funcionamento do argumento da função.

Funções e Classes em Python trabalham com o self que é definido sempre para o primeiro argumento que se tem passado para a ela.

Como somente passamos o dicionário semestre para a função, dentro dela ele pode ser referenciado como self.

Exemplo com retorno:
def calcula_imposto_federal(vlrProd):
    imposto = 999 #%
    return vlrProd * imposto
 
#Produto custa R$ 10.00
vlrComImposto = calcula_imposto_federal(10)
print 'R$', vlrComImposto
>>>
R$ 9990


Outra coisa legal que Python tem é ARGUMENTO PADRÃO, um Argumento que se não for passado, assume um valor declarado na função.
Ex:
def aplica_multa(valor, taxa=0.1):
        return valor + valor * taxa
 
print aplica_multa(10)
>>>
11.0
 
print aplica_multa(10, 0.5)
>>>
15.0

Interessante né?


Classes:

Como já dito antes, tudo em Python é um objeto, tipos, valores, classes, funções, métodos e instancia, todos possuem atributos e métodos associados.
E a estrutura fundamental para de criar novos objetos é obviamente a CLASSE.
Como vocês já sabem, uma classe tem um nome, um conjunto de atributos e seus métodos.

Sintaxe:
class calculadora:
        a = 10
        b = 20
       
        def clacula_soma(vlr1,vlr2):
                return vlr1 + vlr2
               
        def clacula_multiplicacao(a,b):
                return vlr1 * vlr2
               
        def __init__(a, b)
                print 'A soma é ', clacula_soma(a, b)


Quanto aos métodos, agora vemos um novo, __init__. Este é um método muito especial em Python pois ele na verdade é o método CONSTRUTOR, um método opcional que é executado quando esta classe for instanciada.
Ele é equivalente ao Main do Java.

Ex: Se você usar o Qt(biblioteca para interface, tipo o Swing ou JavaFX) montar a tua tela, pegar o código e quiser visualizar ele, tu vais ter que fazer o método __init__ no teu arquivo.py para dizer que vai instanciar um objeto com essa tela. E posteriormente usar o método .show().






Bom, já está bem extenso, isto que era pra ser "apenas" um post! :D


Trabalhando Com Python, Terminal interativo ou IDE:


Tem duas formas de trabalhar com a linguagem, a mais básica e para poucas aplicações que é o terminal interativo. É a mesma coisa que codificar no terminal.
E a maneira profissional que é utilizando uma IDE para isto. Existem várias no mercado, mas eu recomendo a que eu utilizo, :D, Spyder. É completa, pode-se trabalhar com pacotes, intuitiva e simples!
https://code.google.com/p/spyderlib/


Todo arquivo "executável" de Python tem seu formato atribuído ao .py
Todos os códigos descritos aqui podem ser testados online mesmo, sem precisar instalar o Python em suas maquinas(Linux já vem na distribuição), o Snape indicou um interpretador Online: http://repl.it/languages/Python



Obs: Todos os Códigos que possuem >>> Significa que a linha à seguir vem com uma resposta da execução, então na hora de passar para o interpretador remover o >>> e apagar as linhas abaixo, pois elas não fazer parte do código e sim a resposta do console de execução.


Qualquer dúvida, sugestão, reclamação, voadora, ..., estou aberto a ouvir, afinal ainda estou aprendendo, assim como vocês! hehe Erros podem ter escapado da minha percepção.


Dedico este post/artigo ao grande Milhouse que me incentivou a começo e ao Carlos Rocha que me apresentou a esta linguagem!