Introdução aos Objectos

From Wiki**3

Programação com Objectos
Introduction
Creation and Destruction
Inheritance & Composition
Abstraction & Polymorphism
Code Organization
Java Topics
Inner Classes
Enumerations
Data Structures
Exceptions
Input/Output
RTTI
Other Topics
JUnit Tests
UML Topics
Design Patterns
"Simple" Factory
Composite & Visitor
Command
Strategy & State
Template Method
Observer
Abstract Factory
Decorator & Adapter
Façade (aka Facade)

Esta secção apresenta conceitos fundamentais para a programação com objectos. Embora a maioria dos exemplos e da discussão utilize a linguagem Java, também se apresentam em paralelo outros baseados em C++. Quando relevante, podem ser utilizadas outras linguagens (e.g. PHP).

Embora algumas linguagens de programação com objectos não usem classes (e.g., as baseadas em protótipos), as que se discutem nestas páginas são, em geral, baseadas em sistemas de classes relacionadas por hierarquias de herança.

O que é um objecto?

Aspectos:

  • Tipo de Dados Abstracto
  • Encapsulamento
  • Comportamento

Captura de características dos objectos

Aspectos:

  • como capturar a natureza das coisas?
  • ter em consideração os aspectos de interesse para o fim em causa

Introdução à Programação com Objectos

Antes de dar início à programação com objectos utilizando uma linguagem onde os conceitos são suportados nativamente, pode ser útil ver como se implementam algumas operações e conceitos primitivos numa linguagem onde não são nativamente ou completamente suportados, como C.

Entidades Básicas

Organização de Memória e Visibilidade

A forma como as linguagens de programação organizam a memória e permitem a criação de objectos pode condicionar o modo de programar e a simplicidade de criação e destruição de objectos.

C++, tal como C, permite a criação de objectos em memória estática (numa secção do programa executável), na pilha das funções e no heap (reserva de memória dinâmica). Os objectos criados estaticamente existem durante todo o programa; os criados na pilha de funções (incluindo os que são criados em blocos internos de funções), são destruídos quando o contexto onde foram criados termina; os objectos criados no heap têm de ser destruídos explicitamente (através do operador delete). Note-se que, quando se mencionam objectos, não se referem apenas instâncias de classes, mas de qualquer tipo (incluindo os primitivos).

Gato g1;   // objecto estático (criado a nível global)
Gato *pg1; // Ponteiro global para instância de Gato
void f() {
  Gato g2;    // objecto volátil criado na pilha de f
  Gato *pg2;  // ponteiro volátil criado na pilha de f
  pg2 = new Gato();  // objecto criado no ''heap''
  delete pg2;        // objecto apontado por pg2 é destruído
} // g2 e pg2 são destruídos automaticamente
class A {
  Gato g3;    // objecto em A (inicializado e destruído com a instância de A)
  Gato *pg3;  // ponteiro em A (destruído com a instância de A)
public:
  A() {
    pg3 = new Gato();  // objecto criado no ''heap'' (fora da instância de A)
  }
  ~A() {
    delete pg3;   // destruição do objecto apontado por pg3
  }
};

Em Java, embora referências e instâncias de tipos primitivos possam ser criados no contexto de outros objectos ou nos blocos de funções (pilha), as instâncias de classes apenas podem ser criadas no heap. As entidades criadas em contextos voláteis são automaticamente destruídas (como em C/C++). Não existem instâncias criadas em contextos estáticos. As entidades criadas no heap devem ser destruídas quando já não são necessárias (para libertar a memória). No entanto, ao contrário do C++, em Java não existe nenhum operador de libertação explícita de memória, sendo a libertação da responsabilidade de um processo automático, o garbage collector. Este processo destrói periodicamente os objectos que não são referenciados por outros ou por qualquer referência de um contexto activo (e.g., uma referência local a uma função).

class A {
  Gato g4;    // referência em A

  public A() {
    // objecto criado no ''heap'' (fora da instância de A)
    // será destruído quando a instância de A o for
    g4 = new Gato();
  }
  
  // g5 é destruído no fim de f
  // objecto referenciado por g5 pode ser destruído depois
  public void f() {
    Gato g5;    // referência volátil criada na pilha de f
    g5 = new Gato();  // objecto criado no ''heap''
  }
}

Quanto à visibilidade, as entidades estão, em geral, acessíveis no contexto onde são declaradas (e.g., um bloco de uma função), ou são globais. No caso de entidades declaradas no contexto de objectos, o acesso é completo a nível da definição do objecto. O acesso de fora do objecto depende de vários factores, em geral controláveis por primitivas da linguagem. Ver as palavras chave public ou private em linguagens como o C++, o Java ou o PHP.

Classes e Objectos

A palavra chave "static"

O uso da palavra chave static para qualificar um membro de uma classe tem os seguintes efeitos:

  • Quando qualifica um atributo, torna-o independente de um objecto particular, i.e., faz com que seja global a todas as entidades da classe (todos os seus métodos) e todas as suas possíveis instâncias. Neste caso, usa-se directamente o nome da classe para qualificar a variável. Este comportamento está disponível, pelo menos, em Java e em C++.
  • Quando qualifica um método, tem um efeito semelhante ao descrito para atributos, i.e., o método torna-se independente das instâncias da classe e tem um comportamento e uso semelhante ao de uma função global (à la C/C++). Neste caso, o nome da classe qualifica o nome do método.
  • Em Java (mas não em C++), a palavra chave pode ser utilizada no contexto do carregamento da classe: qualquer bloco declarado static é executado quando a máquina virtual carrega a classe.

Por tornar os membros de uma classe globais, o uso da palavra static naquele contexto deve ser cuidadosamente ponderado: entidades globais não são passíveis de participação em composições onde participariam múltiplas instâncias (apenas existe uma cópia da entidade static).

Convenções de Escrita

As convenções de escrita, embora irrelevantes do ponto de vista do compilador (no sentido de que o compilador aceitará o programa desde que correctamente especificado na linguagem em causa), são importantes do ponto de vista humano, pois melhoram a legibilidade do programa.

  • Escrita e compilação de programas.

Operadores, Expressões e Controlo de Fluxo

Operadores e Expressões

  • Apresentação e discussão de operadores, expressões e operações de conversão de tipos primitivos e objectos em Java
  • Aspectos relacionados noutras linguagens (C/C++/Smalltalk)
  • Método equals
  • Casts
  • Restrições às operações sobre booleanos

Controlo de Fluxo

Exemplos e Exercícios

Exemplos

Exercícios