A programação orientada a objetos (POO) é um paradigma amplamente usado no desenvolvimento de softwares. Adotado por linguagens de programação populares como Java e C#, por exemplo, o paradigma é aplicável a uma ampla variedade de projetos, desde os mais simples até os mais complexos.
A orientação a objetos busca aproximar o mundo real ao mundo da programação de computadores. A ideia central deste paradigma é que tudo aquilo que existe no mundo real pode ser representado em código através de objetos.
Para exemplificar, pense no sistema de uma universidade. Neste sistema, um aluno é representado como um objeto, com atributos e comportamentos específicos. Os atributos do objeto ‘aluno’ podem incluir matrícula, nome, data de nascimento, endereço, curso, disciplinas, entre outros. Os comportamentos podem englobar a realização de matrículas em disciplinas, cancelamento de matrículas, listagem de disciplinas matriculadas ou concluídas, cálculo de médias semestrais, entre outros. Da mesma forma, um professor, uma disciplina, um curso e qualquer outra entidade do sistema universitário pode ser representada como um objeto, com seus atributos e comportamentos próprios.
Este artigo não possui a finalidade de detalhar os conceitos da POO, o foco aqui serão os quatro pilares fundamentais deste paradigma. Se você estiver interessado em conhecer mais sobre POO, clique aqui para ler um artigo que escrevi sobre esse assunto.
1 – Os 4 pilares da programação orientada a objetos
Os quatro pilares da POO representam quatro características que linguagens de programação e projetos devem atender para que sejam classificados como orientados a objetos. Essas características que formam os pilares da orientação a objetos são: abstração, encapsulamento, herança e polimorfismo.
Abaixo vamos conhecer cada uma dessas características e na sequência vamos ver um projeto simples de Java, que simula operações bancária, criado para fins didáticos e que com certeza irá lhe ajudar no entendimento dos conceitos aqui apresentados.
1.1 – Abstração
A abstração está relacionada a capacidade de representar as coisas que existem no mundo real como objetos em um software. Os objetos devem ser identificados (nomeados) conforme sua função e devem possuir atributos (ou propriedades) e métodos (ou comportamentos) devidamente definidos e configurados.
Pense, por exemplo, em um sistema bancário. Nesse sistema há um objeto chamado ContaBancaria, no qual temos atributos como número da conta, titular e saldo e temos também métodos como sacar, depositar, transferir e consultar saldo. Esse objeto é uma abstração de algo do mundo real (contas bancárias) representado em um software.
1.2 – Encapsulamento
O encapsulamento está ligado, diretamente, a segurança das informações do sistema. Ele pode ser entendido como uma forma de ocultação de dados.
O encapsulamento restringe os acessos aos atributos e métodos dos objetos, evitando que ocorram acessos desnecessários que possam ocasionar mudanças indesejadas no sistema. A ideia é que cada objeto é responsável por manter a integridade dos seus dados (atributos), permitindo que sejam acessados somente através de métodos públicos específicos para essa finalidade.
Vamos pensar no objeto ContaBancaria que apresentamos na explicação do conceito de abstração. É prudente que os seus atributos número da conta, titular e saldo sejam encapsulados e, portanto, acessados somente pelos seus métodos, a fim de evitar a exposição e manipulação dessas informações em outras partes do código.
Nas linguagens de programação orientadas a objetos, o encapsulamento é alcançado através do uso de métodos de acesso (getters e setters) e modificadores de acesso (como public, private e protected).
1.3 – Herança
Herança é a característica de um objeto ou classe receber (herdar) atributos e métodos de um objeto ou classe superior. Assim, surge o conceito de classe mãe e classes filhas, as quais herdam atributos e métodos da classe mãe.
Pensando novamente no exemplo do sistema bancário, nele temos uma classe chamada ContaCorrente que é uma classe filha de ContaBancaria. Desta forma, a classe ContaCorrente herda os atributos (número da conta, titular e saldo) e os métodos (sacar, depositar, transferir e consultar saldo) da sua classe mãe ContaBancaria. Essa mesma regra valeria para outras classes filhas de ContaBancaria como, por exemplo, ContaDigital e ContaSalario.
Note que, apesar de conta corrente, conta poupança e conta salário serem tipos diferentes de contas bancárias, todas compartilham os mesmos atributos e métodos básicos da classe ContaBancaria. Essa relação entre as classes, permitida pela herança, possibilita a reutilização de código, deixando o projeto mais organizado, legível e performático.
1.4 – Polimorfismo
O polimorfismo pode ser entendido como a característica da POO em que um mesmo método pode ter comportamentos diferentes conforme o objeto em que foi herdado.
Para que possamos entender esse conceito, vamos imaginar a seguinte situação: o método consultarSaldo da classe ContaBancaria é herdado pelos objetos de conta corrente, conta digital e conta salário. A princípio, o método consultarSaldo deveria ter o mesmo comportamento em todos os tipos de contas, porém, nas contas correntes ele deve apresentar além do saldo disponível, o saldo de cheque especial daquela conta. Para contas poupança e salário deve ser apresentado somente o saldo da conta, pois, não há oferta de produtos de crédito nessas contas. Veja que se trata do mesmo método apresentando comportamentos diferentes conforme o objeto em que ele é usado.
2 – Entendendo os pilares da programação orientadas a objetos
Após conhecermos a definição dos 4 pilares da programação orientada a objetos, vamos ver abaixo uma sequência de códigos escritos em Java, que simulam algumas transações bancárias. Esses códigos nos ajudarão a visualizar os pilares de programação orientada a objetos de forma prática, melhorando nossa compreensão a respeito do assunto.
2.1 – A estrutura do projeto
Para executar os códigos em sua máquina você vai precisar instalar: a JDK 17 ou superior e uma IDE compatível com Java. Eu recomendo utilizar o IntelliJ ou o Visual Studio Code (se optar por esta opção não esqueca de instalar a extensão da linguagem Java).
Depois de instaladas as ferramentas necessárias, você pode criar um projeto com a seguinte estrutura ou fazer o download do projeto disponível aqui:

Como podemos ver na imagem acima, a estrutura é bem simples e fácil de entender:
– Contas: é a pasta onde estão os arquivos que representam os tipos de contas que o banco fornece;
– Enums: como o próprio nome sugere, é a pasta onde fica o arquivo de Enums da aplicação;
– Models: pasta onde está a classe principal da aplicação;
– Service: pasta onde fica a função que executa as ações da aplicação;
– BancoApplicaton: arquivo onde está a função main() responsável por executar a aplicação.
2.2- Entendendo os pilares da programação orientada a objetos no código
Abaixo, vou compartilhar alguns trechos dos códigos do projeto onde poderemos visualizar a aplicação de cada um dos pilares da programação orientada a objetos. Vamos lá!
public class ContaBancaria {
// Atributos da classe
private int numeroConta;
private String titularConta;
private double saldo;
// Construtor da classe
public ContaBancaria(int numeroConta, String titularConta,
double saldoInicial, TipoConta tipoConta) {
this.numeroConta = numeroConta;
this.titularConta = titularConta;
this.saldo = saldoInicial;
}
// Métodos da classe
public void depositar(double valor) {
saldo += valor;
System.out.println("Conta "+numeroConta+" - Depósito de R$" + valor + " realizado com sucesso!");
}
public void sacar(double valor) {
if (valor <= saldo) {
saldo -= valor;
System.out.println("Conta "+numeroConta+" - Saque de R$" + valor + " realizado com sucesso!");
} else {
System.out.println("Saldo insuficiente para saque de R$" + valor);
}
}
public void consultarSaldo() {
System.out.println("Conta: "+numeroConta+" / "+"Titular: "+titularConta+" / "+"Saldo atual: R$" + saldo);
}
public void transferir(ContaBancaria destino, double valor) {
if (valor <= saldo) {
saldo -= valor;
destino.saldo += valor;
System.out.println("Conta "+numeroConta+" - Transferência de R$" + valor + " realizada para a conta " + destino.numeroConta);
} else {
System.out.println("Saldo insuficiente para transferência de R$" + valor);
}
}
public double getSaldo() {
return saldo;
}
public int getNumeroConta() {
return numeroConta;
}
public String getTitularConta() {
return titularConta;
}
}
A abstração pode ser vista no código da classe ContaBancaria que replica algo que existe no mundo real (contas bancárias) através de um código de programação que será usado em um sistema computacional.
Observe que a classe ContaBancaria foi construída com três atributos (número da conta, titular da conta e saldo) e quatro métodos (depositar, sacar, consulta saldo e transferir) que compõem a estrutura de uma conta bancária. A conta bancária é algo que existe no mundo real e utilizamos em nosso dia a dia e está sendo abstraída em um código computacional.
Nesta mesma classe, também conseguimos ver o encapsulamento. Observe que os atributos numeroConta, titularConta e saldo foram definidos como private e, portanto, não podem ser acessados fora da classe ContaBancaria. A única forma de serem manipulados por códigos externos a essa classe é através dos getters definidos no código: getNumeroConta(), getTitularConta() e getSaldo().
public class ContaCorrente extends ContaBancaria {
private double chequeEspecial;
public ContaCorrente(int numeroConta, String titularConta, double saldoInicial, double chequeEspecial) {
super(numeroConta, titularConta, saldoInicial, TipoConta.CORRENTE);
this.chequeEspecial = chequeEspecial;
}
public double getChequeEspecial() {
return chequeEspecial;
}
@Override
public void consultarSaldo() {
double saldoTotal = getSaldo() + chequeEspecial;
System.out.println("Conta: " + getNumeroConta() + " / " + "Titular: " + getTitularConta() + " / " + "Saldo atual: R$" + getSaldo() + " / Cheque Especial: R$" + chequeEspecial + " / Saldo Total: R$" + saldoTotal);
}
}
public class ContaDigital extends ContaBancaria {
public ContaDigital(int numeroConta, String titularConta, double saldoInicial) {
super(numeroConta, titularConta, saldoInicial, TipoConta.DIGITAL);
}
}
public class ContaSalario extends ContaBancaria {
public ContaSalario(int numeroConta, String titularConta, double saldoInicial) {
super(numeroConta, titularConta, saldoInicial, TipoConta.SALARIO);
}
}
A herança pode ser vista nas classes ContaCorrente, ContaDigital e ContaSalario. Observe que, em todas elas, possuímos o comando extends ContaBancaria. Quando usamos a palavra reservada extends, indicamos ao Java que aquela classe irá herdar os recursos de uma outra classe, que, em nosso exemplo, é a classe ContaBancaria.
Já o polimorfismo pode ser visto na classe ContaCorrente. Nesta classe é usada a anotação @Override que indica o polimorfismo na linguagem Java. Note que abaixo desta anotação, o comportamento do método consultarSaldo da classe ContaBancária está sendo alterado para apresentar uma informação exclusiva das contas correntes, que é o saldo de cheque especial do cliente.
Conclusão
De forma simples e resumida, neste artigo conhecemos os quatro pilares da programação orientada a objetos, os quais representam as características básicas das linguagens de programação orientadas a objetos.
Recomendo que você analise, estude e edite os códigos para conseguir uma melhor compreensão dos pilares da POO. Procure entender estes conceitos e aprofundar seus conhecimentos neste paradigma de programação que é um dos mais utilizados no desenvolvimento de softwares.
Espero que o conteúdo aqui apresentado seja útil de alguma forma para você. Em caso de dúvidas, sugestões ou reclamações fique à vontade para entrar em contato.
Referências
https://www.devmedia.com.br/os-4-pilares-da-programacao-orientada-a-objetos/9264
https://www.freecodecamp.org/portuguese/news/os-quatro-pilares-da-programacao-orientada-a-objetos-com-javascript
https://www.linkedin.com/pulse/encapsulamento-em-java-como-manter-integridade-dos-plaster-moreira