.NET
- Uma revisão sobre a programação orientada a objetos(POO)
|
Neste artigo vou revisar os
conceitos chaves sobre o paradigma da programação orientada a
objetos sem rodeios. |
A Programação Orientada a
Objetos (POO) é uma abordagem para
desenvolvimento de software no qual a estrutura do software é
baseada em objetos que interagem uns com os
outros para realizar uma tarefa.
Obs: Você vai encontrar com
freqüência a acróstico OOP - Oriented Object
Programming.
Essa interação toma a forma de mensagens que são
trocadas entre os
objetos sendo que em resposta a uma mensagem um objeto pode
executar uma ação ou método.
|
O
mundo é um sistema orientado a objetos |
Se você parar para pensar em como
você realiza as suas tarefas no mundo ao seu redor você vai
perceber que interage em um mundo orientado a objetos.
1- Para provar isso pense em uma
tarefa que você realiza com freqüência : Ir ao Shopping.
- Se você quiser ir até ao
Shopping, por exemplo, você interage com um objeto
veículo (Carro, ônibus, táxi, etc. Isso não
importa. Só não vale ir a pé...) ;
- Um objeto veículo consiste de
outros objetos que interagem uns com os outros
para realizar a tarefa de levar você até ao Shopping;
- Você coloca a chave no
objeto ignição e o aciona;
- Este por sua vez, envia
uma mensagem (através de um sinal elétrico)
para o objeto partida, que interage com o objeto
motor para ligar o veículo;
- Como um motorista você está
isolado da lógica de como os objetos do sistema trabalham
em conjunto (enviando mensagens) para ligar o
veículo;
- Você só tem apenas que iniciar a
seqüência de eventos, executando o método iniciar do objeto
ignição e dessa forma você então espera por uma resposta
(mensagem) de sucesso ou fracasso.
|
O
Software e o paradigma da orientação a objetos |
Da mesma forma, os usuários de
programas de software são isolados a partir da lógica
necessária para realizar uma tarefa.
Vejamos um exemplo comum do dia dia:
Imprimir uma página.
- Quando você imprime uma página
em seu processador de texto, você inicia a ação clicando em um
botão Imprimir;
- Você está isolado do
processamento interno que precisa ocorrer, você só espera
por uma resposta dizendo-lhe se a página foi impressa ou não;
- Internamente, o objeto
botão interage com um objeto impressora
que interage com a impressora para realizar a tarefa de imprimir
a página;
|
Os
conceitos sobre orientação a objetos são antigos |
Os Conceitos POO começaram a
aparecer em meados dos anos 1960 com uma linguagem de programação
chamada Simula e evoluiu nos anos 70 com o
advento do Smalltalk.
Smalltalk-80,
ou simplesmente Smalltalk, é uma
linguagem de programação orientada a objeto fracamente
tipada.
Em Smalltalk tudo é objeto: os números, as classes, os
métodos, blocos de código, etc.
Não há tipos primitivos, ao contrário de outras
linguagens orientadas a objeto; strings, números e
caracteres são implementados como classes em Smalltalk,
por isso esta linguagem é considerada puramente
orientada a objetos. Tecnicamente, todo elemento de
Smalltalk é um objeto de primeira ordem.
(http://pt.wikipedia.org/wiki/Smalltalk) |
Embora os desenvolvedores de
software não abraçassem de imediato esses avanços nas
linguagens POO, a metodologia orientada a objetos continuou a
evoluir.
Em meados dos anos 80 houve um
ressurgimento do interesse nas metodologias orientadas a objetos.
Neste momento linguagens POO
como C++ e
Eifle tornaram-se popular
junto aos programadores dos computadores pessoais (Personal
Computer).
Na década de 90 a POO
continuou a crescer em popularidade, principalmente com o advento
do Java, que atraiu uma grande quantidade de seguidores.
Em 2002, em conjunto com o lançamento
do Framework. NET, A Microsoft introduziu uma nova linguagem POO,
chamada C# (pronuncia-se
C-sharp) e remodelou a
linguagem Visual Basic para que ela se tornasse
uma linguagem com suporte total a POO.
|
Usando
a abordagem da orientação a objetos (POO). Por quê ? |
Por que a abordagem do
paradigma POO tem se desenvolvido de forma a ser amplamente
utilizada para resolver os problemas de negócio dos dias atuais
?
Durante os anos 70 e 80, as
linguagens procedurais como C, Pascal, Fortran, etc. foram amplamente usadas para desenvolver
sistemas de negócios de software.
Pascal
é uma linguagem de programação estruturada, que
recebeu este nome em homenagem ao matemático Blaise
Pascal.
Foi criada em 1970 pelo suíço Niklaus Wirth, tendo em
mente encorajar o uso de código estruturado.(http://pt.wikipedia.org/wiki/Pascal_(linguagem_de_programa%C3%A7%C3%A3o)) |
Linguagens procedurais
organizam o programa de forma linear e utilizam um fluxo de
execução de cima para baixo. Em outras palavras, o programa é
executado em uma série de passos que são executados um após o
outro.
O
termo Programação procedural (ou
programação procedimental) é as vezes utilizado
como sinônimo de Programação imperativa (Paradigma
de programação que especifica os passos que um programa
deve seguir para alcançar um estado desejado), mas
o termo pode se referir (como neste artigo) a um
paradigma de programação baseado no conceito de
chamadas a procedimento.
Procedimentos, também conhecidos como rotinas,
subrotinas, métodos, ou funções (que não devem ser
confundidas com funções matemáticas, mas são
similares àquelas usadas na programação funcional)
simplesmente contém um conjunto de passos computacionais
a serem executados. Um dado procedimento pode ser chamado
a qualquer hora durante a execução de um programa,
inclusive por outros procedimentos ou por si mesmo.(http://pt.wikipedia.org/wiki/Programa%C3%A7%C3%A3o_procedural) |
Este tipo de programação
funcionou bem para pequenos programas que consistiam de algumas
centenas linhas de código, mas connforme os programas se
tornaram maiores e mais complexos eles se tornaram mais difíceis
de gerir e depurar.
Em uma tentativa de
gerenciar o tamanho cada vez maior de programas, a programação
estruturada foi introduzida para quebrar o código em segmentos
gerenciáveis chamados de funções ou procedimentos.
Isso foi uma melhoria, mas
como os programas passaram a ter que realizar funcionalidade de
negócios mais complexas e interagirem com outros sistemas, as
seguintes deficiências da metodologia de programação
estrutural começaram a aflorar:
|
Os
problemas da abordagem procedural |
- A manutenção dos
programas ficou mais difícil;
- Ficou mais difícil
realizar alterações nas funcionalidades existentes sem
alterar toda a funcionalidade do sistema;
- Novos programas eram
essencialmente construídos a partir do zero.
Conseqüentemente, havia pouco retorno sobre o
investimento de esforços anteriores;
- A programação não
era uma tarefa voltada para o desenvolvimento em equipe e
os programadores tinham que conhecer todos os aspectos de
como o programa funcionava, dessa forma não se conseguia
isolar os seus esforços em um aspecto do sistema;
- Ficou muito difícil
traduzir os modelos de negócios existentes em modelos de
programação;
- A integração entre
os diversos sistemas existentes passou a ser um pesadelo;
Além dessas deficiências,
a evolução dos sistemas computacionais causou uma forte tensão
sobre a abordagem adotada pelos estruturais, e os usuários
passaram a exigir uma abordagem mais intuitiva e menos estruturada
de forma a interagir de forma mais fácil com os programas.
Além disso os sistemas
evoluíram para um modelo distribuído onde a lógica de negócio,
a interface com o usuário e a camada de acesso a dados estavam
fracamente acopladas e eram acessadas através da internet e
intranet.
Como resultado, muitos
desenvolvedores de software passaram a adotar a metodologia
orientada a objetos e as linguagens de programação com
suporte a POO para
resolver esses problemas e com o objetivo de obter os seguintes
benefícios:
|
Os
benefícios da abordagem Orientada a objetos(POO) |
- Uma transição mais
intuitiva dos modelos de análise de negócios para a
implementação do software;
- A capacidade de manter
e implementar mudanças nos programas de forma mais
rápida e eficiente;
- A capacidade criar
sistemas de software através de um processo de equipe,
permitindo a especialistas trabalharem em partes do
sistema;
- A capacidade de
reutilização de componentes de código em outros
programas e a aquisição de componentes escritos por
desenvolvedores de terceiros para aumentar a
funcionalidade do seus programas com pouco esforço;
- Uma melhor
integração com sistemas distribuídos e com baixo
acoplamento;
- Uma melhor
integração com os sistemas operacionais modernos;
- A capacidade de criar
uma interface de usuário gráfica intuitiva para os
usuários;
|
As
características da Programação Orientada a Objetos
(OOP) |
Os conceitos básicos e os
termos comum a todas as linguagens orientada a objetos.
|
Objetos |
Conforme disse
anteriormente, vivemos em um mundo orientado a objetos. Você é
um objeto. Você interage com outros objetos.
Na verdade, você é um
objeto com dados como altura e cor do cabelo. Você também tem métodos
que você executa ou são executadas em você, como comer e
andar.
Então, o que são objetos
?
Em termos da POO,
um objeto
é uma estrutura que incorpora dados e procedimentos para
trabalhar com esses dados.
Assim, um objeto é capaz de
armazenar estados através de seus atributos e reagir a mensagens
enviadas a ele, assim como se relacionar e enviar mensagens a
outros objetos.
Por exemplo, se você
estivesse interessado em tratar os dados associados com produtos
em estoque, você criaria um objeto produto que é responsável
pela manter e trabalhar com os dados relativos dos produtos.
Se você desejar ter a
capacidade de impressão em seu aplicativo, você vai trabalhar
com um objeto impressora que é responsável pelos dados e métodos
utilizados para interagir com suas impressoras.
No contexto da OOP um
objeto é uma instância de uma classe. Onde a classe é o tipo e
o objeto é uma instância do tipo. Ex: classe Cliente ->
objeto macoratti.
|
Abstração |
Quando você interage com
objetos no mundo, que são muitas vezes preocupada apenas com um
subconjunto de suas propriedades. Sem esta capacidade de abstrair
ou filtrar as propriedades de objetos você teria muita
dificuldade para processar o excesso de informação que
bombardeia você e se concentrar na tarefa em mãos.
Como resultado de abstração,
quando duas pessoas diferentes interagem com o mesmo objeto, eles
freqüentemente lidam com um subconjunto de atributos diferentes.
Quando dirijo meu carro, por exemplo, eu preciso saber a
velocidade do carro e a direção em que ele está indo.
Se o carro for automático,
não preciso saber nada sobre o giro do motor, assim essa informação
é filtrada. Por outro lado, esta informação seria fundamental
para um motorista de um carro não automático ou de carro de
corrida e assim ela não seria filtrada.
Ao construir objetos em
aplicações OOP é importante incorporar esse conceito de abstração.
Se você estivesse
construindo uma aplicação para remessa de produtos, você
poderia construir um objeto produto com atributos como tamanho e
peso. A cor do item seria uma informação irrelevante e
filtrada. Por outro lado, ao construir uma aplicação de entrada
de pedidos, a cor pode ser importante e seria incluído como um
atributo do objeto produto.
Dessa forma abstração
é a habilidade de concentrar-se nos aspectos essenciais de um
contexto qualquer, ignorando características menos importantes
ou não essenciais. Em modelagem orientada a objetos, uma classe
é uma abstração de uma entidade existente no domínio do
sistema de software. Exemplos de classes: Pedido, Produto,
Cliente, etc.
|
Encapsulamento |
Outra característica
importante da OOP é o encapsulamento.
Encapsulamento
é o processo no qual o acesso direto aos dados de um objeto não
é permitido, em vez disso, ele está escondido.
Se você quiser ter acesso
aos dados, você tem que interagir com o objeto responsável
pelos dados. Em uma aplicação para inventário, se você
quisesse visualizar ou atualizar informações sobre os produtos,
você teria que trabalhar com o objeto produto e para ler os
dados, você poderia enviar uma mensagem ao objeto Produto e o
objeto Produto, então, iria ler o valor do dado e enviar de
volta uma mensagem dizendo qual é o valor. O objeto produto
define quais operações podem ser realizadas sobre os dados do
produto.
Se você enviar uma
mensagem para modificar os dados eo objeto produto determina que
é um pedido válido, ele irá executar a operação para você e
enviar uma mensagem de volta com o resultado.
O encapsulamento esta
presente no nosso dia a dia. Pense em um departamento de recursos
humanos (o departamento de pessoal - DP). Um
departamento pessoal geralmente encapsula a informação sobre os
empregados determinando como e quando esses dados podem ser
manipulados. Qualquer solicitação ou atualização dos dados de
um empregado tem que ser filtrado pelo DP sendo que ninguém pode
ter acesso aos dados a não ser através do departamento pessoal.
O encapsulamento dos dados
os torna mais seguros e confiáveis pois você sabe como os dados
estão sendo acessados e quais operações estão sendo
realizadas sobre os mesmos.
O ato de empacotar ao mesmo
tempo dados e objetos é denominado encapsulamento.
O objeto esconde seus dados de outros objetos e permite que os
dados sejam acessados por intermédio de seus próprios métodos.
Isso é chamado de ocultação de informações (information
hiding).
- O
encapsulamento protege os dados do objeto do uso
arbitrário e não-intencional.
- O
encapsulamento é o resultado (ou ato) de ocultar do
usuário os detalhes da implementação de um objeto.
- O
encapsulamento é importante porque separa a maneira como
um objeto se comporta da maneira como ele é
implementado.
- A
definição de como implementar os conhecimentos ou
ações de uma classe, sem informar como isto é feito.
|
Polimorfismo |
Polimorfismo (do grego:
poli= muitas morphos=formas) é a habilidade de objetos
distintos responderem a mesma mensagem de a sua própria maneira.
Eu posso treinar o meu
cachorro para responder ao comando latir e o meu
gato a responder ao comando miar mas posso
treiná-los também para responder ao comando falar.
Através do polimorfismo eu sei que ao receber o comando falar
o meu cachorro irá responder com um latido e o meu gato
com um miado. (pelo menos esse é o comportamento
considerado normal)
No contexto OOP podemos
criar objetos que respondem a mesma mensagem de forma única de
acordo com sua implementação. Assim, por exemplo, podemos
enviar uma mensagem para um objeto Impressora para imprimir um
texto na impressora e podemos enviar a mesma mensagem para a tela
para imprimir o texto um formulário na tela do computador.
No contexto OOP podemos
implementar esse tipo de polimorfismo através de um processo
chamado sobrecarga. Fazemos isso implementando diferentes
métodos de um objeto com o
mesmo nome mas assinaturas
diferentes. O objeto
pode dessa forma chamar qual método usar dependendo do contexto
da mensagem. Como exemplo podemos implementar dois métodos para
procurar um produto em um estoque da seguinte forma:
- método 1 -
getProduto(nome) - procura o produto pelo nome;
- método 2 - getProduto(preco)
- procura o produto pelo preço;
Um objeto pode então usar
o método getProduto() passando ou o nome ou o
preço para localizar um produto.
|
Herança |
A maioria dos objetos são
classificados de acordo com hierarquias. Por exemplo, você pode
classificar todos os cães como tendo certas características
comuns, tais como ter quatro patas e pelos.
Suas raças podem
classificá-los, posteriormente, em subgrupos com atributos
comuns, tais como tamanho e comportamento.
Você também classificar
objetos de acordo com sua função. Por exemplo, existem
veículos comerciais e veículos de recreio, existem caminhões e
automóveis de passageiros. Você classifica veículos de acordo
com sua marca e modelo.
Para tornar o mundo mais
coerente você precisa usar hierarquias de objetos e
classificações.
Dessa forma você usa a
herança em OOP para classificar os objetos em seus programas de
acordo com características e funções comuns. Isso torna o
trabalho com os objetos mais fácil e intuitivo e também torna a
programação mais fácil, porque ela permite que você combine
as características gerais para um objeto pai e herde estas
características nos objetos filhos.
Por exemplo, você pode
definir um objeto empregado que define todas as
características gerais dos funcionários em sua empresa. Você
pode, então, definir um objeto gerente que
herda as características do objeto empregado, mas também
adiciona características exclusivas para gerentes de sua
empresa.
É comum haver
similaridades entre diferentes classes. Freqüentemente, duas ou
mais classes irão compartilhar os mesmos atributos e/ou
métodos. Como nenhum de nós deseja reescrever várias vezes o
mesmo código, seria interessante se algum mecanismo pudesse
tirar proveito dessas similaridades. A herança é esse
mecanismo. Por intermédio da herança, é possível modelar
relacionamentos do tipo "é" ou "é
semelhante", o que nos permite reutilizar rotinas e
dados já existentes.
Uma subclasse herda
as propriedades de sua classe-pai também chamada superclasse.
Uma subclasse pode herdar a estrutura de dados e os métodos, ou
alguns dos métodos, de sua superclasse. A subclasse também tem
métodos e às vezes, tipos de dados próprios.
Subclasse uma
classe que é um subtipo de uma ou mais classes (denominadas
superclasses). Como tal, ela herda todas as características
de suas superclasses. Em outras palavras, todas as
características de uma classe são reusáveis por suas
subclasses. Se a classe B herda de A, então dizemos que B é uma
subclasse de A.
Superclasse
Uma classe que é um supertipo de uma ou mais classes (também
chamadas de subclasses). Como tal, ela é uma classe a partir
da qual todas as suas características são herdadas por suas
subclasses. Em outras palavras, todas as características de uma
superclasse são reusáveis por aquelas classes que são seus
subtipos. Se a classe B herda de A, então dizemos que A é uma
superclasse de B.
|
Agregação |
A agregação ocorre quando
um objeto consiste de uma composição de outros objetos que
trabalham juntos. Por exemplo, o objeto carro é um composto de
objetos roda, objeto motor, objeto da farol, objeto direção,
etc. Juntos todos esses objetos compõem o objeto carro.
Na verdade, o objeto motor
é um composto de muitos outros objetos. Assim há muitos
exemplos de agregação no mundo que nos rodeia. A capacidade de
usar agregação em POO é um poderoso recurso que permite
modelar e implementar com precisão processos de negócios em
seus programas.
A POO utiliza a agregação
como o mecanismo de reaproveitamento de classes para aumentar a
produtividade e a qualidade no desenvolvimento de software.
Dessa forma ao reutilizar
uma classe você pode usar uma ou várias classes para compor
outra classe e como as classes que estão sendo aproveitadas já
existem você não precisa reescrever o seu código aumentando
assim a produtividade.
Além destes conceitos básicos existem também os
princípios SOLID.
Os princípios SOLID para programação e
design orientados a objeto são de autoria de Robert C. Martin (mais conhecido
como Uncle Bob) e datam do início de 2000.
A palavra SOLID é um acróstico onde cada
letra significa a sigla de um princípio: SRP, OCP, LSP, ISP e DIP
Obs: Os acrósticos são formas textuais onde a primeira
letra de cada frase ou verso formam uma palavra ou frase
Na tabela abaixo vemos cada sigla, o seu
significado e um resumo do princípio a que se refere no original e em uma
tradução livre.
SRP |
The Single Responsibility Principle
Principio da Responsabilidade Única |
Uma classe deve ter um, e somente um,
motivo para mudar.
A class should have one, and only one, reason
to change. |
OCP |
The Open Closed Principle
Princípio Aberto-Fechado |
Você deve ser capaz
de estender um comportamento de uma classe, sem modificá-lo.
You should be able to extend a classes behavior,
without modifying it. |
LSP |
The Liskov Substitution Principle
Princípio da Substituição de Liskov |
As classes
derivadas devem ser substituíveis por suas classes base.
Derived classes must be substitutable for their
base classes. |
ISP |
The Interface Segregation Principle
Princípio da Segregação da Interface |
Muitas interfaces específicas são melhores
do que uma interface geral
Make fine grained interfaces that are client
specific. |
DIP |
The Dependency Inversion Principle
Princípio da inversão da dependência |
Dependa de uma abstração e não de uma
implementação.
Depend on abstractions, not on concretions. |
Os princípios SOLID devem ser aplicados no
desenvolvimento de software de forma que o software produzido tenha as seguintes
características:
-
Seja fácil de manter, adaptar e se
ajustar às constantes mudanças exigidas pelos clientes;
-
Seja fácil de entender e testar;
-
Seja construído de forma a estar
preparado para ser facilmente alterado com o menor esforço possível;
-
Seja possível de ser reaproveitado;
-
Exista em produção o maior tempo
possível;
-
Que atenda realmente as necessidades
dos clientes para o qual foi criado;
"Meus filhinhos , estas coisas vos
escrevo, para que não pequeis; e se alguém pecar, temos um advogado para com o
Pai, Jesus Cristo, o justo." I João 2:1
Referências:
José Carlos Macoratti