Polimorfismo

Olá pessoal! Hoje encerramos a nossa série sobre orientação a objetos… sim, como eu disse lá no primeiro post, esta série é bem mais curta que a nossa primeira, sobre Programação para Iniciantes. Mas acredito que ao longo destes 5 posts, foi possível pelo menos dar uma noção sobre orientação a objetos. Obviamente, não tratei de conceitos complexos (afinal eu nem sou autoridade no assunto pra abordar tópicos muito avançados).

Então, sem mais delongas, vamos falar sobre o tal do Polimorfismo. Sim… um nome “feio”, mas nem por isso é complicado. Praticamente, todo o conceito que envolve o polimorfismo já foi visto no último post, sobre métodos e classes abstratas.

Mas o que vem a ser o polimorfismo (polymorphism, em inglês) é uma técnica que permite que referências de tipos de classes mais abstratas representem o comportamento das classes concretas que referenciam. Dessa forma, é possível tratar vários tipos de maneira homogênea (através da interface do tipo mais abstrato).

Resumidamente, podemos dizer que o polimorfismo busca obter um comportamento específico através de uma mesma interface da classe. Neste post, veremos duas formas de utilizar o polimorfismo.

A primeira forma que veremos, é a utilização de uma superclasse abstrata, que terá seus métodos implementados pelas subclasses. A partir disso, acessaremos os métodos implementados pelas subclasses através da interface da superclasse. Parece confuso, mas deve ficar mais claro no código.

Como sempre, primeiro em C++…

…e depois em Java…

Como podemos ver em nosso exemplo, criamos uma classe abstrata chamada Animal, que possui as implementações Gato e Cachorro. Acredito que a única “novidade” neste código é o fato de chamarmos o construtor da superclasse em cada uma das implementações. Percebam que, em C++, devemos colocar : em frente ao construtor da subclasse e fazermos a chamada a ela. Já no Java, a superclasse é sempre referenciada pela palavra-chave super, de forma que chamamos seu construtor chamando super() e passando seus parâmetros.

Na parte executável do programa, nós criamos uma lista (em C++ um vector – nada mais que um vetor dinâmico, e em Java um ArrayList) de objetos, declarando como tipo a classe Animal, mais genérica. Dessa forma, apesar de ser um tipo abstrato, os objetos que compõem a lista são as implementações que fizemos (no caso, Gato e Cachorro). Com isso, temos uma interface padrão pra acesso aos objetos, independente de seu tipo, já que definimos os objetos pela interface Animal, e não pelas implementações específicas. Isso é polimorfismo: acessando uma mesma interface, podemos obter comportamentos diferentes. Assim, se adicionarmos novas implementações da classe Animal, elas também responderão a esse comportamento específico.

Outra forma de utilizarmos o polimorfismo é através de sobrecarga de funções / métodos. Dessa forma, uma determinada função pode ter um comportamento específico de acordo com seus parâmetros. Vamos ver um exemplo em C++…

…e em Java…

Neste exemplo, criamos um (ou melhor, alguns) método chamado vetTipo que se comporta de acordo com o tipo de dado que ele recebe. Dessa forma, este método tem um comportamento polimórfico, ajustando o seu comportamento de acordo com o dado recebido. Bacana, não?

Bom pessoal, é isso. Espero que tenham gostado da série sobre orientação a objetos, e que tenha servido pra ajudar a compreender esses conceitos tão básicos. A partir dele, poderemos trabalhar com coisas mais elaboradas aqui no blog! Aguardem, pois vem bastante coisa legal por aí…

Abraço e até o próximo post! 😀

Datas em C++

Olá pessoal!

Vasculhando minhas coisas, achei aqui uma classe bem simples em C++ para trabalhar com datas. Está bem “rústico”, já que quando eu fiz (uns 2 anos atrás), não tinha muita noção sobre boas práticas de programação. Mas, de qualquer forma, já serve para dar uma ajuda em projetos pequenos. Ah, os métodos são todos estáticos, de forma que você não precisa instanciar a classe para utilizá-los 😉

Classes e Métodos Abstratos

Uma Classe Abstrata é desenvolvida para representar entidades e conceitos abstratos. A classe abstrata é sempre uma superclasse que não possui instâncias (ou melhor, não pode ser instanciada). Ela define um modelo para determinada funcionalidade e geralmente fornece uma implementação incompleta – a parte genérica – dessa funcionalidade. Caso a classe abstrata não possua nenhuma implementação (possuindo apenas as assinaturas dos métodos), ela também pode ser chamada de Interface. Cada uma das classes derivadas da classe abstrata completa a funcionalidade da classe abstrata, adicionando um comportamento específico.

Geralmente, uma classe abstrata possui métodos abstratos. Esses métodos são implementados em suas subclasses com o objetivo de definir seu comportamento específico. O método abstrato define apenas a assinatura do método e, portanto, não tem código.

No caso do Java, em específico, ele diferencia uma classe abstrata de uma interface. Vamos ver no exemplo a seguir, como ficaria a implementação de uma classe abstrata em C++…

…e em Java…

Percebam, primeiramente, que em C++ a declaração da classe abstrata não ocorre explicitamente. Ela se torna abstrata pelo fato de possuir métodos sem implementação. Se declararmos um método em C++ como virtual e o implementarmos, fazemos com que a implementação dele por parte da subclasse torne-se opcional (o que não ocorre neste caso).

Ao herdarmos nas classes Lobo e Peixe as características da classe Animal, estamos automaticamente obrigando-as a implementar o método abstrato comer(), específico para cada uma das subclasses. Enquanto em C++ declaramos o método como virtual e colocamos um = 0 após a sua declaração, em Java precisamos especificar explicitamente que tanto a classe quanto o método são abstratos, através da palavra-chave abstract. Na hora de implementarmos em C++, não é necessário nada adicional, enquanto que em Java, é necessário adicionar a anotação @Override.

Apesar de não podermos instanciar a classe abstrata, podemos declará-la como um tipo, que aceitará qualquer uma de suas implementações (como ocorre nos exemplos). Porém, nesse caso, se as subclasses tiverem métodos específicos que não pertencem ao escopo da superclasse, eles não serão reconhecidos.

Em Java, ainda temos uma outra característica importante nesse sentido: o conceito de Interfaces. Na verdade, uma interface nada mais é que uma classe abstrata que possui somente assinaturas de métodos. Assim, o nosso exemplo acima ficaria da seguinte forma:

Se analisarmos nosso exemplo, o uso de interface na definição da classe Animal seria a alternativa mais correta. Percebam a diferença: não utilizamos a palavra-chave class, e os métodos não precisam da palavra abstract, já que isso já está implícito no conceito da interface. Além disso, ao herdarmos da interface, utilizamos a palavra implements em vez de extends.

Isso me remete a uma coisa interessante: ao contrário do C++, a linguagem Java não permite o uso de herança múltipla, ou seja, uma classe só pode estender uma outra classe. Porém, ela pode implementar diversas interfaces. Assim:

Bom pessoal, é isso. No próximo post desta série sobre Orientação a Objetos, veremos o conceito de polimorfismo. Estudem o assunto deste post de hoje, pois ele é a base para o entendimento do próximo post.

Documentando Código com Doxygen

Doxygen é um sistema de documentação C++, C, Java, Python, IDL (CORBA e variações da Microsoft), Fortran, VHDL, PHP, C# e algumas extensões de D. Bastante coisa, não? Ele é uma ferramenta open-source para geração de documentação e referências de código. A documentação é escrita através de marcações dentro do próprio código-fonte.

Ele pode ajudar de 3 maneiras:

  • ele pode gerar uma documentação online para o navegador (em HTML) e/ou referência offline (em Latex) a partir de um conjunto de códigos documentados. Também há suporte para gerar saída em RTF (Word), PostScript, PDF com links, HTML compactado e man pages do Linux. A documentação é extraída diretamente dos códigos;
  • Você pode também configurar o Doxygen para extrair a estrutura do código com base em arquivos não documentados, de forma a poder visualizar relações entre os vários elementos, o que inclui gráficos de dependência, diagramas de herança e diagramas colaborativos, todos gerados automaticamente;
  • E pode, ainda, gerar documentação padrão.

Altamente portável, roda em Linux, Windows e Mac OS X. Bastante interessante, não?

Mas como faço pra documentar o código?

A sintaxe do Doxygen segue basicamente dois estilos: baseado em Javadoc e baseado em Qt. O estilo Javadoc consiste em um bloco de comentário estilo C começando com dois *, dessa forma:

Já a forma em estilo Qt consiste em adicionar uma ! logo após a abertura do comentário:

Existem também outras formas de sintaxe, porém menos utilizadas que estas. Todas elas podem ser encontradas no manual do Doxygen.

Estes blocos de comentários são complementados com o uso de parâmetros que fornecerão os dados necessários para a ferramenta montar a documentação. Vejamos o exemplo:

O Doxygen possui várias tags para formatação da documentação, de forma a possibilitar, de fato, a construção de uma documentação bastante robusta. No exemplo, temos a tag brief que fornece uma breve descrição do elemento a ser documentado.

A seguir, vamos ver uma classe documentada utilizando o Doxygen tanto no estilo Javadoc quanto no estilo Qt.

 

Gerando a Documentação

Bom, o primeiro passo é baixar o Doxygen. No Windows, você pode baixar do site oficial mesmo, aqui. No Linux você pode baixar do site oficial, ou dos repositórios da sua distribuição (a maioria tem).

No caso especial do Windows, ele vem com o Doxywizard. Com ele, é bem fácil configurar o projeto.

No caso do Linux, o processo é um pouco mais trabalhoso, já que temos que fazer o processo de configuração manualmente. Primeiramente, navegue até a pasta onde os códigos ou o projeto está. Chegando lá, digite:

Com isso, um arquivo chamado Doxygen será gerado na pasta atual. A sintaxe deste arquivo é bastante similar à sintaxe de um Makefile comum. Após a configuração dos parâmetros, basta salvar e executar:

Neste link você obtém mais informações sobre as configurações do arquivo.

Assim, teremos algo parecido com isso ao gerarmos a documentação do código-fonte de exemplo:

Bacana, não? Aqui você encontra um exemplo de documentação de um projeto real gerada com o Doxygen.

Espero que seja útil a vocês, assim como foi pra mim! 😀

Até a próxima!

Herança

Olá leitores! Apesar de meio parada, a nossa série de posts sobre Orientação a Objetos continua! Hoje iremos tratar de um assunto de extrema importância dentro das técnicas de modelagem de sistemas orientados a objetos: os conceitos de Herança (ou Inheritance). Mas o que vem a ser herança?

A grosso modo, podemos dizer que programar utilizando herança é reaproveitar código que será comum a outras classes. Utilizar herança é descobrir semelhanças entre classes.

Herança lembra família… é o que é transmitido de uma geração para outra…

Projetar utilizando herança significa colocar código comum em uma classe e dizer às classes mais específicas que a classe comum (mais abstrata) é sua superclasse. Quando uma classe herda de outra, a subclasse herda da superclasse. A notação de herança em C++ é…

… e em Java…

Voltando aos conceitos, dizemos que um relacionamento de herança significa que a subclasse herdará os membros da superclasse. Os “membros de uma classe” são os seus métodos e suas variáveis de instância (ou atributos). Vamos a um exemplo prático. Temos uma classe chamada Médico que iremos especializar em Clínico Geral e Cirurgião. Na notação UML (Unified Modeling Language), teríamos algo assim:

Esta é a notação de herança na UML. A classe mãe (ou superclasse) ligada a suas filhas (subclasses) por uma linha, com um triângulo indicando a presença de herança. Bom, neste caso, temos a classe mais abstrata, chamada Médico que possui uma variável de instância chamada trabalhaNoHospital e um método chamado tratarPaciente(). As duas subclasses, Clínico Geral e Cirurgião, automaticamente possuem estes atributos em seu escopo. No caso do Clínico Geral, ele possui, adicionalmente, uma variável de instância chamada atendeEmCasa e um método receitar(), que são específicos dele. Já a classe Cirurgião sobrescreve o método tratarPaciente() e adiciona o método fazerIncisão().

Mas espere aí? O que significa sobrescrever um método?

No nosso exemplo, a superclasse Médico tem o seu método tratarPaciente(), pois é uma ação comum a todo médico. No caso da superclasse, todo médico faz um check-up em seu paciente. Esse é o comportamento comum. A classe Clínico Geral possui esse comportamento, ou seja, um médico clínico geral, ao tratar o paciente, realiza um check-up nele. Já no caso da classe Cirurgião, ele também trata o paciente. Porém, o seu tratamento consiste em realizar a cirurgia no tratamento. Apesar de ter o mesmo método, o seu comportamento é diferente da classe mãe. Assim, ela sobrescreve o método da superclasse, adicionando seu comportamento próprio.

Vamos ver, então, como ficaria isso nas linguagens C++…

… e Java…

Como podem perceber, a sintaxe para a realização de herança nas linguagens é bastante simples, tanto em C++, quanto em Java.

Para não estender muito o post e evitar deixá-lo demasiadamente complexo, vamos encerrando por aqui. Recomendo que, para um melhor aprendizado sobre o assunto, vocês devam procurar material extra, tentar implementar pequenas estruturas de herança, enfim, realmente praticarem. Só através da prática é que conseguirão amadurecer os conceitos e conseguirão englobá-los no desenvolvimento de seus aplicativos.

No próximo post desta série, falaremos sobre o conceito de classes e métodos abstratos, assunto bastante interessante na Orientação a Objetos.

Até lá!

Encapsulamento de Dados

Olá! Continuando a nossa série sobre orientação a objetos, vamos falar hoje sobre Encapsulamento de Dados. Na Orientação a objetos, o encapsulamento significa separar o programa em partes, o mais isoladas possível. Dessa forma, tem-se por objetivo tornar o software mais flexível, fácil de modificar e de criar novas implementações.

No encapsulamento de dados, o que fazemos é impedir o acesso direto aos atributos da classe, criando métodos tanto para obter os valores dos atributos, quanto para atribuir valores a eles. Os métodos que realizam tais tarefas são comumente chamados de setters e getters (dos verbos set e get, em inglês).

Dessa forma, a classe Cachorro que tínhamos em nosso primeiro post sobre OO, utilizando o encapsulamento de dados, ficaria assim:

Primeiro em C++…

…e depois em Java…

Uma das grandes vantagens do uso do encapsulamento de dados é controlar o acesso aos atributos da classe, evitando comportamentos inesperados. Por exemplo, se determinado atributo não pode ser negativo, você pode condicionar o seu método de atribuição (set) a não deixar valores negativos (não atribuindo nada ou atribuindo alguma valor válido padrão). Além disso, caso determinado valor deva ser privado e não deve possuir acesso externo à classe, basta não definir seus métodos de acesso.

Bom pessoal, é isso. Aparentemente um assunto simples, mas onipresente no projeto de sistemas utilizando a orientação a objetos. No próximo post da série, falaremos sobre outro conceito muito importante na OO: a Herança!

Até lá!

Esteganografia com Arquivos Bitmap em C++

Olá, pessoal! Este ano na faculdade tivemos um trabalho sobre esteganografia. Pra quem não conhece, a esteganografia é uma técnica no qual você esconde uma mensagem, de forma que, se ela for interceptada, o intruso nem saberá que ela existe. Combinada com técnicas de encriptação, ela se torna uma ótima forma de se transmitir conteúdo sigiloso.

Este trabalho consistia em ler um arquivo texto, escondê-lo em um arquivo Bitmap (o arquivo Bitmap é um dos mais utilizados na esteganografia pelo fato de não possuir compactação) e depois possibilitar-se extrair a mensagem do arquivo novamente.

Bom, como podem ver, o código está meio “poluído” (confesso que no começo tentei fazer uma coisa bem organizada, mas de acordo que o código foi dando muito problema, acabou “desandando” :D). Então, disponibilizo aqui a classe em C++ que implementa esteganografia em arquivos Bitmap.