aspectj - martinsfontespaulista.com.br · programação orientada a aspectos (poa), apresentando,...

16
AspectJ Programação Orientada a Aspectos com Java Diogo Vinícius Winck Vicente Goetten Junior Novatec Editora

Upload: trancong

Post on 09-Dec-2018

213 views

Category:

Documents


0 download

TRANSCRIPT

AspectJProgramação Orientada

a Aspectos com Java

Diogo Vinícius WinckVicente Goetten Junior

Novatec Editora

13

Capítulo 1As origens

Como muito bem observaram William J. Brown e seus amigos no livro AntiPatterns: Refactoring Software, Architectures, and Projects in Crisis, ainda estamos na Idade da Pedra em termos de tecnologia de desenvolvimento de software. Isso não apenas no que se refere às linguagens, mas se estende também a todas as fases do ciclo de desenvolvimento. Entretanto, não se pode desmerecer os avanços conquistados, afinal, três das maiores descobertas humanas são pré-históricas: a roda, o domínio do fogo e a escrita.

Este capítulo é destinado a contextualizar o leitor nas questões que deram origem à programação orientada a aspectos (POA), apresentando, assim, os principais conceitos de programação, focando principalmente a programação orientada a objetos (POO), os problemas desse paradigma, bem como os fenômenos provenientes desses problemas, chamados código emaranhado (tangled code) e código espelhado (scattering code).

14 AspectJ • Programação Orientada a Aspectos com Java

1.1 Da origem à programação estruturada

Na primeira forma de desenvolver software, os programadores inseriam os programas diretamente em código binário na memória principal do computador. Para tal, eram utilizados bancos de chaves. A probabilidade de erros era alta, a manutenção de códi-go, praticamente impossível e o reúso, um sonho distante. Essa abordagem pode ser chamada de programação binária.

O próximo passo foi dado por um (grande) sujeito chamado John von Neumann (1903-1957), contemporâneo de Einstein, que chegou a ser considerado o maior pen-sador daquele tempo. Ele sugeriu um modelo conhecido por computador seqüencial digital, que ainda hoje é seguido pela maioria dos projetos. Esse modelo propunha o armazenamento das instruções na memória do computador, para então executá-las. Desse ponto para a programação linear, e dela para o surgimento de linguagens de programação que introduziram o conceito de procedimentos, foi questão de tempo. A programação linear caracteriza-se pela criação de estruturas monolíticas de software, responsáveis por desempenhar todas as etapas de uma tarefa. Essa abordagem apresenta dificuldades para manter o controle da qualidade de software, a legibilidade do código gerado e o planejamento da solução de um problema.

A estratégia de dividir para conquistar é bastante comum para a solução de problemas complexos de qualquer natureza. Descartes, com sua proposta para o método científico, tornou-o um padrão. Foi o surgimento de linguagens de programação capazes de re-presentar o conceito de procedimentos que possibilitou trazer para as linguagens essa estratégia. Isso favoreceu a divisão de um problema em outros mais simples e menores, tornando-os compreensíveis e permitindo que mais profissionais pudessem trabalhar em uma mesma solução. Além disso, cada profissional adquiriu a capacidade de man-ter o foco em tarefas simples, sabendo que, se cada uma das etapas para a solução do problema atendesse ao requisito pedido, o problema seria resolvido, mesmo que fosse complexo e o profissional envolvido naquela etapa não o compreendesse.

O foco dessa abordagem são as ações que o sistema deve implementar para a solução dos problemas. O programador cria estruturas semi-autônomas e reutilizáveis nos seus programas: as sub-rotinas e as funções. A solução surge da chamada seqüencial dessas partes. As implementações passam a ter duas partes bem distintas: dados (ou variáveis) e processos (ou funções).

Uma das grandes vantagens desse modelo era a possibilidade da criação de biblio-tecas de funções (Figura 1.1), que possibilitavam bons níveis de reúso. As primeiras bibliotecas foram destinadas às funções matemáticas; depois, às questões de entrada/saída e tratamento de arquivos e assim por diante.

15Capítulo 1 • As origens

#include <stdio.h>int main(){printf(”Hello World”);

{

#include <stdio.h>int main(){ int numero =0;

printf(”Exemplo de uso”); printf(”digite um valor:”); scanf(”%d”,&numero);

Biblioteca de Entrada/Saídastdio.h

* p r i n t f* s c a n f* . . .

Figura 1.1 – Biblioteca de funções.

A simplicidade desse modelo é o que o torna tão atraente. A divisão de um problema em vários outros menores tornou-se uma metodologia comum no desenvolvimento de software. Comum, mas nem sempre viável, uma vez que a programação orientada a procedimento apresenta algumas limitações:

• A capacidade de reúso de código é limitada pela interdependência entre proce-dimentos. Um procedimento, para ser utilizado, precisa estar em um contexto de execução. Para poder reutilizar é necessário recriar o contexto em questão.

• A seqüência das chamadas pode se tornar demasiadamente emaranhada, o que dificulta muito a manutenção e a depuração de código. Esse problema afeta principalmente códigos estruturados de maneira deficiente. Caracteriza-se por procedimentos que chamam procedimentos que chamam, por sua vez, outros, formando uma teia de requisições incompreensíveis.

• Não há possibilidade de se criar procedimentos mais específicos a partir de outros mais genéricos.

• Não há encapsulamento dos dados, por conta da separação entre os procedimen-tos e os dados. Todos os procedimentos são acessíveis, não existindo meios de possibilitar a restrição. Outro ponto é que todo método que utiliza um conjunto de dados deve saber como fazê-lo, não existindo maneira para restringir ou validar o acesso dos procedimentos aos dados.

• Uma mudança na representação dos dados implica na alteração em cada local onde estes são acessados.

• A rigidez nas chamadas dos procedimentos compromete a interação dos usuários com os programas, assim como a possibilidade de desenvolver simulações em computadores.

16 AspectJ • Programação Orientada a Aspectos com Java

Com a incorporação de uma metodologia para restringir essas questões e disponi-bilizar ferramentas para análise e projeto dos sistemas e a incorporação de “regras de boas maneiras” – etapa da elaboração do código – criou a metodologia estruturada.

1.2 Programação modular

Na tentativa de melhorar as deficiências provenientes da programação orientada a procedimentos, foi proposta a programação orientada a módulos. Essa programação divide os programas em vários módulos, combinando procedimentos e dados. Esses módulos ocultam os detalhes internos do restante do programa, pois, quando outra parte do programa precisa usar um, o faz utilizando a interface disponibilizada. Uma das características dessa abordagem foi a introdução do conceito de estado interno. O estado interno é a combinação dos dados (na forma de atributos) de um módulo.

Existem duas restrições na programação orientada a módulos. A primeira é a incapa-cidade de estender um módulo, impossibilitando alterações incrementais. Só é possível o reúso de código por meio da agregação de módulos ou colaboração. Um exemplo do reúso de código por meio de colaboração pode ser observado na Listagem 1.1.

Listagem 1.1 – Módulos1 //módulotipoPessoa2 typedefstruct{3 charnome[50];4 intidade;5 }tipoPessoa67 //módulotipoAluno8 typedefstruct{9 tipoPessoapessoa;10 charmatricula[10];11}tipoAluno

A outra restrição é a limitação gerada pela falta de operadores de visibilidade. Todos os elementos de um módulo podem ser alterados diretamente, não existindo um meio para controlar o acesso aos recursos dos mesmos. Assim, um programador desinforma-do, ou mesmo, mal-intencionado, poderia fazer uso dos elementos que compunham o módulo e comprometer sua utilidade. Linguagens como o C incorporaram os módulos por meio da definição de tipos abstratos de dados. No C utiliza-se o operador struct para tal, conforme apresentado na Listagem 1.1.

17Capítulo 1 • As origens

1.3 Programação orientada a objetos

Uma função evidente dos computadores é utilizá-los para simular situações reais. A programação orientada a procedimentos falhava ao fornecer mecanismos para abstra-ção eficaz a essa classe de problemas. Além disso, a queda considerável dos preços dos equipamentos de informática na década de 1970, devido à popularização da computação pessoal estimulada pela Apple (mais exatamente em 1977), motivou diversas empresas de médio e pequeno porte a se aventurarem e informatizarem alguns de seus processos operacionais.

Nessa época, os conhecimentos e técnicas para desenvolvimento de software não eram suficientes para contornar alguns problemas de desenvolvimento de sistemas, neces-sidade que ficou ainda mais evidente com a crescente demanda do público consumidor de software. Os novos usuários dos sistemas não eram especialistas em computação, mas profissionais de outras áreas e cidadãos comuns. Era necessário criar interfaces que simulassem painéis de controle. Estava formado o cenário para o surgimento da Orientação a Objetos (OO).

A OO surgiu da necessidade de simular a realidade, criando uma abstração do coti-diano, na tentativa de representar as características relevantes dos objetos envolvidos no sistema que se tenta simular. Ao se definir um programa em termos de objetos, passa-se a compreender o software de um modo mais profundo, o que nos obriga a abordar as funções dos objetos em um nível conceitual.

Essa metodologia transportou o estado do sistema para dentro dos objetos, mas de maneira diferente daquela realizada na orientação a módulos, pois adicionou o conceito de visibilidade. Tal conceito permite impedir acessos diretos aos atributos e, por conseguinte, ao seu estado, podendo restringir o acesso aos métodos. Dessa forma, é possível controlar as operações.

Com esse avanço, tornou-se possível para a engenharia de software a habilidade de modelar e projetar software de maneira análoga à análise de um problema no mundo real, transformando o vocabulário do problema em código. O desenvolvedor aproveita a sintaxe do mundo real, composta pelo vocabulário e as relações utilizadas para descrever um determinado problema e também para a implementação do software.

Dentre os benefícios da utilização das técnicas orientadas a objetos, pode-se citar:

• Reúso

• Modularização

• Simplificação

• Redução dos custos de manutenção

18 AspectJ • Programação Orientada a Aspectos com Java

A programação orientada a objetos, de forma simplificada e incremental, adiciona herança e polimorfismo à programação modular. Os programas deixam de ser divididos em procedimentos/módulos e passam a ser divididos em objetos/classes. O reúso pode ser obtido por derivação de classes e também por composição de objetos (meio disponível na programação modular). O fluxo do programa é ditado pela interação entre os objetos, e não apenas pela chamada seqüencial de procedimentos.

1.3.1 Objeto versus classe

A idéia básica por trás da OO é a decomposição da solução em objetos definidos pelas de classes. Logo, a unidade fundamental da OO é o objeto, sendo cada objeto a instân-cia de uma classe. Essa unidade pode ser de vários tipos como, por exemplo, entidades físicas (aviões, robôs, cliente etc.) ou abstratas (listas, pilhas, filas).

Um objeto encapsula o estado e o comportamento de um dos elementos da solução. Eles são dinâmicos, existindo apenas em tempo de execução do programa. O uso racio-nal de objetos, obedecendo aos bons princípios associados à sua definição, conforme estabelecido no paradigma de desenvolvimento orientado a objetos, é a chave para o desenvolvimento de bons sistemas OO.

Toda linguagem de programação orientada a objetos oferece mecanismos para definir os tipos de objetos por meio do conceito de classes. Objetos são instâncias de classes; essas instâncias precisam ser criadas para que, com a sua manipulação, possam realizar seu trabalho. Uma analogia amplamente utilizada é a relação classe x objeto = fôrma x biscoito, conforme exemplificado na Figura 1.2.

Figura 1.2 – Analogia entre fôrma x biscoito e classe x objeto.

A classe pode ser definida como um agrupador para os objetos que possuem características e comportamentos semelhantes. Ela é a abstração dos objetos, que implementa as responsa-bilidades em um sistema; é um tipo de dado abstrato usado para modelar objetos no sistema. Nelas estão descritas as características (atributos) e os comportamentos (métodos).

A classe é projetada com base nos requisitos extraídos da fase de análise. Os requisi-tos irão compor os interesses necessários ao sistema e que precisam ser implementados na estrutura de código. Num sistema típico, um grande número de interesses deve ser definido para que o sistema alcance seu objetivo final.

19Capítulo 1 • As origens

As classes de um sistema podem relacionar-se. Por exemplo, para projetar um sistema que controle a venda de produtos e DVDs na Internet, um projetista OO incluiria ob-jetos como: produto, carrinho de compras, fornecedor, DVD e muitos outros, conforme demonstrado na Figura 1.3.

Produto Fornecedor

DVD

Carrinho

1+

2._*0._*

Figura 1.3 – Exemplificação do relacionamento entre classes.

1.3.2 Estado e comportamento

Um objeto, desde a instanciação até a sua destruição, mantém internamente informações destinadas a representar o momento no qual se encontra em função daquilo pelo que já passou, assim como em um jogo de corrida, em que um carro guardará dados referentes à velocidade, ajustes etc. As características representadas em objeto são denominadas de atributos. Ao conjunto dos valores combinados dos atributos em um dado momento é atribuído o nome de estado. Sempre que uma característica tiver o seu valor alterado, isso afetará diretamente o estado do objeto.

Comportamento é uma ação executada por um objeto em resposta a alguma mensa-gem ou mudança de estado. Os comportamentos são implementados pelas operações. As operações são procedimentos que acessam os atributos internos que podem efetuar alterações do estado de um objeto.

O conjunto de operações que possuem visibilidade pública forma a lista de serviços que um objeto disponibiliza. Essa lista é denominada interface do objeto. A interface informa o que um objeto pode fazer, mas não a forma de implementação. Para favore-cer o encapsulamento, os atributos só devem ser manipulados por outros objetos por meio de operações de acesso. Apenas as operações do próprio objeto podem alterar o valor de um atributo de forma direta. Essa abordagem evita que alterações ocorridas nos atributos levem a uma mudança em todos os locais que os acessem. Em um objeto ideal, por conseqüência do encapsulamento, o estado pode ser alterado apenas pela ativação de um comportamento originada de um elemento externo ao objeto ou ainda em resposta a um evento interno.

20 AspectJ • Programação Orientada a Aspectos com Java

Assim como no mundo real, na orientação a objetos o que efetivamente define um objeto são os seus comportamentos. Se tomarmos como exemplo um cliente, veremos que ele assume esse papel quando executa operações relacionadas à sua função, ou seja, tarefas ligadas a escolha, pagamento e compra de produtos. De uma forma mais clara e até mesmo simplista, tomemos como exemplo um cachorro. O que o define como tal são atitudes como latir, coçar, modo de andar e farejar. Quando ele morre deixa de ser um cachorro por deixar de operar como tal, passando a ser um animal morto, nada diferente de um gato morto, por exemplo. Voltando à orientação a objetos, é normal, em um mesmo sistema, que aquele que se comportava como cliente assuma o papel de funcionário ou fornecedor, realizando um novo conjunto de operações.

1.3.3 Troca de mensagem

Na orientação a objetos, um programa pode ser visto como um grupo de objetos que disponibilizam serviços que são solicitados por meio de troca de mensagens. A troca é análoga à chamada de métodos, entretanto, mais dinâmica, pois ela não determina qual implementação será executada. O objeto solicitante requisita a execução do serviço e não a dos métodos, cuja realização fica a encargo do objeto chamado.

O polimorfismo também contribui para o dinamismo das mensagens. Durante a execução, uma referência a um objeto pode, de fato, apontar para uma instância da mesma classe ou, ainda, para instâncias de classes mais especializadas. Por conseguinte, o método executado pode ter a implementação definida em uma das classes da árvore de herança.

Há certo grau de incerteza por trás do envio de mensagens no que se refere à opera-ção que será realmente executada (se de uma classe base ou especializada). Além disso, a sobrecarga de métodos também contribui para essa incerteza. Dentro de um mesmo objeto pode haver vários métodos (com mesmo nome, mas recebendo número e tipos de parâmetros diferentes) que implementam o mesmo serviço. Por esse motivo, o conceito de troca de mensagens é mais apropriado do que o conceito de chamada de métodos ou execução de operações, pois apenas em tempo de execução decide-se qual método será executado. Um exemplo de troca de mensagens pode ser observado na Figura 1.4, em que o objeto caixa requisita ao livro para que ele informe qual é o seu valor.

:LivroMensagem:getValor()

R$ 50,00

:Caixa

Figura 1.4 – Troca de mensagem.

21Capítulo 1 • As origens

1.4 Os três fundamentos da orientação a objetos

A orientação a objetos possui uma série de conceitos que funcionam como paradigma de desenvolvimento, tais como:

• Encapsulamento

• Ocultação de informações e implementações

• Classes

• Atributos

• Estado

• Identidade de Objeto

• Mensagens

• Comportamento

• Interface

• Serviço

• Herança

• Polimorfismo

• Generalização

Alguns desses conceitos listados estão intimamente ligados, como é o caso do encapsulamento e da ocultação de informações e implementações. Outros conceitos estão no cerne da OO, sendo elementares, tais como classe, estado e interface. Alguns autores, como nós, consideram que a OO é formada, além dos conceitos elementares, por três fundamentos:

1. Encapsulamento

2. Herança

3. Polimorfismo

1.4.1 Encapsulamento

O encapsulamento é um conceito anterior a OO, datado da década de 1940. Naquela época, alguns programadores perceberam que poderiam agrupar em um determinado lugar códigos que muitas vezes apareciam em conjunto dentro de um programa, atri-buindo um nome a esse grupo. Sempre que fosse necessário, poder-se-ia ser requisitar a execução do grupo utilizando o nome atribuído. Dessa forma, criou-se o conceito de sub-rotina.

22 AspectJ • Programação Orientada a Aspectos com Java

Na OO, encapsulamento é o pacote de operações e atributos que representa o estado em um tipo de objeto, de tal forma que o estado é acessível ou modificável somente pela interface provida pelo encapsulamento, promovendo a proteção e ocultação da estrutura interna de cada objeto. O encapsulamento protege os dados contra as uti-lizações arbitrárias que fujam aos objetivos propostos pelo projeto da classe que deu origem ao objeto.

Por meio do encapsulamento, as entidades de software passam a ser como uma caixa-preta, pois o que se tem é apenas uma perspectiva externa, sem preocupar-se com questões internas do objeto. O solicitante de determinada operação para a caixa-preta não precisa se preocupar com detalhes da sua implementação. Observe a Figura 1.5.

Caixa preta?

Interface

Mensagem Mensagem

Mens

agem

Mens

agem

Figura 1.5 – Caixa preta recebendo mensagens.

1.4.2 Herança

A herança é um mecanismo para reúso de código que possibilita a criação de uma classe baseada em uma outra existente. A nova classe irá receber as características da classe-base, utilizando as partes comuns e especializando os pontos onde a classe-filha necessita de um comportamento mais especializado. Ela representa a relação “é um” entre as classes derivadas e especializadas. A herança traz, para o desenvolvimento de sistemas, o conceito Aristotélico (e natural) de classificação. Essa concepção aumenta a inteligibilidade do sistema, pois permite fazer inferências sobre os objetos a partir da sua classificação.

Há três maneiras principais de se utilizar a herança:

• para reutilização de implementação;

• para diferença;

• para substituição de tipo.

23Capítulo 1 • As origens

A herança de implementação visa, principalmente, o reúso de uma classe para im-plementação de outra, sem que sejam levadas em consideração questões de relação de responsabilidade entre as classes. Esse é o modo mais deficiente de herança. É normal, para o desenvolvedor, cometer erros conceituais no desenvolvimento de sistemas em busca da reutilização máxima do código. Um exemplo seria o caso de uma classe C que possui um método M1. A classe D precisa implementar um comportamento que se assemelha ao implementado em M1. Nesse caso, a herança de implementação resol-veria, pois, tornando D uma classe especializada de C, seria possível para D fazer uso do código de M1. Como não há relação de responsabilidade entre as classes C e D, essa forma de herança ocasionaria um erro conceitual no sistema. A Figura 1.6 exemplifica a herança de implementação, em que uma classe Funcionario implementa o método comprarDesconto. Esse mesmo método aparece modelado na classe Cliente. Como o objetivo da herança de implementação é reutilizar o código, o desenvolvedor pode sentir-se direcionado a derivar a classe Clienteda classe Funcionario,mesmo que isso leve a um erro conceitual, pois não é possível enquadrar cliente na relação “éum”com funcionário. O código das classes Funcionarioe Clientepodem ser observados na Listagem 1.2.

+comprarDesconto(desconto;double)

Funcionario

+comprarDesconto(desconto;double)

Cliente

Figura 1.6 – Exemplo de herança de implementação.

Listagem 1.2 – Herança de implementação1 //classeFuncionario2 classFuncionario{3 // atributos e outros métodos de funcionário

4 publicvoidcomprarDesconto(doubledesconto){5 //código referentes ao comportamento

6 }7 }89 //classe Cliente10 classClienteextendsFuncionario{11 // atributos e outros métodos de Cliente

12 // por herança, já está disponível o método comprarDesconto

13 }

A herança para diferença também pode ser chamada de especialização de classes. Essa forma é aplicada para criar uma nova classe que apresente algumas características diferentes de uma classe que já existe. Adiciona-se à nova classe apenas o código que a

24 AspectJ • Programação Orientada a Aspectos com Java

torna desigual à herdada. Esse conceito permite a programação incremental. Podemos citar como exemplo o caso de uma classe C, cujo comportamento implementado pelo método M1, em determinada situação, não se mostra adequado à solução do problema. Nesse caso, cria-se uma classe C1, derivada de C. Em C1, o método M1 é reimplementado de modo a ser apropriado à solução. Observe a Figura 1.7. Para a elaboração das clas-ses Cliente e ClienteEspecial, há diferença no método de comprarDesconto. Todo ClienteEspecialpossui a relação éumcom a classe Cliente. O código das classes Clientee ClienteEspecialpode ser observado na Listagem 1.3.

Cliente Cliente Especial

+comprarDesconto(desconto;double) +comprarDesconto(desconto;double)

Figura 1.7 – Exemplo de herança para diferença.

Listagem 1.3 – Herança para diferença1 //classeCliente2 classCliente{3 // atributos e métodos de Cliente

4 publicvoidcomprarDesconto(doubledesconto){5 //código referentes ao comportamento

6 }7 }89 // classe ClienteEspecial

10 classClienteEspecialextendsCliente{11 // atributos e outros métodos de funcionário

12 publicvoidcomprarDesconto(doubledesconto){13 /* implementação de comprarDesconto com comportamento

14 difente */15 }16 }

A herança para substituição é uma conseqüência direta da especialização. Ela permite que se descrevam relacionamentos capazes de referenciar tipos de objetos diferentes, mas que componham a mesma árvore de herança. É o tipo de herança mais interessante. Por exemplo, dada uma classe C, que possua um atributo M do tipo A. A classe A possui duas especializações: A1 e A2. O atributo M pode ser tanto instância de A, quanto instâncias de A1 e A2. Assim, a classe C pode se adequar a soluções específicas, na medida em que utili-za um ou outro tipo de classe para a instância de M. A implementação das classes Conta,ContaEspeciale ContaUniversitaria, conforme a Figura 1.8, demonstra a herança para substituição. O código das classes pode ser observado na Listagem 1.4.

25Capítulo 1 • As origens

Conta

+sacar(valor;doble)

ContaEspecial

+sacar(valor;doble)

+sacar(valor;doble)

ContaUniversitária

#conta:Conta

Cliente

Figura 1.8 – Exemplo de herança de substituição.

Listagem 1.4 – Herança para substituição1classConta{2//atributos e métodos de conta3publicvoidsacar(doublevalor){4//código de sacar5}6}78classContaEspecialextendsConta{9//atributos e métodos de ContaEspecial10publicvoidsacar(doublevalor){11// código de sacar específico12// da classe ContaEspecial13}14}15classContaUniversitariaextendsConta{16//atributos e métodos de ContaUniversitaria17publicvoidsacar(doublevalor){18// código de sacar específico19// da classe ContaUniversitaria20}21}2223classCliente{24protectedContaconta;// o atributo conta25/* pode apontar para uma instância de Conta,26 ContaUniversitaria ou ContaEspecial. */27//outros atributos e métodos de Cliente28}

26 AspectJ • Programação Orientada a Aspectos com Java

1.4.3 Polimorfismo

O significado literal de polimorfismo é ter muitas formas. No que se refere à OO, é o nome dado à capacidade de uma mesma referência como, por exemplo, um ponteiro para objetos, representar implementações diferentes, selecionadas por algum mecanismo automático. Assim, permite-se que um mesmo identificador de método seja utilizado em mais de uma classe e, ainda, assuma implementações diferentes em cada uma delas. O polimorfismo está presente no cotidiano, por exemplo, quando se designa o verbo fechar para operações tão diferentes quanto fechar uma porta, um livro, uma caixa ou uma conexão com a Internet. Há quatro formas de polimorfismo:

• Inclusão

• Paramétrico

• Sobreposição

• Sobrecarga

O polimorfismo de inclusão, também chamado de polimorfismo puro, permite a criação de métodos capazes de receber parâmetros de várias classes diferentes, desde que façam parte de uma árvore de herança. Permite, ainda, que, em método, um parâ-metro do tipo de uma classe base possa ser utilizado para receber objetos do tipo das classes especializadas.

O polimorfismo paramétrico permite que se criem métodos e tipos genéricos. Pode-se, com ele, retirar das declarações de atributos parâmetros de métodos e tipos de retorno de métodos referências ao tipo de dado. A definição do tipo é postergada para o momento da execução. Esse tipo de polimorfismo não é comumente suportado pelas linguagens OO, por exemplo, não há suporte na versão atual do Java.

O polimorfismo de sobreposição dá suporte para que seja possível a especialização de classes. Esse tipo de polimorfismo permite, em uma classe especializada, redefinir em um método implementando um novo comportamento. A sobrecarga permite que um mesmo identificador seja utilizado para nomear vários métodos diferentes em uma mesma classe. Cada método difere apenas no número e no tipo de seus parâmetros.

1.5 Exercícios

1. Quais são as vantagens da programação orientada a procedimentos?

2. Qual é a diferença entre um módulo e uma classe?

3. Defina o que é o estado de um objeto.

4. O que faz parte da interface de um objeto?

27Capítulo 1 • As origens

5. Qual a relação entre estado e comportamento?

6. Sobre Encapsulamento, complete com V (verdadeiro) ou F (falso):

( ) é a característica da orientação a objetos de ocultar partes independentes da implementação

( ) é o mecanismo que permite basear uma nova classe na definição de uma classe existente

( ) significa que um identificador pode representar implementações diferentes

( ) é um mapeamento do tipo árvore de relacionamentos que se formam entre classes

7. O que é reúso de código por derivação?

8. O que é reúso de código por colaboração?

9. Diferencie herança de implementação de herança de substituição.

10. Assinale as relações de classificação/herança incorretas:

( ) mesa é um tipo de móvel.

( ) veículo é um tipo de carro.

( ) passaporte é um tipo de documento.

( ) cachorro é um mamífero.

( ) pessoa é um tipo de aluno.

11. Sobre polimorfismo, complete com V (verdadeiro) ou F (falso):

( ) é a característica da orientação a objetos de ocultar partes independentes da implementação.

( ) é o mecanismo que permite basear uma nova classe na definição de uma classe existente.

( ) significa que um identificador pode representar implementações diferentes.

( ) é um mapeamento do tipo árvore de relacionamentos que se formam entre classes.

12. Quais são os quatro tipos de polimorfismo?

13. Qual a relação existente entre a mudança da programação binária para a proce-dural, desta para a modular e, por fim, para a orientada a objetos?