Entendendo classes

Nesta aula discutiremos:

Uma primeira classe

Veremos abaixo uma classe que simula uma cartão pré-pago usado para vários tipos de serviços.

/**
 * CartaoPrePagado classe que simula um
 * cartao pre-pago simples
 * @author EESR
 * @version 4.ago.2006
 */
 public class CartaoPrePago
 {
	 private int saldo;
	 
	 public CartaoPrePago()
	 {
	   saldo=50;
	 }
	 	 
	 public void insereCredito(int credito)
	 {
	   if(credito > 0)
	     saldo=saldo+credito;
	 }
	 
	 public void debita(int debito)
	 {
	   saldo=saldo-debito;
	 }
	 
	 public void printSaldo()
	 {
	   System.out.println("Unidades: " + saldo);
         }
 } 

Campos

O código fonte de uma classe pode ser dividido em duas partes:

Abaixo vemos o envoltório de uma classe. Atentem para a padronização dos nomes das classes com caracter alfabético inicial maiúsculo.

 public class CartaoPrePago
 {
  // parte interna omitida
 } 

mais detalhes...
Esta é a ordem das partes: atributos, métodos construtores e outros métodos.

 public class NomeClasse
 {
    Atributos
    Construtores
    Operacoes
 }

Neste caso específico da classe CartaoPrePago temos como atributos:

 public class CartaoPrePago
 {
	 private int saldo;
	 
     outros...
 } 

saldo que armazena o saldo do cartão. Outros atributos poderiam existir.

private é um qualificador do atributo e exprime que este atributo só pode ser acessada pelo próprio objeto. Outras classes (objetos) não acessam este identificador do atributo.
Esta restrição é a base para o que é conhecido como ocultamento de informações que veremos adiante.

Construtor

O método construtor (que leva o mesmo nome da classe) é o responsável pela instanciação da classe, ou seja, a formação de um objeto.

Os construtores não podem oderecer nenhum tipo de retorno. Isto porque normalmente a sua função é a preparação inicial dos atributos, como uma inicialização dos valores dos atributos que caracterizarão o futuro objeto. Comentando de monaira mais técnica, pelo método construtor ocorrem as inicializações das variáveis de estado de cada objeto.

Detalhe da linguagem

Mesmo quando não especificado, todos os identificadores (variáveis) em Java são inicializadas com um valor padrão. Para variáveis tipo int este valor é zero.
Vemos abaixo o método construtor da classe CartaoPrePago

 public class CartaoPrePago
 {
	 private int saldo;
	 
	 public CartaoPrePago()
	 {
	   saldo=50;
	 }
 } 

Transmitindo dados via parâmetros de entrada

Esta passagem funciona de modo similar a linguagem C no sentido do escopo das variáveis e do tempo de vida dos seus parâmetros formais.

O escopo é formado pelo método em que se insere. Já o tempo de vida é limitado a chamada do método, mesmo que este seja um construtor.

O tempo de vida de uma variável de estado é igual ao tempo de vida do objeto a que ele pertence.

 public class CartaoPrePago
 {
...
	 
	 public void insereCredito(int credito)
	 {
	   if(credito > 0)
	     saldo=saldo+credito;
	 }

	 public void debita(int debito)
	 {
	   saldo=saldo-debito;
	 }
 } 
 

Operadores modificadores

Normalmente este nome está associado às operações cuja a função é alterar o valor das variáveis de estado.
Os métodos debita(int) e insereCredito(int) estão nesta categoria.

Métodos de acesso

Como é comum para classes terem atributos private, ou seja, identificadores que não são acessíveis e modificáveis fora do objeto, é muito comum que as classes tenham operadores para acesso, ou seja, para tomar conhecimento, do valor das variáveis de estado.

No caso da classe CartaoPrePago poderíamos ter, por exemplo, um método que informaria ao objeto solicitante o valor da variável de estado saldo. Veja o código a seguir:

 public class CartaoPrePago
 {
...
	 public int getSaldo()
	 {
	   return saldo;
	 }
 } 

Imprimindo a partir de métodos

 public class CartaoPrePago
 {
...
	 public void printSaldo()
	 {
	   System.out.println("Unidades: " + saldo);
         }
 } 

O operador printSaldo() utiliza uma hierarquia de métodos e objetos que veremos mais claramente no futuro mas, println é um método do objeto System.out que, por sua vez, é construído pela linguagem Java.

printSaldo() imprime no terminal uma cadeia de caracteres, uma string formada pelo conteúdo entre aspas e pelo valor da variável saldo, as duas partes concatenadas pelo operador +, aqui funcionando como concatenador do tipo string, não como operador de adição.

Executando

/**
 * RunCartao classe que executa CartaoPrePago
 * @author EESR
 * @version 13.ago.2005
 */
 public class RunCartao
 {
    public static void main(String[] args)
     { 
	     CartaoPrePago c1;
	     
	     c1=new CartaoPrePago();
	 
	     c1.printSaldo();
	     c1.insereCredito(12);
	     c1.debita(20);
	     c1.printSaldo();
     }
 }

Responsabilidades

Referenciando o livro de Timothy Budd:

Um conceito fundamental em POO é descrever o comportamento dos objetos em termos de responsabilidades. Pela discussão do problema nos termos de responsabilidades nós elevamos o nível da abstração. Isto permite uma maior independência entre os agentes (classes e objetos) e é um fator crítico para resolver problemas complexos.
A diferença entre analisar um software na maneira estruturada tradicional e analisar no paradigma de POO pode ser resumido na seguinte frase:
Não pergunte o que você pode fazer pelas suas estruturas de dados,
e sim o que suas estruturas de dados podem fazer por você.

Leia o termo estrutura de dados na sentença acima e entenda este conceito como a união entre o seu arranjo de atributos e seus comportamentos descritos para uma classe.

Outro exemplo

Do livro do Barnes&Kölling retiramos o exemplo a seguir:

/**
 * TicketMachine
 * exemplo de Barnes&Kölling
 */
public class TicketMachine
{
  private int preco;
  // valor inserido pelo cliente
  private int saldo;
  // total coletado pela maquina
  private int total;

  public TicketMachine (int precoBilhete)
  {
      preco=precoBilhete;
      saldo=0;
      total=0;
  }
  
  public int getPreco()
  {
    return(preco);
  }

  public int getSaldo()
  {
    return(saldo);
  }

  public void insereDinheiro(int money)
  {
    if(money>0)
    {
      saldo+=money;
    }
    else
    {
      System.out.println("Insira valores positivos");
    }
  }

  public void imprimeBilhete()
  {
    if(saldo>=preco)
    {
      System.out.println("##############");
      System.out.println("# "+preco+ "centavos#");
      System.out.println("##############");
      total+=preco;
      saldo-=preco;
    }
    else
    {
      System.out.println("Insira pelo menos");
      System.out.println((preco-saldo)+" centavos");
    }
  }

  public int retorno()
  {
      int tot;

      tot=saldo;
      saldo=0;
      return(tot);
  }
}

Estou certo que esta pode não ser a única implementação simples de uma máquina de fichas. Critique esta implemetação, sugira melhoramentos e implemente estes melhoramentos escrevendo a sua própria TicketMachine.

Um resumo de POO até aqui

Estas são algumas das características fundamentais de POO segundo Alan Kay, considerado o pai de POO:

Atividade
  1. Escreva uma classe que converta entre as temperaturas Celsius e Fharenheit.
  2. Escreva uma classe CaixaConversora para converter entre as seguintes moedas: Euro, Dollar norte-americano e Reais. Esta classe deve possuir um lastro, ou seja, uma quantidade inicial de valores em cada moeda e deve também cobrar uma porcentagem por cada conversão de valores.
  3. Escreva uma classe Java chamada Estudante com os seguintes atributos: um tipo String com uma identificação do aluno, um código; um tipo String para o seu nome; e um tipo int para os créditos acumulados até o momento. Como operações: o construtor, métodos de acesso a atributos, um método para adicionar créditos, um para alterar o nome e um para impressão dos atributos.
Última modificação feita em: 16 de agosto de 2008
Evandro Eduardo Seron Ruiz, PhD (evandro at usp ponto br)
www.imagcom.org/evandro