domingo, 3 de dezembro de 2017

Projetando aplicações angulares - AngularJS 1.x

Olá ! Meu nome é João Paulo Maida e bem-vindos ao blog Preciso Estudar Sempre.

Em um mundo de constante evolução e velocidade é necessário saber mudar e se adaptar. Tais características se tornaram decisivas nossas vidas profissionais, e assim como é o mercado de trabalho, também é a vida dos sistemas web. A vinte anos atrás o acesso a internet era raríssimo, era considerado privilégio e os sites eram escassos. Dez anos depois, a internet já era uma realidade assim como a banda larga, jogar e ver vídeos já era possível.

A construção de software voltado para a web também teve uma mudança absurda. A um tempo atrás desenvolver uma aplicação simples com algumas operações requeria uma certa quantidade de páginas HTML, alguns controladores implementados em uma linguagem de programação, usava alguns frameworks e um pouco de JavaScript para controlar o DOM. Se você fosse um cara bom, aprendia até JQuery e fazia a “roda rodar” mais rápida. Mas hoje, isso morreu e porque ?

Como eu disse no início do post, o mundo muda toda hora. Tudo o que tínhamos como idéia de desenvolvimento voltado para web era algo que levava muito tempo. Agora, queremos aplicações velozes, responsivas, fáceis de integrar com outros componentes, leves, multi-plataformas e testáveis. Como atingir tudo isso com velocidade ?

Eis que um novo tipo de aplicações surge: as Single Page Applications. Este novo tipo define que aplicações terão somente uma página aparente ao invés ao invés de várias, como as que conhecíamos até o momento usavam. Quando uso o termo aparente, me refiro a experiência do usuário. Ele terá a impressão de que está em uma única página, assim como são os aplicativos mobile e as aplicações desktop que possuem praticamente uma tela única. Obviamente sabemos que internamente existem várias páginas interagindo e manipulando conteúdo, e é aí que entra uma característica muito forte deste novo tipo, o uso em massa de requisições assíncronas.

Evidentemente estamos falando de AJAX e a esta altura do campeonato esta técnica não é mais uma surpresa. Através deste recurso é possível passar a ideia de que existe somente uma única página responsável por tudo, mas na verdade são várias requisições assíncronas sendo feitas, via JavaScript, para obter conteúdo, trabalhando em conjunto com outras páginas. A consequência disso é um não carregamento total da página, como já conhecido na requisições síncronas, provendo assim uma experiência melhor e uso mais fluido para o usuário. Agora não há mais necessidade de que o usuário tenha que pausar por completo o seu trabalho, pois enquanto um recurso é carregado ele pode executar outras tarefas.

Uma outra característica bem marcante das SPAs é o uso intenso de JavaScript, e isto pode ser justificado pela modernidade dos browsers lançados atualmente e pelo aumento da oportunidade de acesso à internet nos últimos anos. O primeiro fenômeno resulta em um maior poderio de processamento da linguagem, e com uma linguagem mais veloz o interesse em se apoiar mais nela para o desenvolvimento de sistemas aumenta, onde tudo isto é causado pela demanda crescente de acesso à internet. Lembre-se: a primeira porta de entrada para a rede mundial de computadores é um navegador.

Devemos considerar também que o cenário desta linguagem mudou bastante desde de sua concepção. Como já citado acima, antigamente era somente usada para validações de formulário e manipulações do DOM, mas hoje em dia o leque de opções foi expandido devido às inúmeras criações de toda uma comunidade de usuários interessados em recursos mais poderosos e à própria modernização da linguagem.

Uma dessas criações foi a concepção de frameworks, como o AngularJS, que mudam a forma de se construir aplicações para a web e dão vida às Single Page Applications. Este framework possui características bem marcantes como o uso do padrão de projeto MVC como pilar, a incorporação de diversos paradigmas e padrões de projeto, componentização, o alto foco em reuso de código, contextos e um data-binding espetacular. O projeto atualmente tem o suporte da Google, o que lhe dá uma ótima visibilidade para o mercado.

Abaixo temos um exemplo bem simples de uma aplicação AngularJS, que traz conceitos primordiais sobre o funcionamento do framework e analisaremos parte por parte dela para que não façamos confusão.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html lang="pt" ng-app>
    <head>
        <meta charset="utf-8" /> 
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
        
        <!-- Angular v1.6.6 -->
        <script src="angular/angular.js"></script>
    </head>
    <body>
        <div class="container">
            <h2>Minha Primeira Aplicação em AngularJS</h2>
            <div class="form-group">
                <label for="nome">Seu nome aqui:</label>
                <input id="nome" ng-model="nome" type="text" class="form-control" />
            </div>
            <div class="form-group">
                Meu nome é: {{nome}}
            </div>
        </div>
    </body>
    <!-- jQuery library -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

    <!-- Popper JS -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.6/umd/popper.min.js"></script>

    <!-- Latest compiled JavaScript -->
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js"></script>
</html>

O primeiro ponto que deve causar um impacto ao ser visto é o ng-app na tag <html>, após o doctype. O que seria ? Um novo atributo do HTML5 ? Não, meus amigos. Isto é tão pouco pertence ao HTML5 quanto é um atributo, isto na verdade, é uma diretiva, ou em inglês directive, do AngularJS. Impressionante, não ?!

Uma diretiva é uma forma de demarcação de limites de um contexto. Pondo em palavras mais claras, o Angular precisa definir um contexto raiz para criar sua árvore de contextos. Entenda um contexto como uma área de memória. Então, a partir do momento que assinalamos nossa tag <html> com esta diretiva, estamos dizendo ao Angular para marcar esta tag como o elemento raiz da nossa aplicação. Isto nos dá a liberdade de marcar qualquer tag ou a página toda como raiz da aplicação.

Na linha 9 fazemos o import do AngularJS. No exemplo desta postagem estamos utilizando a versão 1.6, mas nada impede que você utilize uma versão mais antiga que esta para aprender os conceitos que estamos discutindo aqui. A partir do momento em que o arquivo é carregado, um callback é registrado para ser executado quando o HTML for carregado por completo. Quando executado, o AngularJS varre a árvore de elementos do DOM à procura da diretiva ng-app. Caso encontre, ele inicializará a aplicação definindo aquele elemento como a raiz.

Dê uma olhada mais abaixo e vá para a linha 16. Mais uma diretiva do AngularJS. Um dos conceitos principais que já vimos acima é o data-binding que o AngularJS realiza. Mas o que seria data-binding ? Traduzindo ao pé da letra, data-binding significaria ligação ou conexão de dados, e é realmente isso que o AngularJS faz. Quando você estabelece uma diretiva ngModel em algum campo HTML de entrada de dados (<input>, <select>, etc.), o AngularJS em tempo de carregamento da página conecta o valor do campo de entrada, no nosso caso um <input>, à uma variável de modelo com o nome especificado dentro da diretiva ngModel e assim mantém os dois em sincronia.

Mas, calma aí, não especificamos nenhuma variável de modelo via JavaScript, como ele realiza essa conexão e mantém essa sincronia ? Ele realiza isso de forma interna, sem que você explicite. Obviamente, que em postagens posteriores veremos como criar controladores via AngularJS e nossas variáveis de modelo, mas agora nos prenderemos ao simples. Essa sincronia é conhecida como two-way data-binding, pois ela é uma via de mão dupla, ou seja, possui dois sentidos. O primeiro sentido já vimos, está na linha 16 e a segunda via está na linha 19.

Imagino que nesse momento mais uma dúvida surja porque o que aquelas chaves estão fazendo lá ? Esta é mais uma característica do AngularJS, conhecida como double-curly binding, e é o segundo sentido de que havíamos conversado. Em suma, representam a outra ponta do two-way data-binding e tornam a sincronia possível. Também aceitam expressões nos moldes JavaScript, que são avaliadas pelo AngularJS e tem seu resultado inserido no DOM. Então, no nosso exemplo, a partir do momento que alterarmos o valor da caixa de texto, uma atualização será feita e exibida abaixo, sem a necessidade de que tenhamos que nos preocupar com isso.

Por último temos que entender como os escopos do AngularJS funcionam porque isso será de grande importância quando você estiver construindo suas próprias aplicações. Existem três coisas muito importantes que acontecem durante a inicialização do AngularJS, são elas:

  • O injetor ou injector usado para a injeção de dependências é criado.
  • O injector criará o escopo raiz da aplicação que se tornará o contexto para o modelo da sua aplicação.
  • O AngularJS irá então compilar o DOM começando no elemento raiz marcado com ngApp, processando quaisquer diretivas e conexão de dados (data-binding) encontradas no caminho.
Uma vez que a aplicação foi inicializada, ela irá então esperar por eventos inesperados do browser que podem mudar o modelo. Uma vez que o evento ocorra, o AngularJS detecta se ele causou alguma mudança no modelo e se alguma mudança for encontrada, o AngularJS irá refletir ela na camada de visão através da atualização de todas as conexões feitas.

De fato, utilizar este framework para o desenvolvimento de SPAs é um fator que torna o processo de construção de um sistema bem veloz, pois como vimos com este simples exemplo, é possível fazer bastante com pouco. Porém, sua curva de aprendizado é bem pesada pois além de trabalhar com diversos padrões de projeto, como factory, injeção de dependências, inversão de controle, MVC, e etc., ele lida com invocações de web services, componentização, entre outros. Então, meu conselho para você que não tem esses conhecimentos tão amadurecidos na sua cabeça é, estude primeiro a teoria para não sair por aí construindo Frankensteins que irão te perseguir até o seu túmulo.

Evidentemente que eu dei uma limpada nos detalhes do framework, mas se eu trouxesse todos eles, a leitura se tornaria impraticável. Por tal motivo recomendo, uma leitura no guia de desenvolvimento ou no roteiro da API.

Como vocês já sabem, todos os links de referências e de download do projeto estão abaixo. 

Até a próxima minha boa gente ! 😘


Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários, envie um e-mail ou uma mensagem na nossa página do Facebook.

Download no GoogleDrive: clique aqui
Download no Dropbox:

E-mail: precisoestudarsempre@gmail.com
Facebook: https://www.facebook.com/precisoestudarsempre/

Referências:
Leia Mais ››

quinta-feira, 23 de novembro de 2017

Recomendação de livro: Clean Code - A Handbook of Agile Software Craftsmanship

Olá ! Meu nome é João Paulo Maida e bem-vindos ao blog Preciso Estudar Sempre.

O livro que venho recomendar aqui hoje é um dos integrantes do hall de ouro da literatura do desenvolvimento de software. Ele compõe a base do que eu costumo chamar de “fonte de conhecimento”. Tenho este costume pois gosto de acreditar que quando estou em contato com este tipo de obra, estou me servindo diretamente da fonte e isto, para mim, é importantíssimo.

Assim como Indiana Jones perseguiu em uma das suas aventuras a fonte da juventude eterna, acho que é de igual importância a perseguição por conhecimento puro em nossas áreas de interesse. Quando digo puro, me refiro as obras iniciais que serviram como base para outros escritores construírem, posteriormente, suas contribuições.

Não sejamos radicais nesse momento. Evidentemente não estou dizendo que as outras obras que existem por aí não servem ou não são boas. Cada trabalho tem sua contribuição no mundo, como por exemplo, o ponto de vista do autor ou a forma de escrita usada no assunto. Porém, é perigoso se ater somente a esses autores. Pego esta situação e aplico à mim.

Possuo quase dez anos na área de T.I. e passei por diversas áreas, como infra-estrutura, desenvolvimento e qualidade e arquitetura. Imagine se eu me satisfizesse somente com os primeiros livros que li. O que aconteceria ? Uma coisa é certa, eu não possuiria o conhecimento que possuo hoje e provavelmente não teria chegado onde cheguei. Ter lido diversas obras sobre o mesmo assunto ou sobre coisas que eu já sabia, deu a mim o mecanismo da crítica e análise, pois eu conheci vários pontos de vista diferentes.

Isto é de imensa importância. Imagine um profissional com trinta anos de carreira onde ele conhece só uma perspectiva do que faz ? Evidentemente é uma pessoa limitada. Dele ou dela não virão soluções fantásticas e o motivo é claro. Quando não há uma preocupação em se beber direto da fonte criadora do assunto, possuir um raso entendimento e, consequentemente, limitado, é uma questão de tempo.

O livro Clean Code - A Handbook of Agile Software Craftsmanship é das fontes quando o assunto é código limpo. Ele introduz no leitor a idéia do software como uma criação artesanal e cíclica. Isto se traduz em tratar um software com o mesmo cuidado e esmero que um artesão trata sua obra de arte. O resultado deste processo é um código limpo e de alta qualidade, pois a construção de um requer uma análise repetitiva, ou seja, o que você fez hoje pode melhorar amanhã.
Figura 1 - Capa do livro

Apontar um único ou um grupo de capítulos seria complicado porque todos são excelentes. Eu li da primeira até a última página, e em todas eu aprendi técnicas e vi reflexões que me deixaram de boca aberta. O que mais me impressionou nesta leitura foi o toque humano que o autor possui em abordar os problemas. Quando digo “humano”, quero dizer que ele narra as dificuldades e dúvidas que teve de uma forma que me faz pensar que eu já passei o mesmo, me identificando assim com ele.

Isto é deveras importante, pois quando se lê uma obra de altíssima qualidade escrita por uma referência no assunto, no nosso caso Robert C. Martin (a.k.a Uncle Bob), a tendência é a idolatria ao autor e um afastamento da sua imagem à dele. Contudo, a partir do momento em que você lê sobre os mesmos problemas e questionamentos que ele já passou e que você tem ou teve, pensa: “cara, ele é tão humano quanto eu”. Neste momento você se identifica e se aproxima.

Minhas considerações finais são:
  • Leia o livro todo e não somente uma vez. Leia toda vez que sentir vontade ou esquecer algo porque você sempre vai aprender algo novo ali.
  • Tente sempre procurar beber da fonte sobre um determinado assunto que te interesse, mas faça isso sem ignorar as contribuições já feitas posteriores a contribuição do criador.
  • Lembre-se que todos os gigantes da orientação a objetos (Robert C. Martin, Erich Gamma, Martin Fowler, Kent Beck, etc.) se conhecem ou se citam. Então dê uma olhada nas obras citadas ao longo do texto, você vai aprender bastante ali.
  • Tente sempre encontrar a obra em questão em seu idioma original para que você não tenha problemas com traduções, como perda de informações. Mas caso você tenha problemas com línguas estrangeiras, leia a versão traduzida, é melhor que nada.
Abraços !
Até a próxima minha boa gente ! 😘 Leia nossa postagem anterior: Aprendendo a escolher bem o que por em ordem - O algoritmo Selection Sort Dúvidas !? Sugestões ?! Críticas ou elogios ?! Deixe aí nos comentários, envie um e-mail ou uma mensagem na nossa página do Facebook. E-mail: precisoestudarsempre@gmail.com Facebook: https://www.facebook.com/precisoestudarsempre/ Canal Preciso Estudar Sempre: https://www.youtube.com/channel/UCUoW8dS38rXr0a5jWU57etA
Leia Mais ››

quarta-feira, 11 de outubro de 2017

Aprendendo a escolher bem o que por em ordem - O algoritmo Selection Sort

Olá ! Meu nome é João Paulo Maida e bem-vindos ao blog Preciso Estudar Sempre.

Ordenar é de fato uma tarefa que às vezes nos deixa de cabelos em pé. Na literatura existem vários algoritmos que podem nos ajudar, cada um com suas características. As opções são tantas que o problema muda de achar uma solução para qual solução escolher.

Como já vimos aqui em várias situações, o que vai determinar a solução necessária para um certo problema é, de fato, o problema. Logo, se velocidade é o seu problema vá para o Quick Sort, mas se o problema for lidar com partes já ordenadas, use o Insertion Sort (clique aqui para dar uma lida), e assim segue.

Um dos algoritmos primordiais ensinados, seja na faculdade ou no cursinho técnico, é o Bubble Sort (clique aqui). Ele, junto com Insertion Sort e Selection Sort, compõem uma categoria conhecida como “Algoritmos de ordenação simples”. Não vá achando que simples signifique inútil. Ao contrário, esses algoritmos servem como base para algoritmos mais complexos como Quick Sort, Merge Sort e Shell Sort. O que difere a categoria dos básicos para dos complexos são as técnicas e estruturas de programação utilizadas.

Nosso foco hoje será exclusivamente o Selection Sort para assim fechar a categoria dos simples. Abordaremos seu conceito, sua implementação e características.

Imagine que exista uma fileira de lápis que possuem vários tamanhos e você quer por eles em ordem porque, assim como eu, tem TOC.
Figura 1 - Lápis desordenados
Para ordenar esses lápis utilizando a técnica do Selection Sort marcamos o primeiro lápis (à esquerda) com um marcador indicando que aquele é o menor lápis encontrado no momento. Após isso, verificamos um após o outro até o fim, a fim de encontrar algum lápis menor. Quando encontrado, é marcado como o menor lápis encontrado até o momento, mas a busca continua até o fim do conjunto. Caso algum lápis menor ainda seja encontrado, o marcador é movido para este. Caso contrário, a troca já pode ser feita e, o ciclo de ordenação do primeiro lápis está concluído. O marcador vai para o segundo lápis do conjunto. 

No exemplo acima, o menor lápis encontrado em uma primeira instância seria o quarto, porém finalmente o sétimo seria revelado como o menor de todos. Os marcadores iriam ser movidos do primeiro para o quarto e posteriormente para o sétimo. Após finalizado o ciclo, o marcador voltaria para o segundo lápis para assim iniciar um novo ciclo, e este processo se repete até o conjunto todo estar ordenado.

A Figura 2 é um GIF animado que mostra todo o processo sendo feito.
Figura 2 - Funcionamento do algoritmo SelectionSort
Visto que já cobrimos toda a parte teórica do assunto, podemos partir para a prática.

Quem acompanha o blog sabe que eu sempre escolho a linguagem Java como alvo das minhas implementações, mas como o grande Mário Sérgio Cortela já diz:
Mudar é complicado mas acomodar é perecer (Cortella, Mário Sérgio).
Vamos implementar o nosso algoritmo em Javascript, afinal de contas é uma linguagem que funciona em todos os navegadores de todos os dispositivos, sejam eles computadores, notebooks, celulares ou tablets; e não é necessário um ambiente tão complexo de execução igual ao do Java.

1
2
3
4
5
6
7
8
9
for(out=0; out<qtd-1; out++){
    marcador = out;                             //primeiro elemento marcado
    for(ins=out+1; ins<qtd; ins++){             //varro os itens a direita procurando o menor de todos
        if(valores[ins] < valores[marcador]){   //encontrado item menor ?
            marcador = ins;                     //move o marcador
        }
    }
    _trocar(out, marcador);                     //acabaram os dois laços então posso fazer a troca
}
(LAFORE, ROBERT)

Note que o algoritmo é bem simples, não há nada extremamente complexo. Os dois laços for orquestram o processo descrito nos parágrafos acima. O primeiro começando do início e o segundo iniciando a partir do ponto de partida do primeiro, ou seja, se o primeiro laço começa em 0, o segundo começará em 1. Contudo, note que o primeiro laço só vai até qtd-1, porque ? Porque não ir até qtd ?

A resposta é simples se pararmos para analisar a situação como um todo. Com as consecutivas trocas dos valores, os maiores (no caso de ordenação crescente) são sempre jogados para o fim e o maior valor terá seu lugar trocado com alguém menor que ele, ocupando assim o último lugar. Por tais motivos não é necessário que o laço mais externo percorra todas posições do array.

Os nomes das variáveis controladoras de laço foram escolhidas devido ao fato de existirem um laço externo e um interno, logo respectivamente out e ins. O resto do código está bem claro e dispensa maiores explicações.

A ordenação por seleção executa o mesmo número de comparações que a ordenação pelo método bolha: N*(N-1)/2. Para grandes valores de N, os tempos de comparação predominarão, portanto a ordenação por seleção executa em tempo de O(n²), exatamente como a ordenação em bolha. Contudo, ela tende a ser bem mais rápida porque há poucas trocas. Para valores menos de N, a ordenação por seleção pode ser de fato mais rápida, especialmente se os tempos de troca forem muitos maiores que os tempos de comparação (LAFORE, ROBERT).

Chegamos ao fim de mais um post meus amigos. Espero que tenham gostado. Caso tenham sentido falta de algum detalhe, deixe nos comentários.

Até a próxima minha boa gente ! 😘

Leia nossa postagem anterior: Cuidado com os comentários, eles são perigosos !!!

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários, envie um e-mail ou uma mensagem na nossa página do Facebook.

Download no GoogleDrive: clique aqui
Download no Dropbox: clique aqui

E-mail: precisoestudarsempre@gmail.com
Facebook: https://www.facebook.com/precisoestudarsempre/
Canal Preciso Estudar Sempre: https://www.youtube.com/channel/UCUoW8dS38rXr0a5jWU57etA

Referências

Cortella, Mário Sérgio; Mudar é complicado? Acomodar é perecer.; https://www.youtube.com/watch?v=Ry3_5Pl8qmk

LAFORE, ROBERT; Estruturas de Dados e Algoritmos em Java; 2004
Leia Mais ››

sexta-feira, 29 de setembro de 2017

Cuidado com os comentários, eles são perigosos !!!

Olá ! Meu nome é João Paulo Maida e bem-vindos ao blog Preciso Estudar Sempre.

Mamãe sempre me disse que comentários são perigoso, e você já comentou o seu código alguma vez na vida ? Já se pegou escrevendo algo do tipo “aqui verifico se é diferente de nulo para não dar erro” ou “se condição igual true é porque idade é maior de 18” ? Se sim, não se desespere pois essa prática é muito comum para quem desenvolve software, porém não é uma das melhores e afirmar isso é o que realmente assusta a maioria das pessoas.
Duas mulheres fofocando. Uma fala no ouvido da outra e causa espanto.
Figura 1 - Cuidado com comentários, eles são perigosos !!!
Mas como um simples comentariozinho seria algo tão grave assim ? Muitos podem afirmar que o que estou dizendo é non-sense ou bobagem, já que comentários são ignorados em processos de compilação. Entretanto, devemos nos lembrar que antes de estarmos escrevendo “linguagem de máquina”, estamos escrevendo texto que outros poderão ler. De que adianta para um autor escrever um livro que ele só entende, ou escrever um livro que para cada parágrafo ele precisa explicar o que realmente ele quis dizer ?

Se ele precisou explicar através de um comentário qual era sua verdadeira intenção, então ele falhou em escrever o texto pois o próprio por si só não consegue ser expressivo bastante para passar sua própria ideia. Em miúdos, podemos concluir que um código-fonte de qualquer linguagem que falhe em passar seu próprio propósito de existência para qualquer leitor é um código defeituoso.

Neste momento espero ser apedrejado até a morte !!!
A figura mostra três homens apedrejando um outro homem de bata branca, com aparência de religioso. Existe um homem atrás de todos, com bata verde que só observa. O homem apedrejado parece pedir ao seu Deus misericórdia.
Figura 2 - Eu sendo cruelmente apedrejado
Bom, se você está lendo isto é porque ainda não fui apedrejado 👌.

Então se você se encaixa no que disse acima, você produz um código defeituoso. Um software deve ser escrito de forma que outros possam lê-lo como se fosse um livro, suavemente. As intenções de todos os elementos, desde daquela minúscula variável até aquela classe mega complexa, devem estar claras ao ponto de que com uma simples passada de olhos um outro programador que nunca tenha visto aquilo possa entender ou ter uma ideia do que está sendo feito.

Como disse no início do texto, esse assunto sempre gera muita discussão, mas é uma das principais métricas quando o assunto é código limpo. Se a sua intenção é, assim como a minha, o self-improvement, ou seja, crescimento pessoal, não se ofenda com o fato de que o código que produz não ser tão bom quanto pensava. Eu já passei pelo mesmo, me surpreendi várias vezes e reaprendi coisas que eu já achava que sabia. Acredito que faz parte passar por isso, quem consegue aprender sem errar ?

O livro que abriu minha mente para a produção de um código mais limpo e expressivo foi o Clean Code, escrito por Robert C. Martin, conhecido também pelo apelido Uncle Bob. Um grande amigo me apresentou a obra e me emprestou o seu livro em português. Comecei a ler e fiquei abismado com a riqueza de detalhes que o simples nome de uma variável pode ter. Depois de um tempo abdiquei do livro emprestado por questões de mobilidade, e baixei o PDF para meu celular. Todo momento livre que eu tenho dou uma lidinha nele, nem que seja cinco minutos. Ele é aquele tipo de livro que você tem que ler várias vezes porque no fim das contas você sempre aprende algo novo.

Existe um capítulo dedicado sobre o estudo de comentários (capítulo 4), onde são abordados assuntos como consequências de um código comentado, categorização, como programar sem comentar, bons e maus comentários, entre outros. O que estamos discutindo aqui é somente uma fração do que o livro cobre. Logo, para mais detalhes dê uma lidinha neste capítulo, vale bem a pena 😊. Deixarei um link para download no fim do post.
A capa do livro traz uma imagem de uma galáxia
Figura 3 - Clean Code escrito por Robert C. Martin
Não pense que sou contra Javadocs, sou muito a favor inclusive. Mas um Javadoc é algo completamente diferente de um comentário explicativo de uma linha de código, e mesmo assim se o Javadoc ferir algumas regrinhas que o livro aborda, então ele possui problemas. Adiante veremos um exemplo de código com o que considero um comentário ruim.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class BadComment {
    /*valores*/
    private final int[] valores = {1,2,3};
   
    private void verificarValores(){
        //verifico se existem valores
        if(valores.length>0){
            System.out.println("Tem valores !!!");
        }
    }
   
    public static void main (String[] args){
        new BadComment().verificarValores();
    }
}

Para um tempinho e olhe bem a amostra de código acima. Me diga, para que raios uma pessoa cria um comentário (linha 2) com o mesmo nome do atributo da classe ? Pra que isso serve ? Do que adianta ? Qual vantagem traz ? Sinceramente, nunca consegui responder tais questões e já vi isso acontecer várias vezes.

Calma porque ainda vai piorar !

Um outro programador começou a trabalhar nessa classe e resolveu mudar de lugar o atributo valores mas esqueceu de levar o comentário junto. Olha só o que aconteceu.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class BadComment {
    /*valores*/
    
    private void verificarValores(){
        //verifico se existem valores
        if(valores.length>0){
            System.out.println("Tem valores !!!");
        }
    }
    
    private final int[] valores = {1,2,3};
    
    public static void main (String[] args){
        new BadComment().verificarValores();
    }
}

Agora quem for trabalhar em cima dessa classe vai ficar confuso. Porque existe esse comentário na linha 2 ? O que ele está fazendo aí ? Essas são umas das perguntas que são feitas quando isso acontece. Tal fenômeno leva os programadores ao medo, pois é criado o receio de que se algo é mudado o sistema inteiro pode parar em produção. Um sistema que não pode sofrer alterações ou testes está doente.

Analise agora o comentário da linha 5 da amostra acima. Qual é a expressividade que ele nos traz ? Obviamente, ele explica o que está sendo feito e pode até ajudar em uma primeira instância. Porém, e se alguém mudar a instrução if da linha 6 de lugar ou mudar a condição ? Será que o comentário vai acompanhar a mudança ? Senão acompanhar, será que uma outra pessoa vai entender que ali havia uma verificação, ou talvez se a que existe ali condiz com o respectivo comentário ? Note que a teia de dúvidas não pára de ser tecida, está na hora de dar um basta nisso.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public class BadCommentConsequence {
    
    private final int[] valores = {1,2,3};
    
    private void verificarValores(){        
        if(temValores()){
            System.out.println("Tem valores !!!");
        }
    }        
    
    private boolean temValores(){
        return valores.length>0;
    }
    
    public static void main (String[] args){
        new BadCommentConsequence().verificarValores();
    }
}

Nossa classe deve ficar como mostrado acima. Antes de tudo, adeus para comentário de atributo, isso não faz o menor sentido. Um atributo ou uma variável deve mostrar o motivo de sua existência através de seu nome e não por um comentário.

O comentário da linha 5 também foi embora e a condição do if possui um método próprio com um nome bem expressivo, que mostra qual é o motivo dele estar ali. Agora sabemos que somente será impresso algo no console quando existirem valores. A consequência dessa refatoração é uma leitura muito mais limpa e simples.

Conclusão: Um código bem feito, limpo e enxuto passa muito significado. Esse é um dos fatores mais importantes no desenvolvimento de software e uma das maiores preocupações que devemos ter se nossa intenção é produzir um bom trabalho.

Não se preocupa em errar, todos erram ou erraram um dia. Saber que errou e permanecer no erro que é o verdadeiro problema. A construção de um bom código requer um ciclo de revisão contínuo, ou seja, o que você fez hoje pode ser melhorado amanhã .

O livro apresentado é uma das referências no assunto. Selo de recomendação de leitura do Maidão.

Terminamos mais um post !! 😀

Espero que você tenha gostado. Até a próxima minha boa gente ! 😘

Leia nossa postagem anterior: Ausência do blog e férias

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários, envie um e-mail ou uma mensagem na nossa página do Facebook.

Download no GoogleDrive: clique aqui
Download no Dropbox: clique aqui

E-mail: precisoestudarsempre@gmail.com
Facebook: https://www.facebook.com/precisoestudarsempre/
Canal Preciso Estudar Sempre: https://www.youtube.com/channel/UCUoW8dS38rXr0a5jWU57etA

Referências
Leia Mais ››

quarta-feira, 20 de setembro de 2017

Ausência do blog e férias

Olá ! Meu nome é João Paulo Maida e bem-vindos ao blog Preciso Estudar Sempre.

Você deve ter notado que eu dei uma parada nas postagens, uma sumida. Sim, eu estava de férias. Estava recarregando minhas baterias, descansando minha cabeça para voltar com potência total com novos assuntos e motivação lá nas alturas. Se você sentiu minha falta nesse período, fico feliz com sua preocupação e carinho até porque é isso que mantém nos trilhos e trabalhando.

Minhas férias foram repletas de novas descobertas, aprendi muito e me reinventei. Pensei sobre diversos pontos, um deles é a trajetória do blog. Devo confessar que senti uma pontada de vontade de engavetar o blog, dar uma parada, descansar mais. Contudo, tenho certeza de que essa seria uma péssima escolha, a qual iria me arrepender profundamente no futuro. O laço, que com muito custo consegui construir com vocês, leitores, é muito importante. Foram anos de dedicação em trazer novos assuntos e de esforço em divulgar o material aqui publicado. A consequência disso são os poucos porém valiosos comentários que tenho de pessoas reais dizendo que de fato eu ajudei elas. Isso, meus amigos, não tem preço.

Ter uma visão voltada para o futuro do blog se faz necessária nesse momento e para haver um futuro é necessário entender o passado, acredito. Um dos pilares que sustenta a idéia do blog é a vontade de se estudar qualquer assunto. Então, pense nas antigas civilizações e o quanto ainda temos que aprender com elas.

Os egípcios, por exemplo, são mestres na arte da engenharia e seu legado possui valor inestimável para a humidade. Contudo ainda existem questões não respondidas. Mais uma vez estamos diante do mesmo questionamento, é necessário entender o que aconteceu milhares de anos atrás para poder entender o presente e pensar em um futuro. Então é isso o que precisa ser feito aqui e agora.

Não quero me estender muito neste assunto até porque esse não é objetivo desta publicação e não temos tempo a perder. Esta declaração é apenas uma satisfação que acho que vocês, leitores, merecem por uma questão de consideração. Feito isso, me despeço e peço que aguardem pois novidades estão por vir.

Até a próxima minha boa gente ! 😘

Leia nossa postagem anterior: Um roteiro para o estudo da teoria dos grafos

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários, envie um e-mail ou uma mensagem na nossa página do Facebook.

E-mail: precisoestudarsempre@gmail.com
Leia Mais ››

quinta-feira, 3 de agosto de 2017

Um roteiro para o estudo da teoria dos grafos

Olá ! Meu nome é João Paulo Maida e bem-vindos ao blog Preciso Estudar Sempre.

Parabéns !!! 🎇🎇🎇🎈🎉🎉🎊

Você chegou ao fim de uma incrível jornada, passou e superou diversas intempéries e fez descobertas espetaculares, você merece estar aqui. Sinto neste momento muito orgulho de você.

Se esta é a sua primeira visita neste blog e não está entendendo muito bem o que está acontecendo, não se desanime e continue lendo pois logo vai entender.

Já parou para pensar que é de extrema importância existir, aqui no blog, um catálogo bem resumido e enxuto voltado para o estudo da teoria dos grafos ? Isso nunca foi feito antes. Uma forma de deixar um passo-a-passo, um guia, para as pessoas que estão perdidas e não sabem por onde começar. Se você acha que isto é uma bobeira, está enganado meu caro. Tal possui tanta importância assim como os assuntos já vistos aqui. Na verdade, na minha opinião, a internet carece de tal iniciativa a fim de facilitar a vida de pessoas que estão tentando entrar neste nova área de conhecimento.

Então, sem mais delongas, lhes apresento um guia feito por mim para o estudo da teoria dos grafos.
  1. Grafos - O início
  2. Matrizes de adjacência
  3. Busca em profundidade
  4. Busca em largura
  5. Árvores geradoras mínimas
  6. Orientação
  7. O algoritmo de Warshall
  8. Grafos ponderados
  9. O algoritmo de Dijkstra
Se você quer um entendimento sólido e básico sobre o assunto, recomendo fortemente que siga de forma ordenada as etapas enumeradas. Todas foram feitas com muito carinho e esmero. Explicações detalhadas, exemplos e imagens são apenas uma mera fração da imensidão de conhecimento contido nestes tópicos.
 
Caso você seja um amante da matemática assim como eu, especialmente da teoria dos grafos e fique triste por termos chegado ao fim desta série. Entendo sua situação. Porém pense que seria muito complicado prender o blog e seu público somente a este assunto sabendo que há uma vastidão de assuntos ainda não explorados mas nada impede que futuramente entre em pauta novamente.

Até a próxima minha boa gente ! 😘

Leia nossa postagem anterior: O princípio da responsabilidade única - S.O.L.I.D

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários, envie um e-mail ou uma mensagem na nossa página do Facebook.

E-mail: precisoestudarsempre@gmail.com
Facebook: https://www.facebook.com/precisoestudarsempre/
Canal Preciso Estudar Sempre: https://www.youtube.com/channel/UCUoW8dS38rXr0a5jWU57etA

Referências
Leia Mais ››

segunda-feira, 24 de julho de 2017

O princípio da responsabilidade única - S.O.L.I.D

Olá ! Meu nome é João Paulo Maida e bem-vindos ao blog Preciso Estudar Sempre.

Pare por um instante e reflita sobre a seguinte frase:


Uma classe deve ter somente um único motivo para mudar. (MARTIN, ROBERT; MARTIN MICAH)


Este é o princípio da responsabilidade única (Single-Responsibility Principle - SRP em inglês) e se você for um iniciante no mundo da programação e achar esta declaração estranha, não se espante. Este conceito não é banal e tampouco é ensinado na faculdade, logo sua reação é normal. Na verdade, são poucos os programadores que sabem do que estou falando e isto de fato é preocupante, visto que o conceito que iremos discutir aqui hoje forma um dos pilares do que realmente a orientação a objetos trata.

Voltando à frase, conseguiu pensar ? Conseguiu ver a quantidade de conhecimento preso em uma única frase ? Não !? Bem, minha meta é abrir sua mente para o verdadeiro desafio de se construir software orientado a objetos.

Análise a situação abaixo.
Figura 1 - Modelo de classes acoplado

A classe AbridorDeGarrafas tem como responsabilidade abrir garrafas, sejam elas com tampa metálica, usadas nas garrafas de cerveja, ou com rolha de cortiça, usadas nas garrafas de vinho. Os objetos Taverna e Adega dependem destes métodos para funcionar. Queremos realizar uma modificação na implementação do método de abrir garrafas com tampa metálica sem causar impacto no funcionamento do método que abre garrafas com rolha. Será que isto é possível ? Não é preciso muito tempo para ver que a resposta é não porque será necessário recompilar, retestar e reimplantar todo este modelo para que esta mudança não resulte em problemas posteriores.

Então como alterar algo sem mexer no que já funciona ? Lembre-se que hoje somente o objeto Adega depende de AbridorDeGarrafas, mas amanhã pode existir uma gama de objetos com esta mesma dependência. Então, o que fazer ?

Isto tudo acontece por um único motivo, a classe AbridorDeGarrafas possui mais de 1 responsabilidade e tal nos leva a um modelo altamente acoplado, onde as mudanças feitas em um método afetam o funcionamento do outro. Isso é péssimo e não pode continuar 👎. Tudo isto fere o princípio da responsabilidade única, então a pergunta que nos sobra é: como corrigir o problema ?

Simples ! Devemos separar as responsabilidades da classe AbridorDeGarrafas. Entenda como responsabilidade uma razão para mudar e aqui existem duas para isso, são elas: atender solicitações de abertura de garrafas com tampa metálica e atender solicitações de abertura de garrafas com rolha. Entender essa separação é crucial para a montagem de um design que é ao mesmo tempo robusto e flexível, mas separar não significa que uma classe só deve conter um único método. Ao contrário, ela pode conter quantos métodos quiser desde que todos estejam relacionados a mesma responsabilidade. A partir do momento em que identificamos uma nova responsabilidade, a separação deve acontecer. Eis como o nosso modelo fica.
Figura 2 - Modelo de classes altamente coeso

Agora temos duas classes, uma que só abre garrafas com tampa metálica e outra que só abre garrafas com rolha e cada uma possui um método chamado abrirGarrafa(). Não há mais necessidade de especificar no nome do método que aquela abertura de garrafa é com tampa metálica ou de rolha, pois o nome da classe já tem essa expressividade. É dedutível que o abrirGarrafa() da classe AbridorDeGarrafasComTampaMetalica só abre garrafas com tampa metalica. Dessa forma podemos realizar quais mudanças quisermos sem afetar o que já funciona e isto é ótimo 👍.

Contudo, ainda falta um ponto para se pensar. Sempre que identificarmos mais de uma responsabilidade ela deve ser separada ? Levantar essa dúvida a esta altura pode parecer antagônico, mas não é. Este princípio é um dos mais difíceis e todos os outros da plataforma S.O.L.I.D se remetem à ele, logo esta indagação faz sentido e para respondê-la devemos levar em conta o contexto. Se uma alteração implica em mudar o todo não há necessidade de separar responsabilidades, pois para aquele contexto aquilo consiste uma única responsabilidade. Profundo, não !? 😕

Considere o seguinte exemplo: suponha que ao invés da mudança ser na abertura de garrafas com tampas metálicas, ela seria a adição de um mecanismo que melhora o encaixe do abridor na garrafa. Realmente deveríamos separar as responsabilidades aqui visto que a alteração afeta o todo ? A resposta é não, logo não faz sentido. Não existe uma fórmula mágica para todas as situações, cada uma deve ser avaliada separadamente.

Chegamos ao fim de mais um post. Nas próximas publicações veremos mais princípios da plataforma S.O.L.I.D e como eles se complementam. Como já é de costume, se inscreva no blog e no canal do youtube para ficar por dentro das novidades, indique para seus amigos e compartilhe em suas redes sociais.

Até a próxima minha boa gente ! 😘

Leia nossa postagem anterior: O algoritmo de Dijkstra - Grafos

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários, envie um e-mail ou uma mensagem na nossa página do Facebook.

E-mail: precisoestudarsempre@gmail.com
Facebook: https://www.facebook.com/precisoestudarsempre/
Canal Preciso Estudar Sempre: https://www.youtube.com/channel/UCUoW8dS38rXr0a5jWU57etA

Referências

MARTIN, ROBERT; MARTIN MICAH; Agile Principles, Patterns and Practices in C#; 2006
Leia Mais ››

sexta-feira, 14 de julho de 2017

O algoritmo de Dijkstra - Grafos

Olá ! Meu nome é João Paulo Maida e bem-vindos ao blog Preciso Estudar Sempre.

Você já pensou em alguma vez fazer uma daquelas viagens como mochileiro que você sai sem rumo de casa, só com mochila e dinheiro para a passagem do ônibus ? Seu destino final você decide a cada parada. Hoje você pode estar saindo do Rio de Janeiro e amanhã estar amanhecendo em alguma cidade do interior de Minas Gerais. A partir de lá você pode decidir ir para São Paulo, e de lá para o Mato Grosso. Em cada cidade que você pára, avalia quais podem ser seus destinos e quais são os mais baratos. Até porque para este tipo de viagem a grana é sempre curta.

Devo admitir que este tipo de aventura sempre foi um sonho para mim, mas acho que dificilmente um dia irei realizá-lo. Se você um dia já se aventurou de tal forma ou pensa em se aventurar, sabe que seria interessante ter uma relação das conexões das cidades baseadas em seus custos de transporte onde esses são baseados em suas distâncias, exemplo: ir do RJ para SP custa R$ 80, do RJ para MG custa R$60, de MG para MT custa R$30 e por aí vai. Com essa relação em mãos fica fácil decidir qual caminho tomar gastando pouco com passagem. Seria um sonho, não ?

Para nossa alegria e felicidade, este sonho sim é possível atingir. Mais uma vez a teoria dos grafos chega para salvar o dia com um algoritmo que nos dá exatamente a resposta que estamos procurando. Apresento o algoritmo de Dijkstra. A criação de Edsger Dijkstra é baseada na representação da matriz de adjacência de um grafo e não somente encontra o caminho mais curto a partir de um nó especificado até outro, como também os caminhos mais curtos do mesmo nó especificado até os outros (LAFORE, ROBERT).

Imaginemos a nossa viagem aventureira representada pela Figura 1 e não se prenda a detalhes de geografia.
Figura 1 - Mapa das cidades

Você está partindo do Rio de Janeiro e quer saber quais são os caminhos mais baratos para as próximas cidades, para assim chegar ao seu destino final pelo melhor caminho. Você sabe que em cada cidade que pára existe um agente da empresa de ônibus e que ele pode te informar os trajetos e tarifas. Então, você pega seu bloquinho (todo bom viajante deve ter um) e faz uma tabelinha com essa relação para que no final você tenha todas as informações “mastigadas”.
Tabela 1 - Tabela das viagens mais baratas
Então, assim como eu, você é um amante da teoria dos grafos e pensa: as cidades podem ser os vértices de um grafo e os trajetos entre elas as arestas. Eu sei que cada trajeto tem uma direção e um preço de passagem, então isso pode ser respectivamente a direção e o peso das arestas. No fim das contas, eu tenho um grafo ponderado orientado. Isso é ótimo !

É aí que entra o algoritmo de Dijkstra. Ele pega o seu grafo, analisa, e como resultado gera uma árvore. Esta árvore segue os conceitos básicos de uma árvore mas não é como uma árvore geradora mínima, a qual já vimos (clique aqui). Ela é diferente porque é montada levando em conta o somatório dos pesos a partir do nó inicial.

Abaixo estão relacionadas as etapas do algoritmo. Os detalhes de cada uma serão vistos mais à frente. Com o entendimento dessas etapas, entender o algoritmo em Java fica muito mais fácil.

Etapas do algoritmo:
  1. Lembra da tabelinha da relação dos caminhos mais baratos ? Aqui ela toma a forma de um array, onde seu tamanho reflete a quantidade de vértices do grafo, cada posição representa um vértice e inicialmente é preenchido por completo com um valor muito grande (veremos o motivo disso mais à frente) que chamaremos de INFINITY. Chamaremos este array de array de menor caminho.
  2. Após montado, o nó inicial, ou seja, nosso ponto de partida é analisado. São adicionados no array de menor caminho somente os vértices que são adjacentes ao nó inicial. Essa adição tem a forma de um objeto com informações sobre o vértice antecessor ao vértice analisado e o custo para chegar até ele. É necessário ter informações sobre o antecessor pois precisamos saber como chegamos ali (entenderemos esse ponto melhor a frente). Porque é assumido que estes vértices representam os melhores caminhos a partir do vértice inicial e podem ser inseridos no array de menor caminho ? A resposta é óbvia. Se os nós que são inseridos no array são aqueles adjacentes ao nó inicial, logo não existe nada que separe-os e assim se tornam os melhores custos neste trajeto. É importante citar que essas adições são feitas por cima dos valores que já existem em uma determinada posição do array.
  3. Lembra que o resultado do algoritmo de Dijkstra é uma árvore ? Então, já podemos adicionar o primeiro nó (início) na árvore.
  4. A partir dos nós restantes, verificamos 1 por 1 para averiguar qual tem o menor custo a partir do início.
  5. Caso não haja nenhum é porque o nó inicial não tem conexões, ou seja, é inatingível ou todos já estão na árvore. Caso contrário, o nó com menor custo (adjacente ao inicial neste caso) é selecionado.
  6. Uma vez selecionado, é adicionado na árvore e o array de menores caminhos é atualizado.
  7. A atualização tem forma parecida com a adição citada no passo 2, ou seja, a mesma estrutura de objeto é citado. Consiste em analisar se o somatório de valores do início até o nó selecionado é menor que o valor já registrado. Isto deve ser feito por causa do passo 2.
  8. Após esses passos terminados, o array de menor caminho está finalizado.
Após a aplicação do algoritmo, nossa tabela deverá ficar assim:
Tabela 2 -Tabela de viagens mais baratas preenchida

Através da Tabela 2 fica claro porque precisamos da informação do antecessor ? Com ela em mãos conseguimos refazer todo o caminho feito desde do nó inicial, ou seja, agora eu sei que o melhor caminho para a cidade de Bonito no estado do Mato Grosso do Sul é através de Ribeirão Preto em São Paulo e que o melhor caminho para este é via São Paulo capital, e para este o melhor é caminho é vir diretamente de Rio de Janeiro capital. Tudo mais claro agora não !?

Agora veremos a implementação desse algoritmo em Java e como já é de costume, é necessário dizer que é possível implementar este algoritmo em qualquer linguagem de programação. A linguagem Java foi escolhida somente por uma questão de afinidade.

O código abaixo é um dos mais difíceis já mostrados aqui no blog, mas não se espante que com calma tudo fica fácil. Não mostrarei o código inteiro aqui porque tal tornará a leitura complicada, visto que o que realmente importa são as três funções chave as quais compõem o algoritmo de Dijkstra.

findAllShortestPaths()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public void findAllShortestPaths(){
    int startTree = 0;
    vertexList[startTree].setIsInTree(true);                                //adiciono o primeiro vértice do grafo na árvore
    nTree = 1;                                                                //mudo o contador porque acabei de adicionar um nó na árvore

    for (int i=0; i<nVerts; i++) {                                            //inicializo o array de menores caminhos com as distâncias
        int distance = adjMat[startTree][i];                                //das adjacências do primeiro vértice
        shortestPathArray[i] = new ShortestPathObject(startTree, distance);
    }

    while (nTree < nVerts) {                                                        //até todos os nós estarem na árvore
        int shortestVertex = getMin();                                                //seleciona o vértice que tem o valor mínimo, essa função provê
        int shortestDistance = shortestPathArray[shortestVertex].getDistance();        //a direção que o algoritmo vai seguir

        if(shortestDistance == INFINITY){                                            //se todos forem infinito ou todos na árvore
            System.out.println("There are unreachable vertices");
            break;
        } else {
            currentVert = shortestVertex;
            startToCurrent = shortestDistance;
        }

        vertexList[currentVert].setIsInTree(true);                                    //coloca o vértice atual na árvore
        nTree++;                                                                    //mais um nó entrou na árvore
        updateShortestPathArray();                                                    //atualizo os valores mínimos
    }

    displayPaths();                                                                    //exibo todos os nós

    nTree = 0;                                                                        //limpa a ávore
    for (int i=0; i<nVerts; i++) {
        vertexList[i].setIsInTree(false);
    }        
}

Aqui é onde acontece toda a mágica. Como existem muitos detalhes que devem ser vistos, esta função divide essa responsabilidade com duas outras funções: getMin() e updateShortestPathArray(). Tentei deixar o código o mais explicado possível através de comentários, nomes de variáveis e métodos. Por causa disso não me estenderei em explicações aqui.

getMin() e updateShortestPathArray()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private int getMin(){
    int shortestDistance = INFINITY;
    int shortestVertex = 0;

    for (int i=1; i<nVerts; i++) {                                                                    //passo por todos os nós
        if (!vertexList[i].isInTree() && shortestPathArray[i].getDistance() < shortestDistance) {    //analiso se o nó já está na árvore e se a distância dele
            shortestDistance = shortestPathArray[i].getDistance();                                    //é a menor. Se for, atualizo as variáveis
            shortestVertex = i;
        }
    }
    return shortestVertex;
}

private void updateShortestPathArray(){
    int column = 1;
    while (column < nVerts) {                                                        //percorre todas as colunas da "tabelinha"
        if (vertexList[column].isInTree()) {                                        //se a coluna da "tabelinha" já estiver na árvore não há
            column++;                                                                //de atualizar sua entrada no array de valores mínimos
            continue;
        }

        int currentToTarget = adjMat[currentVert][column];                            //variável que guarda o valor da distância do nó atual(RJ) até um nó de destino que não está na árvore, nem sempre esse vértice de destino tem uma conexão de fato
        int startToTarget = startToCurrent + currentToTarget;                        //variável que guarda a distância total do início até o destino
        int shortestDistance = shortestPathArray[column].getDistance();                //recupera o valor do array de valores mínimos

        if (startToTarget < shortestDistance) {                                        //essa comparação é essencial pois se não há uma conexão de fato entre dois vértices, a soma total será INFINITY + algum valor de aresta e este é menor que somente INFINITY, logo não há necessidade de atualização nesse caso.
            shortestPathArray[column].setParentVert(currentVert);                    //caso a soma seja de valores que realmente possuem conexão (RJ-SP-RP-BN) ela será menor que INFINITY e logo há porque atualizar
            shortestPathArray[column].setDistance(startToTarget);
        }
        column++;
    }
}

Entender o que essas funções fazem é essencial para um entendimento total da implementação do algoritmo. Elas possuem tanta importância quanto a função findAllShortestPaths(). Basicamente, a função getMin() decide a direção de exploração no grafo escolhendo o nó com o melhor (menor no nosso caso) custo possível que ainda não está na árvore. Outro detalhe importante é que a ação de adicionar um vértice do grafo em uma árvore apartada é o fator determinante para que aquele vértice não seja mais analisado pelo algoritmo.

A função updateShortestPathArray() atua na atualização do array de menores caminhos e avalia se o custo de um trajeto inteiro vale a pena ou não de ser computado como ou não como um melhor caminho. Sem essa função não teríamos os resultados que tanto desejamos. No fim das contas, o que esperamos é a Tabela 2, já mostrada acima, e a Figura 3 que representa a árvore gerada pelo algoritmo.
Figura 3 - Árvore montada pelo algoritmo de Dijkstra
 Com isso fechamos a série Grafos do blog. Se olharmos para trás veremos que falamos e aprendemos muito. Começamos de forma humilde para que no fim pudéssemos fechar de forma triunfal. E a idéia sempre foi essa, discutir e aprender cada vez mais. Se você chegou até esse ponto da leitura fico feliz, espero ter feito a diferença e agradado.

Até a próxima minha boa gente ! 😘

Leia nossa postagem anterior: Você já parou para pensar sobre controle transacional ?

Download do código-fonte
Github: https://github.com/PrecisoEstudarSempre/Graphs.git

Dúvidas !? Sugestões ?! Críticas ou elogios ?!

Deixe aí nos comentários, envie um e-mail ou uma mensagem na nossa página do Facebook.

E-mail: precisoestudarsempre@gmail.com
Facebook: https://www.facebook.com/precisoestudarsempre/
Canal Preciso Estudar Sempre: https://www.youtube.com/channel/UCUoW8dS38rXr0a5jWU57etA

Referências

LAFORE, ROBERT; Estruturas de Dados e Algoritmos em Java; 2004
Leia Mais ››

sexta-feira, 23 de junho de 2017

Você já parou para pensar sobre controle transacional ?

Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar.

Começo este post com a seguinte pergunta: você já parou para pensar sobre controle transacional ? Já tirou cinco minutos do seu dia para pensar no assunto ?

Minha pergunta pode parecer descabida ou até um pouco sentimental pois não fiz nenhuma apresentação sobre o tema. Contudo, faço essa pergunta porque em minha vivência de mercado já me deparei com muitos programadores que não tem a mínima idéia sobre o assunto. Obviamente eles sabem o que é uma transação, um commit ou um rollback. Porém pouquíssimos sabem implementar estruturas de controle transacionais do zero.

Interessante !

Tal fato além de ser extremamente preocupante na minha opinião, ocorre porque este tipo de questionamento não é mais necessário no mercado de trabalho atualmente. A maioria senão todos os projetos utilizam tecnologias que através da inversão de controle (IoC) implementam seu próprio controle transacional. Como resultado, oferecem interfaces bem definidas e limpas para seus usuários, tornando assim trabalho deles mais fácil e ágil. Aí está o ponto que me causa preocupação, o excesso de facilidade. Excesso o qual tende a tornar as pessoas, no caso desenvolvedores, mais preguiçosos causando um esquecimento de conceitos importantes.

Se você acabou de ler o parágrafo acima está pensando que estou falando de tecnologias como Spring, EJB 3 ou Hibernate. Acertou, estou falando exatamente disso ! Mas não vá pensar que estou condenando o uso de tais ferramentas. Pelo contrário, acho super correto utilizá-las e se você não as conhece, recomendo que estude-as. Entretanto, nunca se esqueça disso: não fique preso somente ao funcionamento das ferramentas existentes, entenda como elas funcionam ! O ponto central do meu raciocínio é justamente este, é necessário entender como “por detrás dos panos” o Spring, por exemplo, consegue executar rollback em uma transação inteira, onde nesta várias tabelas sofreram operações. Obviamente não podemos esquecer que ele não somente consegue fazer isso, mas como ainda disponibilizar uma forma que você escreva sua camada DAO (Data Access Object) de forma não acoplada a sua fachada de negócio, ou serviço.

A esta altura do campeonato você já deve ter notado que esta não é uma discussão trivial. Estamos lidando com uma situação arquitetural complexa com vários padrões de projeto, conceitos e preocupações. Logo algumas amostras de código e um diagrama de classes poderiam ajudar a trazer para a realidade o que estou propondo.
Figura 1 - Diagrama de classes proposto
É importante deixar claro aqui que o modelo proposto não é a solução para todos os problemas do universo. Como sempre digo, não existem balas de prata na computação. Talvez você leia esta postagem, encontre vários erros e monte um modelo melhor. Isso acontece, soluções desse tipo são de caráter evolutivo. Então, fique a vontade para contribuir.

Começaremos a explicação deste modelo analisando a classe DAO. Como o nome já mostra, ela segue o padrão DAO (Data Access Object), logo sua responsabilidade é lidar com abstrações de acesso à dados na base de dados. Trabalhando em conjunto com ela existe a classe HelperDAO, e como o seu nome também deixa claro, ela é uma classe auxiliar. Sua responsabilidade é a de prover métodos auxiliares de fechamento de statements e result sets. A interface ConnectionInjector possui um único método, responsável pela injeção da instância do objeto de conexão de banco de dados na classe DAO. Esta injeção de fato ocorre pela implementação da interface pela classe. Mais a frente entenderemos o motivo disso.

As classes que tratam o negócio são representadas pela classe BusinessFacade e é nela que todo o controle transacional acontece. Ali deve estar o nosso foco. Neste momento você pode estar se questionando da seguinte forma: se um contexto transacional é relacionado diretamente a operações de banco de dados, porque implementá-lo na camada de negócio ? Isto não tornaria a implementação altamente acoplada ?

Não te condeno por tal dúvida, pois eu já a tive algum tempo atrás, mas refletindo sobre assunto cheguei a conclusão de que eu estava errado. O negócio é o elemento que dita o que deve estar ou não na transação, por exemplo: toda vez que um usuário for cadastrado no sistema X, ele deve ser imediatamente cadastrado na lista de usuários que tem dívidas em aberto. Então em nossa transação devem existir instruções de escrita nas tabelas de usuários e de devedores. Caso haja algo de errado nesta operação como um todo, todas as operações devem ser desfeitas. Agora imagine se para cada operação uma nova transação fosse aberta, como controlar todo o contexto de negócio ? Se uma operação falhar como comunicar a próxima ou a anterior ? Essas são as reais preocupações que devem tomar a mente de um desenvolvedor/arquiteto quando ele planeja montar seu contexto transacional.

Para que a classe de negócio não tenha mais de uma responsabilidade, fato que fere o primeiro princípio da S.O.L.I.D, ela utiliza a ajuda de uma classe helper, ou seja, uma classe auxiliar. É aí que a classe HelperTransactionalControl ganha espaço, pois nela estão concentradas todas as funções necessárias para se criar ou não um contexto transacional, e caso criado, controlá-lo. Lembre-se que leituras em banco podem ser feitas e não há a necessidade de uma transação para isso. 

Um ponto interessante nesta classe é o método injectConnection(ConnectionInjector…). Note que ele recebe um varargs do tipo ConnectionInjector e que esta é a interface que todas as classes DAO implementam. Na implementação deste método ele faz uma chamada ao método injectConnection(Connection) da interface ConnectionInjector, ou seja, ele injeta um objeto de conexão em todas as instâncias DAO utilizadas. Então, na classe de negócio a única preocupação que deve ser considerada é a de invocar este método da classe helper, passando como parâmetro todos as instâncias DAOs utilizadas no momento. Fiz essa escolha de projeto pois não me agradava o fato de talvez passar como parâmetro, no construtor de todas as instâncias DAOs, um objeto do tipo Connection. Não é responsabilidade da classe que trata o negócio lidar com esse objeto, logo escolhi fazer isso “por debaixo dos panos”.

A interface que a classe de negócio implementa é uma simples escolha de programação orientada a interface. Com este recurso posso facilmente trocar a implementação de um contexto de negócio sem ter que me preocupar com quem já consumia informações a partir dele.

--------------------------------------------------------------------------------------------------------------------------
OBSERVAÇÃO: O varargs da linguagem Java é uma categoria de tipo de dados que se comporta como se fosse um array. Logo, se o seu método recebe um varargs do tipo inteiro, você pode passar os valores tanto separados por vírgula como por um array, exemplo: foo(1,2,3,4,5) ou foo(new int[]{1,2,3,4,5}).
--------------------------------------------------------------------------------------------------------------------------

Acredito que com as amostras de código abaixo tudo o que foi discutido ficará mais claro.

ConnectionInjector.java


1
2
3
4
5
6
7
package src.model.dao;
 
import java.sql.Connection;
 
public interface ConnectionInjector {
 void injectConnection(Connection connection);
}

Classe HelperDAO.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package src.model.dao.helper;
 
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
 
public class HelperDAO {
 
 public void closeStatement(Statement statement)
   throws SQLException {
  if (statement == null) {
   throw new IllegalArgumentException(
     "Errorcode: 102 - Statement nulo.");
  }
  try {
   statement.close();
  } catch (SQLException e) {
   throw new SQLException("Errorcode: 102 - Erro no fechamento do objeto Statement.", e);
  }
 }
 
 public void closeStatementAndResultSet(Statement statement,
   ResultSet resultSet) throws SQLException {
  this.closeStatement(statement);
  if (resultSet == null) {
   throw new IllegalArgumentException(
     "Errorcode: 102 - ResultSet nulo.");
  }
  try {
   resultSet.close();
  } catch (SQLException e) {
   throw new SQLException("Errorcode: 102 - Erro no fechamento do objeto ResultSet.", e);
  }
 }
}


DAO.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package src.model.dao;
 
import src.model.dao.helper.HelperDAO;
import java.util.List;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
 
public class DAO implements ConnectionInjector{
 private Connection connection;
 private HelperDAO helperDAO;
 
 public DAO(){
  this.helperDAO = new HelperDAO();
 }
 
 @Override
 public void injectConnection(Connection connection) {
  this.connection = connection;
 }
 
 public List findAll() throws IntegrationException {  
  PreparedStatement preparedStatement = null;
  ResultSet resultSet = null;
  List results = new ArrayList<>();
  try{
   String sql = "SELECT COLUMN1, COLUMN2, COLUMN3 FROM TABLE";  
   preparedStatement = this.connection.prepareStatement(sql);
   resultSet = preparedStatement.executeQuery(sql);
   while (resultSet.next()) {
    MyObject myObject = new MyObject();
    myObject.setMyProperty1(resultSet.getLong("COLUMN1"));
    myObject.setMyProperty2(resultSet.getString("COLUMN2"));
    myObject.setMyProperty3(resultSet.getString("COLUMN3"));
    
    results.add(myObject);
   }
   return results;
  } catch (SQLException e){
   throw new SQLException("Errorcode: 104 - Erro de SQL na listagem de todos os objetos.", e);
  } finally {
   helperDAO.closeStatementAndResultSet(preparedStatement, resultSet);
  }
 }
 
 private void insert(MyObject myObject) throws SQLException {
  PreparedStatement preparedStatement = null;
  try {
   int paramPos = 0;
   String sql = "INSER INTO TABLE (COLUMN1, COLUMN2, COLUMN3) VALUES (?,?,?)");
   preparedStatement = this.connection.prepareStatement(sql);
   preparedStatement.setLong(paramPos++, myObject.getProperty1());
   preparedStatement.setString(paramPos++, myObject.getProperty2());
   preparedStatement.setString(paramPos++, myObject.getProperty3());
   preparedStatement.execute();
  } catch (SQLException e) {
   throw new SQLException(
     "Errorcode: 104 - Erro de SQL no cadastro do objeto.",
     e);
  } finally {
   helperDAO.closeStatement(preparedStatement);
  }
 }
}


HelperTransactionalControl.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package src.model.business.helper;
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
 
import src.model.dao.ConnectionInjector;
import src.model.exception.IntegrationException;
 
public class HelperTransactionalControl {
 
 private static final String USER_DATABASE = "USER";
 private static final String PASSWORD_DATABASE = "PASSWORD";
 
 private Connection connection;
 
 public void openConnection() throws IntegrationException {
  try {
   Class.forName("com.jdbc.mysql.Driver");
   this.connection = DriverManager.getConnection(
     "jdbc:mysql://localhost:3306/DATABASE",
     USER_DATABASE, PASSWORD_DATABASE);
  } catch (SQLException | ClassNotFoundException e) {
   throw new IntegrationException("Errorcode: 100 - Erro na criação de uma conexão ao banco de dados.", e);
  }
 }
 
 public void injectConnection(ConnectionInjector... daosObject){
  for (ConnectionInjector daoObject : daosObject) {
   daoObject.injectConnection(connection);
  }
 }
 
 public void endConnection() throws IntegrationException {
  if (connection == null) {
   throw new IllegalArgumentException(
     "Errorcode: 102 - Connection nula.");
  }
  try {
   connection.close();
  } catch (SQLException e) {
   throw new IntegrationException("Errorcode: 102 - Erro no fechamento do objeto Connection.", e);
  }
 }
 
 public void beginTransaction() throws IntegrationException{
  try {
   connection.setAutoCommit(false);
  } catch (SQLException e) {
   throw new IntegrationException("Errorcode: 103 - Erro na abertura da transação de banco.", e);
  }
 }
 
 public void commitTransaction() throws IntegrationException{
  try {
   connection.commit();
  } catch (SQLException e) {
   throw new IntegrationException("Errorcode: 103 - Erro na commit da transação de banco.", e);
  }
 }
 
 public void rollbackTransaction() throws IntegrationException{
  try {
   connection.rollback();
  } catch (SQLException e) {
   throw new IntegrationException("Errorcode: 103 - Erro no rollback da transação de banco.", e);
  }
 }
}


BusinessFacade.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package src.model.business;
 
import java.sql.SQLException;
import java.util.List;
 
import src.model.dao.DAO;
import src.model.exception.IntegrationException;
import src.model.business.helper.HelperTransactionalControl;
 
public class BusinessFacade implements IBusinessFacade{
 
 private HelperTransactionalControl helper;
 
 public BusinessFacade() {
  helper = new HelperTransactionalControl();
 }
 
 @Override
 public MyObject insert(MyObject myObject) throws IntegrationException {
  try {
   DAO dao = new DAO();
   
   helper.openConnection();
   helper.beginTransaction();
   helper.injectConnection(dao);
   dao.insert(myObject);
   helper.commitTransaction();
   
   return myObject;
  } catch (SQLException e) {
   helper.rollbackTransaction();
   throw new IntegrationException(e);
  } finally {
   helper.endConnection();
  }
 }
 
 @Override
 public List findAll() throws IntegrationException{
  try {
   DAO dao = new DAO();
      
   this.helper.openConnection();
   this.helper.injectConnection(dao);
   return dao.findAll();
  } catch (SQLException e) {
   throw new IntegrationException(e);
  } finally {
   this.helper.endConnection();
  }
 }
}


Note que como consequência da implementação do projeto de software proposto, a classe de tratamento do negócio ficou bem enxuta e com responsabilidades bem definidas. Não achei necessário expor aqui o código-fonte da exceção especializada (IntegrationException) e da interface IBusinessFacade porque seus conteúdos são bastante triviais e iriam causar mais volume na postagem, talvez tornando a leitura mais cansativa.

Como dito anteriormente, sinta-se livre para criticar, existem várias formas de se fazer o mesmo e é isso que engrandece um projeto de software, mas se você quiser elogiar não se acanhe 😋.

Chegamos ao fim de mais um post e neste aqui devo grandes agradecimentos a um amigo e mentor, o imenso Luís Marcelo Bruckner. Muito obrigado meu amigo.

Até a próxima minha boa gente ! 😘
Leia nossa postagem anterior: Grafos ponderados - Grafos
Download do código-fonte
GoogleDrive: clique aqui 
Dropbox: 
Dúvidas !? Sugestões ?! Críticas ou elogios ?!
Deixe aí nos comentários, envie um e-mail ou uma mensagem na nossa página do Facebook.
E-mail: precisoestudarsempre@gmail.com
Leia Mais ››