terça-feira, 6 de maio de 2014

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/