PROGRAMAÇÃO ORIENTADA A OBJETO - POO



PROGRAMAÇÃO ORIENTADA A OBJETO

Introdução

  Existem no mercado muitos livros e outras referências bibliográficas apresentando os conceitos da Tecnologia de Objetos e os benefícios de sua utilização. Além disso, a grande maioria dos softwares comerciais, especialmente os do ambiente Windows, já incorporam características orientadas a objetos. Porém, ainda percebe-se muito ceticismo na comunidade de profissionais de desenvolvimento de sistemas organizacionais. Em parte pela falta de cultura na utilização de metodologias, e muitas vezes por não acreditarem que é possível aplicar os conceitos OO em suas empresas.

  São comuns casos em que se aplica análise baseada em objetos para especificação do sistema, mas a implementação é feita em um ambiente que não suporta a orientação a objetos. Em outros casos, parte-se de uma definição tradicional de sistema e o software é construído com uso de algum mecanismo de orientação a objetos, porém sem respeitar uma arquitetura verdadeiramente orientada a objetos. Em ambas as situações, não se obtêm todos os benefícios associados à Tecnologia de Objetos, e as iniciativas acabam sendo consideradas frustrantes ou pouco vantajosas.

  A fim de se alcançar tais benefícios, é necessária a aplicação da orientação a objetos em todo o ciclo de desenvolvimento do sistema. Para tanto, obviamente, é preciso um completo entendimento da tecnologia e a obtenção de respostas para questões não abordadas pela bibliografia disponível.

 

Programação Orientada a Objetos

  Programação orientada a objetos (POO) é uma metodologia de programação adequada ao desenvolvimento de sistemas de grande porte, provendo modularidade e reusabilidade. A POO introduz uma abordagem na qual o programador visualiza seu programa em execução como uma coleção de objetos cooperantes que se comunicam através de mensagens. Cada um dos objetos é instância de uma classe e todas as classes formam uma hierarquia de classes unidas via relacionamento de herança. Existem alguns aspectos importantes na definição de POO:

  Usa objetos, e não funções ou procedimentos como seu bloco lógico fundamental de construção de programas

  Objetos comunicam-se através de mensagens

  Cada objeto é instância de uma classe

  Classes estão relacionadas com as outras via mecanismos de herança

  Programação orientada a objetos dá ênfase à estrutura de dados, adicionando funcionalidade ou capacidade de processamento a estas estruturas. Em linguagens tradicionais, a importância maior é atribuída a processos, e sua implementação em subprogramas. Em uma linguagem como Pascal, procedimentos ativos agem sobre dados passivos que foram passados a eles. Em linguagens orientadas a objetos, ao invés de passar dados a procedimentos, requisita-se que objetos realizem operações neles próprios.

  Alguns aspectos são fundamentais na definição de programação orientada a objetos, que serão devidamente investigados neste trabalho:  

  Abstração de dados

  Objetos

  Mensagens

  Classes

  Herança

  Polimorfismo

Objetos

  Na visão de uma linguagem imperativa tradicional (Pascal, C, COBOL, etc.), os objetos aparecem como uma única entidade autônoma que combina a representação da informação (estruturas de dados) e sua manipulação (procedimentos), uma vez que possuem capacidade de processamento e armazenam um estado local. Pode-se dizer que um objeto é composto de:

  Propriedades: são as informações, estruturas de dados que representam o estado interno do objeto. Em geral, não são acessíveis aos demais objetos.

  Comportamento: conjunto de operações, chamados de métodos, que agem sobre as propriedades. Os métodos são ativados (disparados) quando o objeto recebe uma mensagem solicitando sua execução. Embora não seja obrigatório, em geral uma mensagem recebe o mesmo nome do método que ela dispara. O conjunto de mensagens que um objeto está apto a receber está definido na sua interface.

  Identidade: é uma propriedade que diferencia um objeto de outro; ou seja, seu nome.

  Enquanto que os conceitos de dados e procedimentos são freqüentemente tratados separadamente nas linguagens de programação tradicionais, em POO eles são reunidos em uma única entidade: o objeto. A figura 1.1 apresenta uma visualização para um objeto.

Figura 1.1. Representação para um objeto

 

  No mundo real não é difícil a identificação de objetos (em termos de sistemas, objetos são todas as entidades que podem ser modeladas, não apenas os nossos conhecidos objetos inanimados). Como exemplo, em uma empresa pode-se identificar claramente objetos da classe empregado.

  Um empregado possui uma identidade própria, seu nome: José da Silva. Possui também propriedades como: endereço, idade, dependentes, salário, cargo, entre outras. O comportamento pode ser determinado por operações como: aumentar salário, listar dependentes e alterar cargo. As propriedades somente podem ser manipuladas através das operações definidas na interface do objeto, de modo que a forma de armazenamento das propriedades e a implementação das operações são desconhecidas pelas outras entidades externas (encapsulamento de informações).

Figura 1.2. O objeto "Empregado" 

  Benefícios proporcionados pelos objetos:

  Uma vez que objetos utilizam o princípio da abstração de dados, o encapsulamento de informação proporciona dois benefícios principais para o desenvolvimento de sistemas:

  Modularidade: o código fonte para um objeto pode ser escrito e mantido independentemente da código fonte de outros objetos. Além disso, um objeto pode ser facilmente migrado para outros sistemas.

  Ocultamento de informação: um objeto tem uma interface pública que os outros objetos podem utilizar para estabelecer comunicação com ele. Mas, o objeto mantém informações e métodos privados que podem ser alterados a qualquer hora sem afetar os outros objetos que dependem dele. Ou seja, não é necessário saber como o objeto é implementado para poder utilizá-lo.

Mensagens

  Um objeto sozinho não é muito útil e geralmente ele aparece como um componente de um grande programa que contem muitos outros objetos. Através da interação destes objetos pode-se obter uma grande funcionalidade e comportamentos mais complexos.

  Objetos de software interagem e comunicam-se com os outros através de mensagens. Quando o objeto A deseja que o objeto B execute um de seus métodos, o objeto A envia uma mensagem ao objeto B. Algumas vezes o objeto receptor precisa de mais informação para que ele saiba exatamente o que deve fazer; esta informação é transmitida juntamente com a mensagem através de parâmetros.

  Uma mensagem é formada por três componentes básicos:

  o objeto a quem a mensagem é endereçada (receptor)

  o nome do método que se deseja executar

  os parâmetros (se existirem) necessários ao método

Classe

  "É a definição dos atributos e funções de um tipo de objeto. Cada objeto individual é então criado com base no que está definido na classe. Por exemplo, homo sapiens é um classe de mamífero; cada ser humano individual é um objeto dessa classe."

  Objetos de estrutura e comportamento idênticos são descritos como pertencendo a uma classe, de tal forma que a descrição de suas propriedades pode ser feita de uma só vez, de forma concisa, independente do número de objetos idênticos em termos de estrutura e comportamento que possam existir em uma aplicação. A noção de um objeto é equivalente ao conceito de uma variável em programação convencional, pois especifica uma área de armazenamento, enquanto que a classe é vista como um tipo abstrato de dados, uma vez que representa a definição de um tipo.

  Cada objeto criado a partir de uma classe é denominado de instância dessa classe. Uma classe provê toda a informação necessária para construir e utilizar objetos de um tipo, cada instância pertence a uma classe e uma classe pode possuir múltiplas instâncias. Devido ao fato de todas as instâncias de uma classe compartilharem as mesmas operações, qualquer diferença de respostas a mensagens aceitas por elas, é determinada pelos valores das variáveis de instância.

Figura 2. Relacionamento entre classes e objetos

  A Figura 2. ilustra o relacionamento entre classes e objetos. Cada objeto instanciado a partir de uma classe possui as propriedades e comportamento definidos na classe, da mesma maneira que uma variável incorpora as características do seu tipo. A existência de classes proporciona um ganho em reusabilidade. pois o código das operações e a especificação da estrutura de um número potencialmente infinito de objetos estão definidos em um único local, a classe. Cada vez que um novo objeto é instanciado ou que uma mensagem é enviada, a definição da classe é reutilizada. Caso não existissem classes, para cada novo objeto criado, seria preciso uma definição completa do objeto.

Metaclasses  

  Uma metaclasse é uma classe de classes. Pode-se julgar conveniente que, em uma linguagem ou ambiente, classes também possam ser manipuladas como objetos. Por exemplo, uma classe pode conter variáveis contendo informações úteis, como:

  o número de objetos que tenham sido instanciados da classe até certo instante;

  um valor médio de determinada propriedade, calculado sobre os valores específicos desta propriedade nas instâncias (por exemplo, média de idade de empregados).

Benefícios proporcionados pelas classes:

  O maior benefício proporcionado pela utilização das classes é a reusabilidade de código, uma vez que todos os objetos instanciados a partir dela incorporam as suas propriedades e seu comportamento.

Métodos

  Um método implementa algum aspecto do comportamento do objeto. Comportamento é a forma como um objeto age e reage, em termos das suas trocas de estado e troca de mensagens.

  Um método é uma função ou procedimento que é definido na classe e tipicamente pode acessar o estado interno de um objeto da classe para realizar alguma operação. Pode ser pensado como sendo um procedimento cujo primeiro parâmetro é o objeto no qual deve trabalhar. Este objeto é chamado receptor. Abaixo é apresentada uma notação possível para o envio de uma mensagem (invocação do método)

  Construtores são usados para criar e inicializar objetos novos. Tipicamente, a inicialização é baseada em valores passados como parâmetros para o construtor. Destrutores são usados para destruir objetos. Quando um destrutor é invocado, as ações definidas pelo usuário são executadas, e então a área de memória alocada para o objeto é liberada. Em algumas linguagens, como C++, o construtor é chamado automaticamente quando um objeto é declarado. Em outras, como Object Pascal, é necessário chamar explicitamente o construtor antes de poder utilizá-lo.

  Um exemplo de utilização de construtores e destrutores seria gerenciar a quantidade de objetos de uma determinada classe que já foram criados até o momento. No construtor pode-se colocar código para incrementar uma variável e no destrutor o código para decrementá-la.

 Herança

  O conceito de herança é fundamental na técnica de orientação a objetos. A herança permite criar um novo tipo de objeto - uma nova classe - a partir de outra já existente.

  A nova classe mantém os atributos e a funcionalidade da classe da qual deriva; por isso, dizemos que ela "herda" as características daquela classe. Ao mesmo tempo, ela pode receber atributos e funções especiais não encontrados na classe original. Voltando ao exemplo da janela de um programa Windows, esse tipo de objeto poderia se chamar Janela. Ao criarmos um tipo de objeto para funcionar como caixa de diálogo é uma janela com atributos especiais, como a exibição de botões e opções.

  O novo tipo poderia se chamar JanelaDiálogo e herdaria as características da classe Janela, recebendo também os atributos exclusivos de uma caixa de diálogo.
Uma das vantagens da herança é a facilidade de localizar erros de programação. Por exemplo, caso um objeto derivado de outro apresente um erro de funcionamento; se o objeto original funcionava corretamente, é claro que o erro está na parte do código que implementa as novas características do objeto derivado. A herança permite, também, reaproveitar o código escrito anteriormente, adaptando-o às novas necessidades.
Isso é muito importante porque os custos de desenvolvimento de software são muitos elevados. A mão-de-obra altamente especializada é cara; o processo é demorado e sujeito a ocorrências inesperadas.

Polimorfismo

  Polimorfismo refere-se à capacidade de dois ou mais objetos responderem à mesma mensagem, cada um a seu próprio modo. A utilização da herança torna-se fácil com o polimorfismo. Desde que não é necessário escrever um método com nome diferente para responder a cada mensagem, o código é mais fácil de entender. Por exemplo, sem polimorfismo, para inserir um novo empregado, seria necessário o seguinte código:

  Colaborador1.InsereColaborador

  Gerente1.InsereGerente

  Presidente1.InserePresidente

  Neste caso, Colaborador1, Gerente1 e Presidente1 são objetos das respectivamente das classes Colaborador, Gerente e Presidente. Com o polimorfismo, não é necessário ter um método diferente para cada tipo de objeto. Pode-se simplesmente escrever o seguinte:

  Colaborador.Insere

  Gerente.Insere

  Presidente.Insere

  Neste exemplo, os três diferente empregados têm três diferentes métodos para ser inseridos, embora o tenha sido utilizado o método com o mesmo nome. No código para esses empregados, seria necessário escrever três métodos diferentes: um para cada tipo de empregado. O mais importante a ser observado é que as três rotinas compartilham o mesmo nome.

  Outra forma simples de polimorfismo permite a existência de vários métodos com o mesmo nome, definidos na mesma classe, que se diferenciam pelo tipo ou número de parâmetros suportados. Isto é conhecido como polimorfismo paramétrico, ou sobrecarga de operadores ("overloading"). Neste caso, uma mensagem poderia ser enviada a um objeto com parâmetros de tipos diferentes (uma vez inteiro, outra real, por exemplo), ou com número variável de parâmetros. O nome da mensagem seria o mesmo, porém o método invocado seria escolhido de acordo com os parâmetros enviados.

Benefícios proporcionados pelo polimorfismo

  Legibilidade do código: a utilização do mesmo nome de método para vários objetos torna o código de mais fácil leitura e assimilação, facilitando muito a expansão e manutenção dos sistemas.

  Código de menor tamanho: o código mais claro torna-se também mais enxuto e elegante. Pode-se resolver os mesmos problemas da programação convencional com um código de tamanho reduzido.

   

Relações entre Objeto, Classe e Herança

  Objetos, classes e o mecanismo de herança permitem a definição de hierarquias de abstrações, que facilitam o entendimento e o gerenciamento da complexidade dos sistemas estudados. Isto porque classes agrupam objetos com características iguais, enquanto herança estrutura classes semelhantes.

  A capacidade em classificar objetos e classes concede grande poder de modelagem conceitual e classificação, podendo expressar relações entre comportamentos, tais como classificação/instanciação, generalização/especialização e agregação/composição. Facilita a compreensão humana do domínio estudado e também o desenvolvimento de programas que o satisfaça. Pode-se dizer que a grande vantagem do paradigma de objetos é a possibilidade de expressar diretamente este poder de modelagem conceitual numa linguagem de programação orientada a objetos. Assim, o modelo conceitual proposto durante a etapa de análise não se perde nas etapas de projeto e implementação. O que em geral ocorre é a sua extensão.

Classificação/Instanciação

  A capacidade de classificar objetos (em classes) permite expressar relações do tipo classificação/instanciação. O relacionamento é feito a partir da observação de diversos fenômenos para categorização dos mesmos em grupos (classes), com base no conjunto de propriedades comuns a todos. Por exemplo, dois computadores, IBM PC e Machintosh, podem ser classificados como instâncias (objetos, modelos, ou espécimes) da classe (categoria) Microcomputador (Figura 3). A relação inversa é a de instanciação de uma publicação (IBM PC, por exemplo) a partir da classe Microcomputador.

Figura 3. Relação de classificação/instanciação

 Generalização/Especialização

  Este tipo de relação ocorre quando, a partir a observação de duas classes, abstraímos delas uma classe mais genérica. Por exemplo, as classes Microcomputador e Mainframe podem ser considerados casos especiais da classe Computador. Esta classe é considerada uma generalização das duas primeiras, que são chamadas de especializações da classe Computador (Figura 4). A idéia da generalização/especialização é a base para a classificação das espécies nas ciências naturais. Do ponto de vista de propriedades, o pressuposto é que as subclasses tenham todas as propriedades das classes de quem elas são especializações. Deve haver pelo menos uma propriedade que diferencie duas classes especializadas (subclasses) a partir da mesma classe genérica (superclasse). Este é o tipo de relação utilizado com o mecanismo de herança.

Figura 4. Relação de generalização/especialização

Composição/Decomposição

  A relação de composição (ou agregação)/decomposição permite que objetos sejam compostos pela agregação de outros objetos ou componentes. Neste relacionamento, são determinados que instâncias (objetos) de uma classe são compostas por instâncias de outras classes. Essa operação é denominada decomposição e a relação inversa, a formação de uma nova classe como um agregado de classes preexistentes, é denominada composição. Por exemplo, instâncias da classe Microcomputador são compostas por, entre outras, instâncias das classes Teclado e Vídeo (Figura 5).

Figura 5. Relação de composição/decomposição

A linguagem de programação SmallTalk

  Smalltalk é o único sistema de desenvolvimento de software orientado a objeto, construídos a partir de uma hierarquia de objeto inteiramente consistente. A classe objeto é a super-classe de todos os outros e define os protocolos comuns a todos os objetos no sistema. Define o comportamento default para a apresentação, cópia, comparação e inspeção de objetos. Tem a capacidade de manter as relações entre os objetos e transmitir as mensagens de um ascendentes para os seus descendentes.

  A Smalltalk é projetada para que cada componente do sistema esteja acessível ao usuário (exceto o núcleo) e possa ser apresentado para observação e manipulação. É fácil modificar qualquer coisa que promove erros em Smalltalk, mas isto também a torna flexível e altamente personalizada. A interface do usuário reflete as tentativas de criar uma linguagem visual para cada objeto. Foi o primeiro ambiente que exigiu uma interação direta com o usuário utilizando um dispositivo indicador (mouse) e ainda coloca nas mãos do desenvolvedor de software um maior controle do que qualquer outro software disponível no mercado.

Avaliação da POO

  A grande vantagem do paradigma de objetos é o seu caráter unificador: trata todas as etapas do desenvolvimento de sistemas e ambientes sob uma única abordagem. Nesse sentido, podemos ter análise, projeto, programação, banco de dados e ambientes orientados a objetos, eliminando as diferenças de "impedância" entre eles.

Vantagens da POO

  A POO tem alcançado tanta popularidade devido às vantagens que ela traz. Entre elas podemos citar:

  Reusabilidade de código

  Escalabilidade de aplicações

  Mantenabilidade

  Apropriação

  A reusabilidade de código é, sem dúvida, reconhecida como a maior vantagem da utilização de POO, pois permite que programas sejam escritos mais rapidamente. Todas as empresas sofrem de deficiência em seus sistemas informatizados para obter maior agilidade e prestar melhores serviços a seus clientes. Um levantamento feito na AT&T, a gigante das telecomunicações nos EUA, identificou uma deficiência da ordem de bilhões de linhas de código. Uma vez que a demanda está sempre aumentando, procura-se maneiras de desenvolver sistemas mais rapidamente, o que está gerando uma série de novas metodologias e técnicas de construção de sistemas (por exemplo, ferramentas CASE). A POO, através da reusabilidade de código, traz uma contribuição imensa nesta área, possibilitando o desenvolvimento de novos sistemas utilizando-se muito código já existente. A maior contribuição para reusabilidade de código é apresentada pela herança.

  Escalabilidade pode ser vista como a capacidade de uma aplicação crescer facilmente sem aumentar demasiadamente a sua complexidade ou comprometer o seu desempenho. A POO é adequada ao desenvolvimento de grandes sistemas uma vez que pode-se construir e ampliar um sistema agrupando objetos e fazendo-os trocar mensagens entre si. Esta visão de sistema é uniforme, seja para pequenos ou grandes sistemas (logicamente, deve-se guardar as devidas proporções).

  O encapsulamento proporciona ocultamento e proteção da informação. Acessos a objetos somente podem ser realizados através das mensagens que ele está habilitado a receber. Nenhum objeto pode manipular diretamente o estado interno de outro objeto. De modo que, se houver necessidade de alterar as propriedades de um objeto ou a implementação de algum método, os outros objetos não sofrerão nenhum impacto, desde que a interface permaneça idêntica. Isto diminui em grande parte os esforços despendidos em manutenção. Além disso, para utilizar um objeto, o programador não necessita conhecer a fundo a sua implementação.

  O polimorfismo torna o programa mais enxuto, claro e fácil de compreender. Sem polimorfismo, seriam necessárias listas enormes de métodos com nomes diferentes mas comportamento similar. Na programação, a escolha de um entre os vários métodos seria realizada por estruturas de múltipla escolha (case) muito grandes. Em termos de manutenção, isto significa que o programa será mais facilmente entendido e alterado.

  A herança também torna a manutenção mais fácil. Se uma aplicação precisa de alguma funcionalidade adicional, não é necessário alterar o código atual. Simplesmente cria-se uma nova geração de uma classe, herdando o comportamento antigo e adiciona-se novo comportamento ou redefine-se o comportamento antigo.

Desvantagens da POO

  Apesar de das inúmeras vantagens, a POO tem também algumas desvantagens, que incluem:

  Apropriação

  Fragilidade

  Linearidade de desenvolvimento

  A apropriação é apresentada tanto como uma vantagem como uma desvantagem, porque a POO nem sempre soluciona os problemas elegantemente. Enquanto que a mente humana parece classificar objetos em categorias (classes) e agrupar essas classes em relacionamentos de herança, o que ela realmente faz não é tão simples. Em vez disso, objetos com características mais ou menos similares, e não precisamente definidas, são reunidos em uma classificação. A POO requer definições precisas de classes; definições flexíveis e imprecisas não são suportadas. Na mente humana, essas classificações podem mudar com o tempo. Os critérios para classificar objetos podem mudar significativamente. A apropriação utilizada na POO torna-a muito rígida para trabalhar com situações dinâmicas e imprecisas.

  Além disso, algumas vezes não é possível decompor problemas do mundo real em uma hierarquia de classes. Negócios e pessoas têm freqüentemente regras de operações sobre objetos que desafiam uma hierarquia limpa e uma decomposição orientada a objetos. O paradigma de objetos não trata bem de problemas que requerem limites nebulosos e regras dinâmicas para a classificação de objetos.

  Isto leva ao próximo problema com POO: fragilidade. Desde que uma hierarquia orientada a objetos requer definições precisas, se os relacionamentos fundamentais entre as classes chave mudam, o projeto original orientada a objetos é perdido. Torna-se necessário reanalisar os relacionamentos entre os objetos principais e reprojetar uma nove hierarquia de classes. Se existir uma falha fundamental na hierarquia de classes, o problema não é facilmente consertado. A mente humana adapta-se continuamente, e geralmente adequadamente, a situações novas. Ela encontra maneiras de classificar objetos no ambiente automaticamente. Enquanto a nossa mente tem essa capacidade, os ambientes POO não são tão bem equipados.

  Em virtude dessa fragilidade, a POO requer análise e projeto frontal para assegurar que a solução é adequada. Com isto existe uma tendência em criar uma abordagem linear de desenvolvimento, em vez de cíclica. Infelizmente, alguns problemas são "perversos", o que significa que não se sabe como resolver um problema até efetivamente resolvê-lo. Tais problemas desafiam até mesmo os melhores projetistas e analistas de sistemas. Utilizar a abordagem de Desenvolvimento Rápido de Aplicações (RAD - Rapid Application Development, é utilizada pelo Delphi, SQL-Windows e outros ambientes de desenvolvimento para Windows adequados à prototipagem) com ciclos entre o projeto do protótipo, construção e realimentação do usuário é algumas vezes uma boa maneira de resolver problemas "perversos". Entretanto, a POO necessita de um projeto cuidadoso da hierarquia de classes, o que pode elevar os custos da sua utilização em um ambiente RAD.

 
Autor: ADILSON BENEVIDES SOBRAL


Artigos Relacionados


Quem Sou Eu...quem és Tu ?!

TraÇos De Um Povo

Brasil, Dilema De Um Povo Esquecido

Moradores Do Tempo

Maior LiÇÃo De Vida

DiÁrio De Um Depressivo

A Análise De Swot Da Seduc/ma