Orientação a objetos em profundidade

Karl Marx Alexander
investigacoesholisticas
6 min readJun 23, 2019
Photo by Namroud Gorguis on Unsplash

Um dos primeiros aprendizados “abstratos” que tive no início dos meus estudos em computação foi o paradigma da orientação a objetos.

A matéria introdutória a lógica de programação utilizava C, e como me pareceu incrível não ter que usar iterações estranhas ou código repetidos para reaproveitar funções existentes (GOTO era técnica proibida, embora o preguiçoso dentro de mim não entendesse o motivo).

No momento estou aprendendo TypeScript e utilizando um ORM muito parecido com Hibernate, usando um livro de estrutura de dados em Java, e decidi acompanhar o livro junto com todas as indicações de bibliografia não necessariamente relacionadas a Java.

Como não poderia deixar de ser, o primeiro capitulo trata primariamente do paradigma de orientação a objetos, e qual não foi minha surpresa ao perceber que muitos conceitos são extremamente profundos e interessantes por si só.

Um novo paradigma

Até o inicio dos anos 90 o paradigma de programação da maioria dos softwares comercias seguia uma forma “estruturada” (também chamada de imperativa) de programação, que consiste na execução sequencial de operações em um ciclo bem definido, com instruções diretas e acesso a todos os dados.

Quando os sistemas com este tipo de paradigma começaram a se tornar grandes e evoluir em complexidade, se tornou cada vez mais difícil representar o domínio de um sistema e entender tudo que um software executaria em cada ponto do seu ciclo de vida. Pensando nessas dificuldades é que surge o paradigma da orientação a objetos.

Conceito fácil…

Na orientação a objetos, todo componente do software pode ser representado como um conjunto entre propriedades e funções, e neste ponto o paradigma já começa a se diferenciar da programação estruturada. Quando pensamos em um objeto, seus atributos são diretamente relacionados as funções que o mesmo pode executar.

Todo objeto tem intrinsecamente três aspectos: O que ele é, o que pode fazer e como pode ser acionado, e toda modelagem de um problema pode ser feita a partir destas interações.

Um carro pode andar, mas somente se tiver todas as rodas, e o atributo rodas esta diretamente relacionado a função andar. O local onde estão reunidas todas as propriedades e funções de um objeto é chamado de “classe”, e comumente se diz que uma classe “encapsula” atributos e métodos de um objeto. E isso é quase tudo!

…Aplicação difícil

“experienced programmers and software engineers must be retrained significantly: not because object orientation is hard, but because of their experience in traditional, function centered (or information-centered) problem solving. […] reacquire an object-oriented frame of mind”

[1] pág. 2128

Esta citação direta da introdução ao paradigma da orientação a objetos nos mostra como algumas coisas podem ser replicadas em contextos completamente diferentes. Dificilmente pensamos na nossa própria forma de ver o mundo, ainda mais quando se relaciona a nossa maneira de desenvolver software.

O tempo todo pensamos sequencialmente sobre a solução de um problema, e na maioria dessas nossas abordagens sobre a construção de um sistema tende a ser desta forma, o que torna o paradigma de orientação a objetos fácil de entender, e de parecer estar sendo aplicado, quando na verdade tudo o que temos é programação estruturada em arquivos diferentes.

Voltemos ao exemplo do carro. Em um sistema eu defino um objeto carro, que pode acelerar, freiar, virar, entre outras funções, e tem como atributo cor, velocidade máxima e tamanho. Mais a frente no meu projeto, eu entendo a necessidade de que um carro seja abastecido, e para isso crio uma nova classe posto com o método abastecer e dou ao carro um atributo tanque, que representa o nível de combustível.

Eu deveria ter um método abastecer no posto? Um carro de fato só deveria ser abastecido em um posto de gasolina (no nosso mundo), porém uma classe externa deveria ser capaz de mexer nos atributos de carro? A resposta é sim e não.

A classe posto pode alterar o atributo tanque do meu carro, porém ela não pode fazer isso diretamente, uma vez que o controle de todos os atributos da minha classe carro deveria ser apenas dela, por isso o método abastecer do posto deve chamar um outro método na classe carro, talvez com o mesmo nome.

Isso parece pouco relevante, porém encapsulamento é um dos assuntos mais importantes e menos entendidos da orientação a objetos. Imagine que no futuro meu carro não aceita mais gasolina e se pode ser abastecido com etanol, onde eu deveria fazer esta checagem se vários postos podem acessar o meu tanque diretamente? É muito mais fácil implementar isto uma vez no meu carro e não em todas as chamadas de abastecimento em diversos postos diferentes.

Herança e polimorfismo

Nosso sistema viário já possui carros e postos, porém essa não é uma estrada comum. Estradas de verdade possuem caminhões, motos, entre outros tipos de veículos que compartilham características com um carro embora não sejam exatamente do mesmo tipo. Pensando neste tipo de configuração, poderíamos criar uma nova classe para cada um dos veículos anteriores, mas se você se lembra que um dos objetivos da orientação a objetos é reaproveitar características em comum que sejam generalistas, vai contra nossos novos princípios reescrever o método de frenagem para cada um dos veículos. Este tipo de configuração é uma ótima aplicação para outro conceito da orientação a objetos, a herança.

Imagine que agora temos uma classe veículo, com métodos de aceleração, frenagem, e mudança de direção, podemos então estender esta classe em carro, nos preocupando apenas com as especificidades do veiculo do tipo carro, e o mesmo poderia ser feito para um caminhão ou ônibus. A classe carro “herda” todas as propriedades de um veiculo, e se torna uma “sub-classe” de veiculo, sendo veiculo então sua “super-classe”.

É assim que definimos a herança entre classes na orientação a objetos, do mesmo modo como um filho herda todas as propriedades de um pai, “sub-classes” tem todas as propriedades e métodos encapsuladas pela “super-classe”, com suas especificidades próprias.

Agora imagine que um carro não use mais exatamente o tipo de direção padrão para veículos, como por exemplo hidráulica, e seja um carro elétrico com direção eletrônica, mas que continua compartilhando todas as outras características de veículos. Em um paradigma de programação imperativa, faria sentido reimplantar uma nova classe de veículos elétricos, e talvez aqui também o faça supondo-se que todo veiculo possa ter sua versão elétrica, no entanto se apenas carros podem ser elétricos comercialmente, e se pudéssemos apenas reescrever a direção de carros para lidar com este novo tipo de direção possível?

Polimorfismo trata da transformação de certas propriedades (atributos ou métodos) dentro do paradigma da orientação a objetos. O polimorfismo pode ser entendido essencialmente como uma função, que transforma um identificador inicial em algum outro, uma subclasse pode sobre-escrever um método ou propriedade da sua classe pai para que se adapte melhor as suas necessidades de implementação

É possível classificar o polimorfismo em alguns tipos de acordo com as operações necessárias para que o mesmo funcione, veja [2] para um tratamento mais aprofundado.

O resumo da ópera

A orientação a objetos é um paradigma poderoso e que parece simples ao primeiro olhar, sendo esse um dos principais motivos para sua adoção, porém sofrendo do mesmo problema do paradigma imperativa, quando sistemas se tornam complexos demais até mesmo para o novo paradigma, é importante termos os valores e técnicas em mente durante o design e execução dos projetos.

Existem muitas referencias hoje em dia sobre como isto deve ser feito, sendo uma das principais o “Design Patterns”, que surgiu para resolver este novo problema já em 1994. Talvez a orientação a objetos não seja o paradigma perfeito, mas ele é o melhor em muitos casos, e no fim das contas, você já devia saber que não existe bala de prata para software.

Referências

[1] TUCKER, Allen B. (Ed.). Computer science handbook. CRC press, 2004.

[2] CARDELLI, Luca; WEGNER, Peter. On understanding types, data abstraction, and polymorphism. ACM Computing Surveys (CSUR), v. 17, n. 4, p. 471–523, 1985.

--

--

Karl Marx Alexander
investigacoesholisticas

The less smarter and Brazilian Feynman, Software engineer at Gaivota