Que o mundo do desenvolvimento de software é extremamente amplo e as vezes difícil para um iniciante todo mundo já sabe; Temos diversos padrões, algumas dezenas de linguagem e outros vários paradigmas seguidos por tais linguagens, e no meio disso tudo temos o amado e odiado paradigma de Orientação a Objetos, que atualmente é o mais difundido e talvez o mais usado — graças a linguagens como: Java, C++, C# e Python, por exemplo.
A Programação Orientada a Objetos — apenas OOP ou POO para os mais íntimos — como já vimos, é um dos mais difundidos paradigmas, e isso não se deve apenas ao fato de algumas pessoas gostarem de sofrer aprendendo o padrão, mas se deve ao fato de que o padrão tem evoluído bastante, principalmente no assunto segurança, reaproveitamento de código e manutenibilidade.
Não sei se você sabe, mas a POO se baseia em 4 pilares que são a base — obviamente — de todo este padrão; são eles: Abstração, Encapsulamento, Herança e Polimorfismo. Talvez já tenha dado pra ter uma ideia do que são eles, mas vamos falar mais sobre cada um, ok?
Pilares do paradigma
Abstração
A abstração é um dos pilares mais incríveis e mais importantes em linguagens que implementam o paradigma. Neste pilar, apesar de estarmos apenas tentando descrever nosso objeto inicial, devemos pensar em ao menos três coisas: primeiro precisamos dar uma identidade, isso é, um nome à nossa classe base, ou um "caminho" como em java, e isso não deve se repetir no nosso pacote; agora precisamos definir as propriedades do nosso objeto; e por fim, os métodos dele.
Ok, não parece tão claro, eu sei.
Suponhamos que queremos criar classes para vários tipos de animais terrestres — ou, com patas —, e para reaproveitarmos os códigos que com certeza teríamos que repetir várias vezes, vamos pensar em algo assim:
Criaremos uma classe base chamada AnimalTerrestre
.
Agora vamos pensar nos atributos/propriedades.
O que todos esses animais têm em comum? Quais características são compartilhadas? Todos têm patas, dedos e um pescoço, por exemplo, então estes serão as propriedades da nossa classe base.
Agora vamos pensar nos métodos, isto é, ações possíveis.
Todos eles andam, giram o pescoço e falam — ou não, mas fazem barulhos próprios entre espécie.
Então estes serão nossos métodos, ficando então nossa classe AnimalTerrestre
assim:
Propriedades:
numero_de_patas
numero_de_dedos
tamanho_do_pescoco
Métodos:
andar()
girar_pescoco()
falar()
Deu para ter uma ideia até agora?
Vamos ver outro então.
Encapsulamento
Se o anterior pareceu um pouco complicado, vamos ver este, prometo que será um pouco mais suave.
Na prática este apenas "esconde" partes do código, criando assim algum tipo de cofre que ninguém além do próprio dono ou herdeiro, tem acesso.
Como já deve ter imaginado, este é um dos elementos que adicionam certa segurança à uma aplicação que segue a POO.
Aqui, a maior parte das linguagens usam algo chamado de modificadores de acesso, que são palavras chaves que basicamente indicam o que pode ser público — por vezes tudo é público por padrão —, privado ou protegido.
Atributos ou métodos definidos como privado só podem ser vistos/acessados pela classe que os descreve, enquanto isso, os protegidos podem ser manipulados tanto por quem os cria quanto pelas classes que a herdam.
Mas você deve tá se perguntando: ok, e como ter um meio-termo entre públicos e privados?
É aí que entram os métodos especiais que chamamos de getters e setters. Estes métodos devem ser criados um para cada atributo.
O primeiro — getter — define um "espelho público" da propriedade que está privada, é por meio dele que podemos recuperá-lo de qualquer parte da aplicação sem alterar o modificador de acesso.
Já o segundo — setter — por sua vez, define uma "entrada pública" para alterar o valor.
Meio confuso? Vamos a um exemplo do mundo real.
Suponhamos que estamos sentados em um sofá e queremos assistir TV que atualmente está desligada.
O estado atual é desligado, certo? A menos que você entenda de eletrônica e saiba exatamente como a TV foi feita, não sabe o que está acontecendo internamente.
Agora pegamos o controle e apertamos o botão de ligar, este é o nosso método setter que vai alterar um estado, ou seja, vai alterar de desligado, para ligado.
Podemos então dizer que todo o funcionamento interno está encapsulado, só temos acesso a getters/setters e outros métodos, certo?
Herança
Acho que já falamos no início, mas o reuso de código é uma das maiores vantagens da POO, e este pilar junto ao primeiro são os maiores responsáveis por isso.
Herança, como o próprio nome diz, herda coisas de um pai.
Vamos pensar em um exemplo do mundo real.
Você! Você tem genes que herdou de seu pai, que herdou do seu avô, e seu avô por sua vez, do seu bisavô, certo?
Por enquanto, aqui estamos falando apenas de herança múltipla. Em algumas linguagens como C++ e Python, temos algo que chamamos de Herança Múltipla.
Na vida real isso é como: Você herdou genes tanto do seu pai quanto da sua mãe; eles respectivamente, herdaram de seus avós paternos e maternos; seus avós então... você entendeu.
Polimorfismo
Este está intimamente ligado ao pilar de herança.
É aqui onde sobrescrevemos ou alteramos comportamentos de membros das classes pai.
Como sabemos, objetos filhos herdam características e ações de seus ancestrais, porém, as vezes há casos específicos onde os ancestrais não têm propriedades ou métodos que vão suprir nossos requerimentos, e aí que entra o polimorfismo.
O polimorfismo não é nada mais nem menos que moldar o que pode já estar pronto, às suas próprias necessidades.
Um exemplo da vida real seria um objeto Veiculo
com um método abastecer
, por exemplo. Um carro não é abastecido do mesmo jeito que uma moto, cada um tem suas particularidades, e ainda existem o veículos elétricos que o "abastecimento" é completamente diferente.
Deu pra ter uma ideia de o que é e como funciona a Programação Orientada a Objetos?
Eu quis passar uma visão mais focada em teoria para no futuro vermos conceitos em linguagens específicas.
Se você chegou até aqui, muito obrigado e até a próxima.