Sem sombra de dúvidas o paradigma da Orientação a Objetos trouxe um ganho significativo na qualidade da produção de software.
Muito destes benefícios são em decorrência de 4 pilares, que quando colocados em prática, facilitam a vida do programador e proporcionam um código de qualidade.
Os pilares da Orientação a Objetos são: Abstração, Encapsulamento, Herança e Polimorfismo.
Abstração
A Abstração é a habilidade e a capacidade de se modelar conceitos, entidades, elementos, problemas e características do mundo real, de um domínio do problema em questão, levando-se em conta apenas os detalhes importantes para a resolução do problema e desprezando coisas que não têm importância no contexto.
Por exemplo, se pensarmos no conceito de uma “conta corrente” bancária e abstrairmos este conceito, podemos identificar detalhes comuns, como o número da conta, número da agência e saldo; e operações como débito em conta, depósito e extrato da conta. Basicamente essas são as características de conta corrente para todos os bancos, apesar de um ou outro banco ter uma política de descontos de taxas etc.
Encapsulamento
Na Orientação a Objetos, encapsulamento é o mecanismo utilizado para disponibilizar métodos que operam sobre os dados e que protegem o acesso direto indevido aos atributos de uma instância fora da classe onde estes foram declarados.
Esta proteção consiste em se usar modificadores de acesso mais restritivos sobre os atributos definidos na classe e fornecendo métodos que alteram os valores destes atributos de alguma forma.
O encapsulamento ajuda a prevenir o problema de interferência externa indevida sobre os dados de um objeto, como objetos que possam alterar os dados de outros objetos indevidamente.
Um exemplo deste problema pode ser o saldo da conta bancária. O saldo certamente não pode ser alterado ou manipulado diretamente, mas sim através de métodos adequados para isso, como métodos que fazem lançamentos de débitos e créditos.
A alteração direta do saldo causaria um problema de cálculos e inconsistência de dados. E é justamente por isso que devemos criar classes bem encapsuladas, que estejam aptas a fornecer métodos adequados para operar sobre os
dados dos objetos daquela classe.
É importante dizer que o uso de encapsulamento também evita que um programa torne-se tão interdependente que uma pequena mudança tenha grandes efeitos colaterais.
Herança
Herança é um mecanismo da Orientação a Objetos que permite criar novas classes a partir de classes já existentes, aproveitando-se das características existentes na classe a ser estendida. Este mecanismo é muito interessante pois promove um grande reuso e reaproveitamento de código existente.
Com a herança é possível criar classes derivadas (subclasses) a partir de classes bases (superclasses). As subclasses são mais especializadas do que as suas superclasses, mais genéricas.
As subclasses herdam todas as características de suas superclasses, como suas variáveis e métodos.
Imagine que dentro de uma organização empresarial, o sistema de RH tenha que trabalhar com os diferentes níveis hierárquicos da empresa, desde o funcionário de baixo escalão até o seu presidente.
Todos são funcionários da empresa, porém cada um com um cargo diferente. Mesmo a secretária, o pessoal da limpeza, o diretor e o presidente possuem um número de identificação, além de salário e outras características em comum.
Essas características em comum podem ser reunidas em um tipo de classe em comum, e cada nível da hierarquia ser tratado como um novo tipo, mas aproveitando-se dos tipos já criados, através da herança.
Os subtipos, além de herdarem todas as características de seus supertipos, também podem adicionar mais características, seja na forma de variáveis e/ou métodos adicionais, bem como reescrever métodos já existentes na superclasse (polimorfismo).
A herança permite vários níveis na hierarquia de classes, podendo criar tantos subtipos quanto necessário, até se chegar no nível de especialização desejado.
Podemos tratar subtipos como se fossem seus supertipos, por exemplo o sistema de RH pode tratar uma instância de Presidente como se fosse um objeto do tipo Funcionário, em determinada funcionalidade.
Porém não é possível tratar um supertipo como se fosse um subtipo, a não ser que o objeto em questão seja realmente do subtipo desejado e a linguagem suporte este tipo de tratamento, seja por meio de conversão de tipos ou outro mecanismo.
Algumas linguagens de programação permitem herança múltipla, ou seja, uma classe pode estender características de várias classes ao mesmo tempo. É o caso do C++.
Outras linguagens não permitem herança múltipla, por se tratar de algo perigo se não usada corretamente. É o caso do Java.
Na Orientação a Objetos as palavras classe base, supertipo, superclasse, classe pai e classe mãe são sinônimos, bem como as palavras classe derivada, subtipo, subclasse e classe filha também são sinônimos.
Polimorfismo
Formalmente polimorfismo quer dizer “várias formas”. E no caso da Orientação a Objeto, o polimorfismo denota uma situação na qual um objeto pode se comportar de maneiras diferentes ao receber uma mensagem, dependendo do seu tipo de criação.
O polimorfismo é alcançado com auxílio do uso de herança nas classes e a reescrita (overriding) de métodos das superclasses nas suas subclasses.
Duas subclasses de uma mesma classe podem ter implementações completamente diferentes de um mesmo método, o que leva os objetos a se comportarem de forma diferente, dependendo do seu tipo (classe).
Exemplificando, podemos imaginar um programa que faça a impressão de um relatório, por meio de uma classe chamada Impressora, que é uma interface de acesso às funcionalidades da impressora usada, por meio de um driver fornecido pelo fabricante.
Uma impressora a laser tem um mecanismo de impressão totalmente diferente de uma impressora a jato matricial, mas isso não importa para o programa.
Ele manda uma simples mensagem de imprimir para a impressora, e o modo como a impressora imprime no papel varia de acordo com o tipo de impressora usada, ou seja, a impressão se dá de formas diferentes para a mesma mensagem de imprimir.
Algumas linguagens promovem o polimorfismo principalmente através do uso de classes abstratas e interfaces.
Tendo que classes abstratas são classes que não podem gerar instâncias de objetos e que possuem um ou mais métodos sem implementação, deixando para suas subclasses a tarefa de implementar estes métodos abstratos.
Interfaces são um tipo de contrato que algumas classes têm de seguir, ou seja, as interfaces apenas definem métodos abstratos que as classes que implementam esta interface têm de implementar.
Caso ainda tenha dúvidas sobre o tema, recomenda-se este excelente vídeo do canal Código Fonte TV, que exemplifica de uma forma muito simples tais conceitos.