segunda-feira, 1 de outubro de 2012

Entendendo orientação a objetos


Praticamente toda linguagem de programação estruturada possui a capacidade de definirmos estruturas de dados. Uma estrutura de dados é um tipo de dado composto definido pelo usuário. Assim, podemos definir um tipo Pessoa que possui um nome e uma idade. Sei que este exemplo não é nada útil mas vou usá-lo mesmo assim. Isto é uma estrutura de dados. Em C definimos como struct e em pascal como um record. Em C fica assim:

struct {
     char * nome;
     int idade;
} pessoa;

Ao criarmos uma estrutura de dados é como se criássemos um novo tipo de variável que pode ser usada em nosso programa. Bem, podemos então instanciar uma variável do tipo pessoa da mesma forma que instanciamos uma variável do tipo inteiro ou float. Assim:

int a;
float b;
pessoa c;

Ao usarmos esta variável é possível acessar os dados encapsulados pelo tipo. Desta forma é possível acessar o nome e a idade do tipo pessoa diretamente:

pessoa c;
c.idade = 20;
c.nome = "Antonio";

Além da possibilidade de criarmos estruturas de dados, as linguagens costumam permitir que criemos fuincionalidades. Assim é possível definirmos uma função gravar que irá gravar uma pessoa. O tipo de dado pessoa pode ser passado diretamente para a função que irá conhecer este tipo de dado da mesma forma que conhece um inteiro, float ou char:

/* Exemplo que recebe dois inteiros */
int somar(int a, int b){
     return a + b;
}

/* Exemplo que recebe uma pessoa */
int gravar(pessoa p){
     /* Grava de alguma forma em arquivo ou banco de dados
     p.nome;
     p.idade;
     */
     return 1;
}


Bem, o que a orientação a objetos traz de diferente é a possibilidade de definirmos as funcionalidades junto com a estrutura de dados. A este conjunto de estrutura de dados (que chamamos de propriedades) e funcionalidades (que chamamos de métodos) chamamos classe. Seria o mesmo que definir em uma struct algo assim:

struct {
     char * nome;
     int idade;
     int gravar_pessoa(){
     /* Grava de alguma forma */
     /* this.nome; */
     /* this.idade */
     return 1;
     }
} pessoa;

NOTE QUE ESTE EXEMPLO NÃO FUNCIONA!

Ao adicionarmos a funcionalidade a estrutura de dados, podemos acessar o método (função) da mesma forma que acessamos os atributos ou propriedades.

pessoa p;
p.nome = "Maria";
p.idade = 23;
p.gravar();


Veja que o método gravar pertence a instância da variável e por isto ele consegue acessar internamente suas propriedades. E isto é orientação a obejtos. Costumo dizer que OO é uma questão de escopo. De maneira muito simplista é fechar a chave que define a estrutura de dados depois de definirmos as funcionalidades. :-) Só que em vez de definirmos esta estrutura como uma struct (que o próprio nome indica ser APENAS estrutura de dados) definimos como uma classe.

Se Orientação a objetos fosse apenas isto, já seria muito legal. Mas OO é bem mais que isto.

Polimorfismo


Na maioria das linguagens, não podemos definir duas funções com o mesmo nome. Se tivermos um sistema que possui as estruturas de dados pessoa, usuário, aluno, livro e empréstimo teremos de ter funções com nomes diferentes para gravar pessoa, usuário, aluno, livro e empréstimo. Cada uma delas receberá um tipo de dado definido anteriormente e que a função sabe como tratar.

int gravar_pessoa(pessoa p){ /* ... */}
int gravar_usuário(usuário u){/* ... */}
int gravar_aluno(aluno a){/* ... */}
int gravar empréstimo(empréstimo e){ /* ... */}


Agora é possível termos vários métodos gravar, um para pessoa, outro para livro e outro para aluno. E isto tem nome, chama-se polimorfismo. Polimorfismo nada mais é que métodos com o mesmo nome em classes diferentes, no caso gravar, que fazem coisas diferentes, no caso gravar coisas diferentes.

Encapsulamento

Orientação a objetos também permite acessos distintos a estrutura de dados. Podemos ter dados privados, que apenas a própria classe pode acessar ou dados públicos que qualquer dado pode acessar. Isto não existe na programação estruturada e é bem útil para dados internos e de controle.

Assim, sua estrutura de dados não fica "exposta" indevidamente e você pode definir diferentes níveis de acesso para as propriedades e também para os métodos. Diria que com isto a programação em equipe fica mais organizada.

Herança

OO também permite que uma classe complemente os dados de outra. Assim, temos uma estrutura de dados chamada usuário que possui apenas login e senha e temos uma estrutura de dados chamada aluno que irá estender usuário e só por estender já ganha os atributos login e senha.

O método logar também será herdado e este método usará apenas os dados do usuário, que o aluno já herdou.


class Usuário {
   char * login;
   char * senha;
public:
   int logar() { /* ... */  }
};

class Aluno : public Usuário { /* ... */ };

 int main() {
   Aluno a;
   a.logar();
}

Conclusão

Orientação a objetos é bem legal!

Nenhum comentário: