Java from Scratch – Parte 04

Introdução

No post anterior (Java from Scratch – Parte 03), vimos linha a linha o que faz cada instrução do fonte do Ping-Pong 😀 Agora, vamos ver outras formas de escrever este mesmo código, e começar a usar a orientação a objetos propriamente dita.

Orientação a Objetos

As linhas de código responsáveis por fazer a movimentação da “bolinha” pela tela estão todas no método “main” da classe. Se eu quiser, por exemplo, fazer duas bolinhas (Letras “O”) passear na tela ao mesmo tempo, eu teria que — por exemplo — trocar as variáveis que armazenam a linha e coluna (row e col) atuais da bolinha, e as direções horizontal e vertical atuais (rowinc e colinc) para um tipo “Array” de inteiros, e aplicar cálculos e apagar/pintar a bolinha para os elementos desse array … Isto significaria mexer no código inteiro.

Agora, se usarmos efetivamente a “orientação a objetos”, isso fica muito, muito mais fácil. Vamos ver como, escrevendo agora a classe PingPong2, aproveitando praticamente todo o código escrito na PingPong, e acrescentando algumas “coisinhas” — criando o arquivo “PingPong2.java”

import java.util.Random;

public class PingPong2
{

  private static char escCode = 27;
  private static Random rand = new Random();

  private int row,col;
  private int rowinc = 1 ,colinc = 1;

  public PingPong2()
  {
    row = rand.nextInt(25)+1;
    col = rand.nextInt(80)+1;
    if ( rand.nextInt(2) > 0 )
      rowinc = -1 ;
    if ( rand.nextInt(2) > 0 )
      colinc = -1 ;
  }

  public void PingStep()
  {
    System.out.printf("%c[%d;%df ",escCode,row,col);
    if ( row + rowinc < 1 || row + rowinc > 25 )
      rowinc = -rowinc;
    row += rowinc;
    if ( col + colinc < 1 || col + colinc > 80 )
      colinc = -colinc;
    col += colinc;
    System.out.printf("%c[%d;%dfO%c[1D",escCode,row,col,escCode);
  }

  public static void main(String args[]) throws InterruptedException
  {
    int balls = 1;
    if ( args.length > 0 )
      balls = Integer.parseInt(args[0]);
    if ( balls < 1 )
      balls = 1;
    PingPong2[] aBalls = new PingPong2[balls];
    for ( int i = 0; i < balls; i++)
      aBalls[i] = new PingPong2();
    while (true)
    {
      for ( PingPong2 oBall : aBalls )
        oBall.PingStep();
      Thread.sleep(250);
    }
  } 
}

Agora sim, parece um “polvo” com pernas, braços e tentáculos, esse fonte virou de ponta cabeça. Acho que explicar cada pedaço vai exigir mais alguns posts … Nesse fonte usamos o primeiro “import”, uma classe com construtor , propriedades privadas estáticas e não-estáticas, e um novo método público — além no nosso “main”, que ganhou um corpo completamente diferente, que passa a receber um argumento de linha de comando, e é capaz de “animar” múltiplas bolinhas aleatórias !!!!

Testando o PingPong

Salve o fonte como “PingPong2.java”, compile com “javac PingPong2.java”, e execute com “java PingPong2”. Uma bolinha surge em um ponto qualquer da tela, e vai em uma direção, batendo nas bordas. Agora, execute “java PingPong2 8” — E você verá OITO bolinhas aparecerem na tela, cada uma andando em uma direção, todas se movendo praticamente ao mesmo tempo, e batendo nos cantos da tela !!

 

PingPong2 01

No fonte inicial da classe PingPong, existia apenas o método “main”, que fazia “tudo”, e desenhava apenas uma bolinha. Na classe PingPong2, usamos uma grande parte da flexibilidade da orientação a objetos do Java, para criar uma classe que é responsável por “animar” uma bolinha, e pode ser “instanciada” várias vezes, animando múltiplas bolinhas em pontos distintos da tela. Antes de mais nada, vamos dar uma “passada rápida” no conceito de orientação a objetos, e em suas terminologias, tomando por base a classe de exemplo PingPong2().

Uma classe é uma forma de agrupar um conjunto de atributos nomeados e qualificados, que representam uma unidade de informação complexa, para a qual podemos criar funções associadas a este conjunto, que realizem ações baseadas nas propriedades e mudanças de estado dessas propriedades.

Na classe usada como exemplo, o caractere de escape, o numero da linha e coluna atuais da bolinha e a direção horizontal e vertical que ela se move são “propriedades” da classe PingPong2, e as funcionalidades de inicializar uma nova bolinha, mover a bolinha e o ponto de entrada de execução ( main() ) são métodos desta classe.

Vamos ver a abordagem prática do ponto de vista da execução, para entender o papel das declarações e do resto do código. Vamos começar pelo método main():

public static void main(String args[]) throws InterruptedException
{
  // Quantas bolinhas serão criadas
  int balls = 1;
  if ( args.length > 0 )
    balls = Integer.parseInt(args[0]);
  if ( balls < 1 )
    balls = 1;
  // Criação e animação das bolinhas
  PingPong2[] aBalls = new PingPong2[balls];
  for ( int i = 0; i < balls; i++)
    aBalls[i] = new PingPong2();
  while (true)
  {
    for ( PingPong2 oBall : aBalls )
      oBall.PingStep();
    Thread.sleep(250);
  }
}

Recebimento de parâmetros de linha de comando

O parâmetro args, do tipo Array de Strings, declarado no método main(), pode receber um ou mais argumentos que você informar ao executar a sua classe. Por exemplo, para executar a classe PingPong2.class, você usa o comando “java PingPong2”, certo ? O que mais você colocar depois disso, separado por espaços em branco, será recebido como uma String no array args. Por exemplo, executar a classe usando “javac PingPong2 8”, o número 8 vai ser recebido como uma string, no primeiro (e único) elemento do array args.

Como eu posso ou não informar um argumento adicional, a primeira parte do código cria uma variável do tipo primitivo “int”, para armazenar a quantidade de bolinhas que serão desenhadas na tela, e já atribui o valor 1. Então, o programa verifica se foi informado algum argumento adicional, e caso tenha sido, o programa realiza a conversão desse argumento de String para número, e assume que este será o número de bolinhas a ser desenhado. Como eu posso passar um argumento 0 ou negativo, o programa somente considera o argumento, caso ele seja informado, e seja maior que zero.

Observação : O aplicativo parte da premissa que, se um argumento foi informado, será possível converter ele para um número. Se, ao invés do número 8, você informar uma letra, a aplicação será interrompida com a ocorrência de erro  “Exception in thread “main” java.lang.NumberFormatException: For input string: “A”“. Mais para a frente vamos ver formas de lidar e tratar esse tipo de ocorrência.

Declaração de um Array de Objetos

PingPong2[] aBalls = new PingPong2[balls];

Dessa forma nós declaramos um array de objetos de tamanho fixo, preparado para conter objetos do tipo “PingPong2”, limitado ao número de bolinhas que eu quero desenhar na tela — armazenado na variável balls. Lembrando que essa é apenas a definição do array aBalls. Após a instrução ser executada, a variável aBalls passa a existir, como um container / lista de objetos do tipo PingPong2, com um determinado número de elementos (informado na variável balls), mas cada elemento dessa lista não contém um objeto … o valor de cada elemento está “null”.

Criação dos objetos para popular o Array 

for ( int i = 0; i < balls; i++)
   aBalls[i] = new PingPong2();

A instrução for() é uma instrução de repetição um pouco mais sofisticada que o while(). Ela permite a execução de uma expressão de inicialização, executada apenas uma vez, que foi usada para declarar e inicializar uma variável inteira chamada “i” com o valor zero, que será visível somente dentro do escopo ( ou bloco ) de instruções após o for(). Permite também uma expressão condicional — i < balls — que será verificada ao final do bloco para determinar se o bloco deve ser executado novamente ( igual ao while() ) , e uma última expressão, executada a cada final do bloco executado, que no nosso caso incrementa a variável i. Tudo isso, serve para que a linha abaixo do for() seja executada uma vez para cada bolinha que deve ser criada. Trata-se de uma atribuição no elemento de número i do Array aBalls, que recebe uma nova instância (objeto) da classe PingPong2. Agora sim, após esta execução, cada elemento do Array aBalls terá uma instância distinta da classe PingPong2.

Em Java e c++, entre outras linguagens, o primeiro elemento de um array é o elemento  “0” (zero). Em AdvPL e outras linguagens, o primeiro elemento é o “1” (um).

Método construtor da classe PingPong2

Cada operação “new PingPong2()” vai executar o método PingPong2() da classe PingPong2. Quando criamos um método com o mesmo nome da classe, este método é chamado de “construtor”, pois cada vez que um programa chamar um “new PingPing2()”, esse método será executado.

public PingPong2()
{
  row = rand.nextInt(25)+1;
  col = rand.nextInt(80)+1;
  if ( rand.nextInt(2) > 0 )
    rowinc = -1 ;
  if ( rand.nextInt(2) > 0 )
    colinc = -1 ;
}

No construtor de uma nova bolinha, usamos uma função randômica da classe Random() do Java, para sortear  um numero entre 0 e o número informado menos um … Logo, nextInt(25) sorteia um numero entre 0 e 24. Sempre somamos 1 a este resultado, pois ele será a linha inicial da bolinha na tela. E, sorteamos também um número entre 0 e 79 ( somando 1 ao resultado ) para se a coluna da bolinha. Assim, cada bolinha que vai ser desenhada na tela pode ter um ponto de início diferente. E, partimos por default que a bolinha originalmente está se movendo para a direita e para baixo, mas ao sortear um numero entre 0 e 1, se ele for maior que zero, a bolinha pode inciar o movimento para a esquerda, o sorteamos de novo um numero entre 0 e 1, para decidir se a bolinha deve começar o movimento subindo, ao invés de descer a tela.

Animando todas as bolinhas

while (true)
{
  for ( PingPong2 oBall : aBalls )
    oBall.PingStep();
  Thread.sleep(250);
}

Aqui a mágica acontece. Primeiro, eu tenho um for() diferenciado, especialista. Esta forma de declaração cria dentro do escopo de repetição uma variável chamada oBall, do tipo objeto “PingPong2”, que a cada repetição do bloco, vai conter um elemento do array aBalls. Esta sintaxe de uso seria o equivalente a fazer isso aqui:

for ( int i = 0; i < aBalls.length; i++)
{
   PingPong2 oBall = aBalls[i];
   /* demais instruções a repetir ... */ 
}

Da mesma forma que a while() e a if(), se eu não iniciar um bloco  — abrindo  e fechando chaves para delimitar as instruções que eu quero executar / repetir — apenas a próxima instrução do for() será executada por n repetições. No caso, vamos executar o método PingStep() para cada uma das bolinhas, uma de cada vez.

Movimentação e pintura

public void PingStep()
{
   System.out.printf("%c[%d;%df ",escCode,row,col);
   if ( row + rowinc < 1 || row + rowinc > 25 )
      rowinc = -rowinc;
   row += rowinc;
   if ( col + colinc < 1 || col + colinc > 80 )
      colinc = -colinc;
   col += colinc;
   System.out.printf("%c[%d;%dfO%c[1D",escCode,row,col,escCode);
}

É exatamente igual ao primeiro PingPong .. porém nesse contexto, as variáveis row, col, rowinc e colinc são propriedades da do objeto. Como cada objeto possui a sua coordenada e movimento, cada chamada do método PingStep() é executada para a instância atual do objeto, obtida no array de objetos. Assim, movemos todas as bolinhas desenhadas na tela em um loop, e depois damos uma pausa de 1/4 de segundo, para mover todas novamente. 😀

Importação de pacotes 

import java.util.Random;

Salvo algumas classes básicas do Java, qualquer recurso adicional da linguagem é disponibilizado na forma de “packages” — ou pacotes. Para consumir uma função de um determinado pacote, no início do fonte .java, o pacote necessário deve ser “importado”, usando o statement “import”, seguido do nome completo do pacote. Veja por exemplo, o programa utiliza uma classe chamada “Random”, para a geração de números randômicos (ou “sorteados”). A documentação dessa funcionalidade está em https://docs.oracle.com/javase/8/docs/api/java/util/Random.html, e ao abrir a documentação, repare no trecho destacado em vermelho:

Java - Random

Este é o nome do pacote que disponibiliza essa funcionalidade. Para usá-la no seu fonte, você deve fazer o “import” desse pacote. Sem ele, o fonte não compila, pois o compilador não sabe quem é  “Random()” …. Mesmo sendo uma funcionalidade trivial, o Java foi concebido para trabalhar com o mecanismo de declaração explícita de dependências, assim ele carrega para dentro da maquina virtual apenas os componentes necessários para executar um determinado código.

Conclusão

Ainda falta explicar mais da metade dos “porquês” do código, não exatamente sobre a funcionalidade do PingPong2, mas sim sobre a orientação a objetos em si, os modificadores de acesso de propriedades e métodos (public , private, static, final), onde e quando podemos usá-os e o que isso muda no comportamento. E isso vai abrir caminho para as heranças de classe, classes de interface ( ou “abstratas” ) … 😛

Espero que estas informações lhes sejam úteis, e lhes desejo TERABYTES DE SUCESSO !!! 

Referências

 

Um comentário sobre “Java from Scratch – Parte 04

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s