Introdução à Orientação a Objetos

Padrão


Olá pessoal! Depois da nossa série de posts para programadores iniciantes, vamos começar aqui uma nova série, avançando um pouco mais no mundo da programação. Nesta série, falaremos sobre os principais conceitos da Orientação a Objetos, o paradigma mais utilizado hoje no mercado. Esta série (bem mais curta que a outra), servirá de base para que possamos seguir em frente em nossos estudos, para desenvolvermos aplicações Web (que está planejado para ser uma terceira série de posts). Na orientação a objetos, normalmente não se usa a representação algorítmica, como na programação estruturada, mas sim a representação na forma de diagramas, utilizando a Unified Modeling Language (UML). Para as partes práticas, pretendo demonstrar os conceitos utilizando as linguagens C++ e Java. Para o desenvolvimento dos aplicativos em C++ recomendo o Code::Blocks (o mesmo que utilizávamos para escrever códigos em C) ou o Visual Studio Express e para Java recomendo o Eclipse ou o NetBeans. Lembrando que, para o desenvolvimento de aplicações em Java, é necessário instalar o Java Development Kit (JDK), que pode ser baixado no site da Oracle. Todas as ferramentas citadas aqui são gratuitas e, com exceção do Visual Studio, estão disponíveis tanto para Windows, quanto para Linux e Mac. Em um momento oportuno, posso escrever um post a respeito da utilização de algumas dessas IDEs.

Bom, então vamos começar a falar sobre a Orientação a Objetos. 🙂

A orientação a objetos (OO) é um paradigma (uma espécie de fórmula, ou melhor, metodologia) de análise, projeto e programação de sistemas de software. O termo foi criado por Alan Kay, criador da linguagem Smalltalk. Hoje em dia, grande parte das linguagens utilizam esse paradigma, como Java, C#, C++, Object Pascal (Delphi), Ruby, Python, PHP (a partir da versão 4) etc. Mas não vamos nos prender muito a história e vamos entender o que vem a ser a OO.

Antigamente, o objetivo principal dos desenvolvedores era otimizar o código, principalmente pelo motivo de que o hardware na época era um tanto quanto limitado com relação aos recursos. Esse tipo de desenvolvimento, muitas vezes melhorava o aplicativo, em detrimento ao desenvolvimento, que era mais trabalhoso. Nessa época, o paradigma estruturado era o que dominava o mercado (o tipo de desenvolvimento que vimos na primeira série de posts.) Com o tempo, viu-se a necessidade de uma nova forma de desenvolver software. Queria-se um nível de abstração maior, de forma que o planejamento e o desenvolvimento do software se assemelhasse com o mundo real. No mundo real, interagimos com as coisas. E cada uma dessas “coisas” que interagimos, tem características e fazem certas ações. Assim, essas “coisas” foram chamadas de objetos. Passou-se então a pensar em objetos interagindo no software. Surgia assim a Orientação a Objetos.

Ao projetar sistemas utilizando a OO, identificamos as entidades, que são essas “coisas” que comporão o nosso software. Essas entidades, ao passarmos para a programação, essas entidades se tornarão as classes. As classes tem, basicamente, duas partes principais. A primeira dela, é a parte de características. É nessa parte que temos as informações a respeito da classe. Por exemplo, uma classe chamada Pessoa. O que temos com relação às características? O nome, a data de nascimento, o tipo sanguíneo, etc. Na programação, essas características são conhecidas como variáveis de instância. A segunda parte é a parte que define o comportamento da classe. Ainda seguindo o exemplo de uma classe chamada Pessoa, teríamos ações como andar, falar, bater o coração (por que não?), respirar, etc. Na programação, as coisas que determinada classe faz são chamadas de métodos. Então, podemos dizer que as classes são compostas de variáveis de instância e métodos.

Tá bom… eu to falando aqui de classes e tal… mas e os objetos? Afinal é orientação a objetos, não é? Sim sim, claro. O que acontece é que não trabalhamos diretamente com as classes, e sim com os objetos. As classes são usadas para construir os objetos. Uma classe é o projeto de um objeto. Ela diz como o objeto deve ser criado. É como se fosse o tipo do objeto (tipo no conceito de tipos na programação). Vamos pensar em um exemplo. No meu programa tenho uma classe Botão. A classe Botão será usada para criar vários botões (cada um, um objeto), cada qual com suas próprias características. Como assim? Mas as características não são definidas na classe? Mais ou menos. Continuando com o exemplo do Botão. Podemos definir na classe que um botão terá características como cor, tamanho, forma, rótulo. Cada um dos objetos do tipo botão, terá suas próprias características: sua própria cor, seu próprio tamanho, etc.

Antes de eu mostrar como funciona isso na prática, apenas vou fazer algumas breves explanações a respeito das linguagens que iremos utilizar para ilustrar a OO. Em C++, que foi derivado do C, temos também a função main() (derivada do paradigma estruturado) que define o ponto onde o programa é inicializado. Já no Java, não temos funções. Tudo nele é orientado a objetos, de forma que não temos funções. Um programa é executado através de um método de uma classe, que também deve ser chamado de main. Ao executarmos um programa, indicamos a classe que possui tal método e então, a partir dele, é iniciada a execução.

Vamos pegar como exemplo a classe Cachorro. Vamos criar essa classe e definir objetos a partir dela. Percebam que a sintaxe das duas linguagens são bastante parecidas com a linguagem C. O conjunto de linguagens com esse estilo de sintaxe é conhecida como linguagens C-Like. Mas logo em seguida, vou explicar alguns elementos principais da linguagem.

Primeiro em C++…

// Arquivo cachorro.h

#include <iostream>
#include <string>
using namespace std;

class Cachorro {
public:
    int tamanho;
    string raca;
    string nome;

    Cachorro();
    ~Cachorro();
    void latir();
};

 

// Arquivo cachorro.cpp

#include <iostream>
using namespace std;
#include "cachorro.h"

Cachorro::Cachorro() {
}

Cachorro::~Cachorro() {
}

void Cachorro::latir() {
   cout << "Ruff! Ruff!" << endl;
}

 

// Arquivo main.cpp

#include <iostream>
using namespace std;
#include "cachorro.h"

int main(void) {
    Cachorro cachorro;
    cachorro.tamanho = 40;
    cachorro.raca = "Doberman";
    cachorro.nome = "Rex"; // Muito criativo, não?

    cachorro.latir();

    return 0;
}

… e depois em Java…

// Arquivo Cachorro.java

package canil.exemplo

public class Cachorro {
    public int tamanho;
    public String raca;
    public String nome;

    public void latir() {
        System.out.println("Ruff! Ruff!");
    }

    public static void main(String[] args) {
        Cachorro cachorro = new Cachorro();
        cachorro.tamanho = 40;
        cachorro.raca = "Doberman";
        cachorro.nome = "Rex";

        cachorro.latir();
    }
}

Em uma visão geral, no exemplo acima, criamos uma classe chamada Cachorro, com as variáveis de instância tamanho, raça e nome, além do método latir(). A partir dessa classe, criamos um objeto chamado cachorro, atribuímos valores às suas variáveis de instância e, em seguida, fizemos uma chamada ao método latir, que escreve “Ruff! Ruff!” na tela, pulando para a próxima linha logo em seguida. Vamos agora analisar como isso foi feito nas duas linguagens.

Primeiramente, vamos analisar o código-fonte na linguagem C++. Em ambas as linguagens, é convenção declararmos cada classe em um arquivo separado. No caso do C++, cada classe é composta de um arquivo .h (declaração) e um arquivo .cpp (implementação). No arquivo de cabeçalho, cachorro.h, Iniciamos com um comentário (da mesma forma como fazíamos em C), e em seguida incluímos alguns arquivos. O arquivo iostream é parecido com o stdio que utilizávamos em C. É ele que nos fornece os meios de realizarmos, por exemplo, uma escrita na tela ou uma leitura de teclado. Isso é feito através dos objetos cout e cin. Esses objetos são objetos de fluxo, ou seja, direcionamos conteúdo a eles, e eles o mostram na tela (no caso do cout). Veremos mais sobre objetos de fluxo no decorrer dos posts. Logo em seguida, incluímos o arquivo string, que possui a declaração de uma classe de mesmo nome. Em C, utilizávamos vetores do tipo char para representar strings. Já em C++, temos essa classe que nos fornece mais recursos com relação a esse tipo de dados. Essa classe resolve as questões relativas a alocação de memória, além de nos fornecer métodos para obtermos o tamanho da string, concatenar uma string com outra, obter porções da string, etc. Por isso, ao programar em C++, é sempre bom utilizar a classe string ao invés do tipo char*. Ah, e se precisar, por algum motivo, do vetor de caracteres, a classe string pode lhe fornecer o vetor correspondente através de um método chamado c_str(). Mas não vamos nos preocupar com isso agora. Logo após os includes, temos using namespace std. O uso de namespaces no C++ serve, dentre outras coisas, para que possamos ter classes de mesmo nome no programa. Como assim? É uma coisa que acontece mais em projetos maiores, mas por exemplo, eu posso ter duas classes cachorros. Uma no namespace canil e outra no namespace petShop… pode parecer meio inútil agora, mas em alguns casos mais específicos pode ser a solução. Nessa linha do código, dizemos que estamos utilizando o namespace de nome std em todo o código desse arquivo. Isso nos poupa de ficar digitando o namespace, por exemplo, em std::cout, std::string, etc. Em seguida começamos a declaração propriamente dita da nossa classe. A palavra-chave class seguida do nome da classe. Por convenção, colocamos a primeira letra em maiúsculo no nome da classe. Iniciamos então o escopo da classe com as chaves. Perceba que ao final da classe, deve-se colocar ; da mesma forma que fazíamos nas structs em C. Temos logo no início da classe o label public. Isso significa que tudo que vier após esse label poderá ser acessado de fora da classe. Não se preocupe muito com isso agora. Veremos mais a fundo isso no próximo post sobre encapsulamento. Temos, inicialmente a declaração das variáveis de instância da nossa classe: o atributo tamanho do tipo inteiro, e os atributos raca e nome, que são objetos da classe string. Em seguida, temos a declaração do método Construtor e Destrutor da classe. Esses métodos, que são obrigatórios em C++, representam o que a classe fará ao ser inicializada e ao ser encerrada. Por exemplo, ao criar um objeto do tipo Cachorro, automaticamente será chamado o método construtor, Cachorro(). O método destrutor será chamado quando a execução sair do contexto onde a classe foi criada (por exemplo, no bloco entre chaves onde a classe foi declarada) ou quando ela for explicitamente desalocada (veremos isso mais adiante, quando virmos alocação dinâmica). Em seguida temos o método latir() que não recebe nada e não retorna nada. Perceba que métodos nada mais são que funções dentro da própria classe. Os métodos são funções correspondentes ao “tema” da classe. Por exemplo, o método latir() faz sentido dentro de uma classe Cachorro, mas não faria sentido numa classe chamada Vaca. Perceba que no arquivo de cabeçalho apenas declaramos os métodos – como se fossem os “protótipos” dos métodos -, a implementação vai no arquivo .cpp. O nosso arquivo cachorro.cpp começa com a inclusão dos arquivos de cabeçalho: o arquivo iostream, pois usaremos o objeto cout no método latir() e o arquivo cachorro.h que contém a declaração de nossa classe. Iniciamos então com o construtor e o destrutor. A sintaxe em C++ na hora de implementar os métodos é <retorno> NomeDaClasse::NomeDoMétodo(<Argumentos>) {}. Percebam que o construtor e o destrutor são diferentes, pois por padrão não possuem nem argumentos, nem valor de retorno – isso pode não acontecer (veremos isso mais a frente, quando falarmos sobre sobrecarga de operadores). Logo em seguida, temos a declaração do nosso método latir(). Simplesmente jogamos para o objeto cout (que escreve dados na tela) a string “Ruff! Ruff!” através do operador <<. Enviamos também uma quebra de linha, em C++ representada pelo endl. Poderíamos também, como em C, o caractere n para pularmos de linha. Por fim, no arquivo main.cpp temos a nossa já conhecida função main(). Nela, incluímos o arquivo cachorro.h que contém a declaração de nossa classe e o iostream. Criamos então um objeto chamado cachorro (reparem que a declaração de um objeto se assemelha muito a declaração de uma variável. Só que ao invés de um tipo primitivo, temos uma classe. O objeto é uma variável do tipo daquela classe). O acesso aos itens da classe se dá pelo operador . da mesma forma que fazíamos o acesso às structs em C (exceto quando a alocação for dinâmica. Aí, utilizamos o ->). Simplificadamente (bastante simplificadamente), uma classe é uma “struct com funções”. Para essa primeira parte, essa definição é suficiente. Assim, terminamos a explicação do código em C++. Quaisquer dúvidas, postem aqui nos comentários.

Bom, agora vamos explicar o código-fonte em Java. Em Java, como eu disse acima, a execução é inicializada a partir de um método main de uma classe (Java não tem funções ou procedimentos como C++). Dessa forma, declaramos o nosso método main dentro da própria classe Cachorro. Outra coisa é que em Java, toda a declaração da classe é feita em um único arquivo .java. Dessa forma, em nosso exemplo, temos apenas o arquivo Cachorro.java. Lembrando que o nome do arquivo deve coincidir com o nome da classe que ele contém. Bom, iniciamos o código com a declaração do pacote. Pacotes são uma alternativa para estruturar e organizar as classes em uma aplicação Java. Verificando-se externamente ao código, nada mais é que uma estrutura de pastas. Nesse nosso exemplo, na raiz do projeto teríamos uma pasta chamada canil, dentro dela outra pasta chamada exemplo e por fim o nosso arquivo Cachorro.java. Por convenção, os nomes de pacotes devem sempre ser em minúsculo. Por enquanto pode parecer besteira, mas é uma forma útil de organizar suas classes quando seus projetos começarem a crescer. Em seguida, temos a declaração da classe. Encontramos aqui algumas diferenças para o C++. A primeira delas é que a declaração da classe possui um classificador de acesso, no caso, o public. Em Java, se a classe não for definida como pública, ela somente poderá ser acessada dentro do próprio arquivo em que foi declarada. Geralmente, quando temos somente uma classe declarada no arquivo, ela é declarada como pública. Outra diferença para o C++ é que a definição do escopo da classe não termina com o ponto-e-vírgula! Fiquem atentos a isso para evitar erros de sintaxe. Em seguida, temos a declaração dos atributos da classe. No caso do Java, também deve-se ficar atento, pois os identificadores de acesso vem antes de cada um dos atributos ou métodos. Outro detalhe é que a declaração de strings é feita com letra maiúscula! Em Java, todas as classes tem seu nome em maiúsculo – e como String é uma classe, ela tem seu nome em maiúsculo! No método latir, o único comentário a ser ponderado é o método para a escrita de texto na tela. Como eu disse anteriormente, em Java tudo são classes. Assim, a biblioteca padrão define uma classe chamada System para representar o sistema. Porém, ela é definida como estática, ou seja, não é possível instanciá-la (criar objetos a partir dela). Dessa forma, acessamos seus métodos e atributos diretamente – no caso o atributo out que representa a saída de vídeo, e chamamos seu método println, que escreve uma linha na tela e pula para a próxima linha em seguida. Depois, temos a declaração do método main, que é o ponto inicial de nosso aplicativo. Sua declaração sempre deve ser assim, declarado como public static void (ou seja, método público, estático (não é necessário instanciar a classe para executá-lo) e não retorna nada), recebendo um vetor de objetos String como argumento (sim, a declaração de vetores no Java é um pouco diferente que o C++… veremos isso com mais detalhes quando for oportuno). Bom, no método main, declaramos um objeto do tipo Cachorro e o instanciamos com o uso do new. Em Java, tudo é referência (apesar de não existirem ponteiros). Assim, precisamos instanciar explicitamente nosso objeto, caso contrário ele permanecerá vazio (null). No mais, atribuímos valores às variáveis de instância da classe e chamamos o método latir(). E encerramos a explicação do código em Java.

Bom pessoal, nesta primeira parte, vimos o conceito de orientação a objetos, qual a diferença entre uma classe e um objeto, além de fazermos um exemplo bem básico, pra já irmos nos acostumando com as linguagens. Aguardem o próximo post, onde falaremos sobre um assunto muito importante na OO: Encapsulamento de Dados!

Até lá!


Um comentário sobre “Introdução à Orientação a Objetos

Deixe uma resposta