Anteriormente, consideramos programação como uma atividade envolvendo três `mundos': real, computacional e linguístico (ou sintático). Já somos familiares com os objetos e classificações do mundo real; resta agora se familiarizar com os objetos e classes computacionais e aprender a descrevê-los (especificá-los) em Java. Aí finalmente poderemos mapear os objetos reais em objetos computacionais e escrever programas que dão vida a estes objetos em um sistema computacional.
Objetos (computacionais) são caracterizados por atributos e métodos. Atributos são as propriedades de um objeto. Métodos são as ações que um objeto pode realizar. Naturalmente, os atributos de um objeto podem mudar de acordo com o tempo. De fato, a execução de um método muda os valores dos atributos do objeto responsável pela execução. Neste caso dizemos que o objeto mudou de estado.
Atributos podem ser classificados como derivados ou essenciais (também chamados armazenados). O valor de um atributo derivado é determinado em função dos valores de atributos essencias e de outros atributos derivados. Por outro lado, o valor de um atributo essencial é determinado pelo estado interno de um objeto. Naturalmente, esta classificação depende das diferentes visões que podemos ter da realidade.
Classes são agrupamentos de objetos (computacionais) que têm propriedades em comum e podem realizar as mesmas ações. Naturalmente, este agrupamento deve refletir a classificação natural dos objetos reais. De fato, classes introduzem a noção de tipo em linguagens orientadas a objetos, o que é fundamental para organizar informações e evitar erros desnecessários. Mais adiante veremos que temos também a noção de subtipo (subclasse), a qual está diretamente associada a noção de subagrupamento (subconjunto). De uma forma geral, os objetos de uma subclasse têm as propriedades e ações dos objetos de uma superclasse e mais algumas propriedades e ações adicionais.
Para descrever uma classe em uma linguagem de programação basta descrever os atributos e métodos dos objetos da classe. De certa forma, isto poderia ser feito usando-se pacotes de ADA com mecanismos para esconder informações, como vimos previamente. No entanto, desta maneira objetos não são cidadãos de primeira classe; isto é, não podem ser argumentos ou resultados de operações, não podem ser armazenados em variáveis, etc. Todos estes aspectos são essenciais para programação orientada a objetos.
Contrastando, Java oferece recursos linguísticos para resolver estes problemas. De uma maneira geral, classes são especificadas (descritas) da seguinte forma em Java:
class NomeDaClasse{ Corpo }onde Corpo corresponde à descrição dos atributos e métodos da classe.
Em particular, atributos essenciais são descritos da seguinte forma:
class Livro { private String titulo; private int anoDePublicacao; private int numeroDePaginas; ... }onde cada atributo tem um tipo específico que caracteriza as propriedades dos objetos da classe. Neste caso, int e String denotam os tipos cujos elementos são inteiros e strings.
A palavra reservada private indica que os atributos só podem ser acessados (isto é, lidos ou modificados) pelas operações da classe correspondente. De fato, alguns autores consideram isto uma pré-condição para que uma linguagem seja orientada a objetos. Outros vão mais adiante, exigindo que os atributos de um objeto só podem ser acessados pelos métodos deste objeto; depois será mais fácil entender a diferença entre estes dois requisitos.
Vários atributos essenciais de um mesmo tipo podem ser declarados da seguinte maneira:
class Pessoa { private int anoDeNascimento; private String nome, sobrenome; private boolean casado = false; ... }Este exemplo também mostra como podemos especificar que um atributo deve ser inicializado com um valor específico. Neste caso, o atributo casado deve ser inicializado com o valor booleano false toda vez que for criado um objeto da classe Pessoa.
Podemos ver os atributos essenciais de um objeto como sendo variáveis locais que armazenam valores denotando os respectivos atributos. Assim, atributos essenciais são usualmente chamados variáveis de instância, dado que os objetos de uma classe são também chamados instâncias desta classe.
Atributos derivados são representados por operações que simplesmente calculam o valor do atributo e retornam este valor como resultado da operação. Por exemplo, sabemos que o século no qual uma pessoa nasceu é um atributo de Pessoa que pode ser calculado em função do ano de nascimento desta pessoa. Similarmente, a idade de uma pessoa também é um atributo de Pessoa que pode ser calculado a partir do ano corrente e do ano de nascimento desta pessoa. Em Java, estes atributos são definidos da seguinte forma:
class Pessoa { private int anoDeNascimento; private String nome, sobrenome; private boolean casado = false; int seculoDeNascimento() { return ...anoDeNascimento...; } int idade(int anoCorrente) { return ...anoDeNascimento...; } ... }onde na definição de cada operação indica-se primeiro o tipo do valor a ser retornado, seguido pelo nome da operação e uma lista, possivelmente vazia, indicando o tipo e o nome dos argumentos a serem recebidos pela operação. O comando return especifica o valor a ser retornado como resultado da operação.
Um tipo trivial de atributo derivado simplesmente retorna como resultado o valor de um atributo essencial, como ilustrado a seguir:
class Livro { private String titulo; private int anoDePublicacao; private int numeroDePaginas; String titulo() { return titulo; } int anoDePublicacao() { return anoDePublicacao; } ... }Estes atributos derivados são necessários pois os valores de alguns atributos essenciais geralmente devem ser visíveis. Da forma ilustrada acima, garante-se que os valores de alguns atributos essenciais podem ser lidos por clientes (usuários) dos objetos, mas não diretamente alterados.
Observando que classes são (descritas por) um tipo especial de módulo e que private é um mecanismo para esconder informações, ressalta-se a importância do uso de private para definir atributos essenciais.
Paulo Borba (phmb@di.ufpe.br) |