Principais conceitos da Programação Orientada a Objetos

Neste artigo serão mostrados os principais conceitos do paradigma da programação Orientada a Objetos. Veja também como implementar os conceitos de POO com exemplos em Java.

Hoje, a maioria das linguagens de programação são orientadas a objetos como Java, C#, Python e C++ e, apesar de terem algumas diferenças na implementação, todas seguem os mesmos princípios e conceitos. Muitos programadores, apesar de utilizarem linguagens orientadas a objetos, não sabem utilizar alguns dos principais conceitos desse paradigma orientado a objetos e, por isso, desenvolvem sistemas com alguns erros conceituais e acabam escrevendo mais código que o necessário, não conseguindo reutilizar o código como seria possível.

Este artigo fará uma revisão dos principais conceitos de orientação a objetos e mostrará diversos exemplos de implementação dos conceitos de orientação a objetos em Java. Os principais conceitos do paradigma orientado a objetos são Classes e Objetos, Associação,Encapsulamento, Herança e Polimorfismo.

Já viu as novidades que a DevMedia preparou nos cursos de Java? Confira!

Classe e Objeto

Uma classe é uma forma de definir um tipo de dado em uma linguagem orientada a objeto. Ela é formada por dados e comportamentos.

Para definir os dados são utilizados os atributos, e para definir o comportamento são utilizados métodos. Depois que uma classe é definida podem ser criados diferentes objetos que utilizam a classe. A Listagem 1 mostra a definição da classe Empresa, que tem os atributos nome, endereço, CNPJ, data de fundação, faturamento, e também o método imprimir, que apenas mostra os dados da empresa.

package com.devmedia.model; import java.util.Date; public class Empresa { private String nome; private String cnpj; private String endereco; private Date dataFundacao; private float faturamento; public void imprimir() { System.out.println("Nome: " + nome); System.out.println("CNPJ: " + cnpj); System.out.println("Endereço: " + endereco); System.out.println("Data de Fundação: " + dataFundacao); } public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getCnpj() { return cnpj; } public void setCnpj(String cnpj) { this.cnpj = cnpj; } public String getEndereco() { return endereco; } public void setEndereco(String endereco) { this.endereco = endereco; } public Date getDataFundacao() { return dataFundacao; } public void setDataFundacao(Date dataFundacao) { this.dataFundacao = dataFundacao; } public float getFaturamento() { return faturamento; } public void setFaturamento(float faturamento) { this.faturamento = faturamento; } }
Listagem 1. Classe Empresa

Com a classe definida, podem ser criados diversos objetos do tipo Empresa, por isso a Listagem 2 mostra como criar esses objetos, bastando declarar uma variável com o tipo Empresa e com a palavra reservada new criar um novo objeto. Depois podemos definir os dados para os atributos da classe Empresa e, por fim, chamar o método definido.

package com.devmedia.main; import java.util.Date; import com.devmedia.model.Empresa; public class Main { public static void main(String[] args) { Empresa empresa1 = new Empresa(); empresa1.setNome("Loja 1"); empresa1.setCnpj("12343232"); empresa1.setDataFundacao(new Date()); empresa1.setEndereco("Rua abc, 100"); empresa1.setFaturamento(50000); empresa1.imprimir(); Empresa empresa2 = new Empresa(); empresa2.setNome("Loja 2"); empresa2.setCnpj("12354432"); empresa2.setDataFundacao(new Date()); empresa2.setEndereco("Rua abc, 200"); empresa2.setFaturamento(50000); empresa2.imprimir(); Empresa empresa3 = new Empresa(); empresa3.setNome("Posto de Gasolina"); empresa3.setCnpj("12345434"); empresa3.setEndereco("Rua afd, 500"); empresa3.setFaturamento(10000); empresa3.setDataFundacao(new Date()); empresa3.imprimir(); } }
Listagem 2. Definição dos objetos do tipo Empresa

Encapsulamento

O conceito do encapsulamento consiste em “esconder” os atributos da classe de quem for utilizá-la. Isso se deve por dois motivos principais.

Um é para que alguém que for usar a classe não a use de forma errada como, por exemplo, em uma classe que tem um método de divisão entre dois atributos da classe - se o programador java não conhecer a implementação interna da classe, ele pode colocar o valor zero no atributo do dividendo, mas se a classe estiver corretamente encapsulada podemos impedir que o programador faça isso. Esse tipo de implementação é feito via os métodos get e set. A Listagem 3 mostra o código da classe Divisao citada como exemplo.

package com.devmedia.model; public class Divisao { private int num1; private int num2; public void divisao() { System.out.println("A divisao e: " + (num1 / num2)); } public int getNum1() { return num1; } public void setNum1(int num1) { this.num1 = num1; } public int getNum2() { return num2; } public void setNum2(int num2) { if (num2 == 0) { num2 = 1; } else { this.num2 = num2; } } }
Listagem 3. Classe Divisao

O outro motivo é de manter todo o código de uma determinada classe encapsulada dentro dela mesmo como, por exemplo, se existe uma classe Conta, talvez seja melhor não permitir que um programador acesse o atributo saldo diretamente, nem mesmo com os métodos get e set, mas somente por operações, como saque, depósito e saldo. A Listagem 4 mostra a implementação da classe Conta, onde só é possível acessar a atributo saldo pelas operações disponibilizadas na classe e os outros atributos podem ser acessados via métodos get e set.

package com.devmedia.model; public class Conta { private String agencia; private String numero; private float saldo; public void saque(float valor) { if (saldo < valor) { System.out.println("O saldo é insuficiente!"); } else { saldo -= valor; } } public String getAgencia() { return agencia; } public void setAgencia(String agencia) { this.agencia = agencia; } public String getNumero() { return numero; } public void setNumero(String numero) { this.numero = numero; } public void deposito(float valor) { saldo += valor; } public void saldo() { System.out.println("O saldo é: " + saldo); } }
Listagem 4. Classe Conta

Associação de Classes

A associação de classes indica quando uma classe tem um tipo de relacionamento "tem um" com outra classe como, por exemplo, uma pessoa tem um carro e isso indica que a classe Pessoa tem uma associação com a classe Carro. Esse tipo de relacionamento entre classes é importante, porque define como as classes interagem entre elas nas aplicações.

A Listagem 5 mostra como implementar uma associação um para um. A associação é feita entre as classes Pessoa e Carro. A classe Carro tem os atributos modelo, placa, ano e valor; e a classe Pessoa tem os atributos nome, endereço, telefone e dataNascimento. Além disso, ela tem um relacionamento com a classe Carro.

package com.devmedia.model; public class Carro { private String modelo; private String placa; private int ano; private float valor; public String getModelo() { return modelo; } public void setModelo(String modelo) { this.modelo = modelo; } public String getPlaca() { return placa; } public void setPlaca(String placa) { this.placa = placa; } public int getAno() { return ano; } public void setAno(int ano) { this.ano = ano; } public float getValor() { return valor; } public void setValor(float valor) { this.valor = valor; } } package com.devmedia.model; import java.util.Date; public class Pessoa { private String nome; private String endereco; private String telefone; private Date dataNascimento; // Relacionamento com a classe Carro private Carro carro; public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getEndereco() { return endereco; } public void setEndereco(String endereco) { this.endereco = endereco; } public String getTelefone() { return telefone; } public void setTelefone(String telefone) { this.telefone = telefone; } public Date getDataNascimento() { return dataNascimento; } public void setDataNascimento(Date dataNascimento) { this.dataNascimento = dataNascimento; } public Carro getCarro() { return carro; } public void setCarro(Carro carro) { this.carro = carro; } }
Listagem 5. Associação entre as classes Pessoa e Carro

Além do relacionamento um para um, também existem o relacionamento um para N e N para N. Esses relacionamentos são modelados com um vetor de objetos de uma classe para outro como, por exemplo, se uma pessoa pode ter mais de um carro, é necessário mapear esse relacionamento com uma lista de carros na classe Pessoa. A Listagem 6 mostra a alteração necessária na classe Pessoa, mas na classe Carro não é necessária nenhuma alteração.

package com.devmedia.model; import java.util.ArrayList; import java.util.Date; import java.util.List; public class Pessoa { private String nome; private String endereco; private String telefone; private Date dataNascimento; // Relacionamento com a classe Carro private List<Carro> carros = new ArrayList<Carro>(); public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getEndereco() { return endereco; } public void setEndereco(String endereco) { this.endereco = endereco; } public String getTelefone() { return telefone; } public void setTelefone(String telefone) { this.telefone = telefone; } public Date getDataNascimento() { return dataNascimento; } public void setDataNascimento(Date dataNascimento) { this.dataNascimento = dataNascimento; } public List<Carro> getCarros() { return carros; } public void setCarros(List<Carro> carros) { this.carros = carros; } }
Listagem 6. Classe Pessoa com relacionamento um para N

Herança

A herança é um tipo de relacionamento que define que uma classe "é um" de outra classe como, por exemplo, a classe Funcionario que é uma Pessoa, assim um Funcionário tem um relacionamento de herança com a classe Pessoa. Em algumas linguagens, como C#, é possível fazer herança múltipla, isto é, uma classe pode herdar de diversas outras classes, mas em Java isso não é permitido, pois cada classe pode herdar de apenas outra classe.

A Listagem 7 mostra como implementar a herança entre classes: a classe Pessoa definida será utilizada como base para as outras classes. Será definida a classe Funcionario, com os atributos salario e matricula, e a classe Aluno com os atributos registroAcademico e curso. Como é possível observar, para implementar herança em Java usaremos a palavra reservada extends.

package com.devmedia.model; public class Funcionario extends Pessoa { private float salario; private String matricula; public float getSalario() { return salario; } public void setSalario(float salario) { this.salario = salario; } public String getMatricula() { return matricula; } public void setMatricula(String matricula) { this.matricula = matricula; } } package com.devmedia.model; public class Aluno extends Pessoa { private String registroAcademico; private String curso; private float mensalidade; public String getRegistroAcademico() { return registroAcademico; } public void setRegistroAcademico(String registroAcademico) { this.registroAcademico = registroAcademico; } public String getCurso() { return curso; } public void setCurso(String curso) { this.curso = curso; } public float getMensalidade() { return mensalidade; } public void setMensalidade(float mensalidade) { this.mensalidade = mensalidade; } }
Listagem 7. Classes Funcionario e Aluno, filhas da classe Pessoa

Sobre a herança em Java é importante saber que existem quatro tipos de acesso aos atributos:

  1. public: que permite que métodos e atributos sejam acessados diretamente de qualquer classe;
  2. private: que permite que métodos e atributos sejam acessados apenas dentro da classe;
  3. protected: que permite que métodos e atributos sejam acessados apenas dentro da própria classe e em classes filhas;
  4. tipo de acesso padrão, que permite que métodos e atributos sejam acessadas por qualquer classes que esteja no mesmo pacote.

Como usamos a classe Pessoa com herança, uma possibilidade é mudar o tipo de acesso dos atributos da classe Pessoa para protegido. A Listagem 8 mostra a alteração feita.

package com.devmedia.model; import java.util.ArrayList; import java.util.Date; import java.util.List; public class Pessoa { protected String nome; protected String endereco; protected String telefone; protected Date dataNascimento; // Relacionamento com a classe Carro protected List<Carro> carros = new ArrayList<Carro>(); public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public String getEndereco() { return endereco; } public void setEndereco(String endereco) { this.endereco = endereco; } public String getTelefone() { return telefone; } public void setTelefone(String telefone) { this.telefone = telefone; } public Date getDataNascimento() { return dataNascimento; } public void setDataNascimento(Date dataNascimento) { this.dataNascimento = dataNascimento; } public List<Carro> getCarros() { return carros; } public void setCarros(List<Carro> carros) { this.carros = carros; } }
Listagem 8. Classe Pessoa com os atributos protegidos

Polimorfismo

O Polimorfismo é a possibilidade de em uma hierarquia de classes implementar métodos com a mesma assinatura e, assim, implementar um mesmo código que funcione para qualquer classe dessa hierarquia sem a necessidade de implementações específicas para cada classe. O principal objetivo do polimorfismo é diminuir a quantidade de código escrito, aumentando a clareza e a facilidade de manutenção.

A Listagem 9 mostra um exemplo de polimorfismo, onde a classe Conta tem um método saque que apenas permite o saque caso o cliente tenha saldo em sua conta. A classe ContaEspecial, que é filha da classe Conta, permite que o cliente faça um saque caso tenha saldo ou tenha ainda limite no cheque especial, e a classe ContaPoupanca permite que o cliente faça um saque caso ainda tenha saldo na conta corrente ou saldo na conta poupança.

package com.devmedia.model; public class Conta { protected String agencia; protected String numero; protected float saldo; public void saque(float valor) { if (saldo < valor) { System.out.println("O saldo é insuficiente!"); } else { saldo -= valor; System.out.println("Saque efetuado com sucesso!"); } } public String getAgencia() { return agencia; } public void setAgencia(String agencia) { this.agencia = agencia; } public String getNumero() { return numero; } public void setNumero(String numero) { this.numero = numero; } public void deposito(float valor) { saldo += valor; } public void saldo() { System.out.println("O saldo é: " + saldo); } } package com.devmedia.model; public class ContaEspecial extends Conta { private float limite; public void saque(float valor) { if (saldo + limite < valor) { System.out.println("O saldo é insuficiente!"); } else { saldo -= valor; System.out.println("Saque efetuado com sucesso!"); } } public float getLimite() { return limite; } public void setLimite(float limite) { this.limite = limite; } } package com.devmedia.model; public class ContaPoupanca extends Conta { private float saldoPoupanca; public void saque(float valor) { if (saldo + saldoPoupanca < valor) { System.out.println("O saldo é insuficiente!"); } else { if (saldo < valor) { valor -= saldo; saldo = 0; saldoPoupanca -= valor; } else { saldo -= valor; } System.out.println("Saque efetuado com sucesso!"); } } public void depositoPoupanca(float valor) { saldoPoupanca += valor; } private float getSaldoPoupanca() { return saldoPoupanca; } private void setSaldoPoupanca(float saldoPoupanca) { this.saldoPoupanca = saldoPoupanca; } }
Listagem 9. Hierarquia de classes Conta

A Listagem 10 mostra o uso das classes, apesar de serem definidos objetos dos três tipos de classes. O método fazSaque pode ser utilizado para qualquer um dos tipos de conta, não dependendo do tipo que é passado como parâmetro para ele, e isso é possível graças ao polimorfismo, pois o método saque das três classes tem a mesma assinatura e em tempo de execução a JVM sabe o método saque de qual classe executar.

package com.devmedia.main; import javax.swing.JOptionPane; import com.devmedia.model.Conta; import com.devmedia.model.ContaEspecial; import com.devmedia.model.ContaPoupanca; public class MainConta { public static void main(String [] args) { Conta conta = new Conta(); conta.setAgencia("1234"); conta.setNumero("1244"); conta.deposito(3000); ContaPoupanca contaPoupanca = new ContaPoupanca(); contaPoupanca.setAgencia("3456"); contaPoupanca.setNumero("1234"); contaPoupanca.deposito(3000); contaPoupanca.depositoPoupanca(1000); ContaEspecial contaEspecial = new ContaEspecial(); contaEspecial.setAgencia("3432"); contaEspecial.setLimite(1000); contaEspecial.setNumero("1434"); contaEspecial.deposito(3000); fazSaque(conta); fazSaque(contaPoupanca); fazSaque(contaEspecial); } public static void fazSaque(Conta conta) { float valor = Float.parseFloat( JOptionPane.showInputDialog("Digite o valor do saque") ); conta.saque(valor); conta.saldo(); } }
Listagem 10. Utilização das classes conta

Este artigo mostrou alguns dos principais conceitos do paradigma de programação Orientado a Objetos com o objetivo de ajudar pessoas que estão começando a programar com esse paradigma e auxiliar aqueles que apesar de já programarem com linguagens orientadas a objetos ainda tem algumas dúvidas de como utilizar alguns conceitos desse paradigma.

Espero que este artigo tenha sido útil! Até a próxima.


Links Úteis

  • Você conhece o método chinês?: O método chinês é uma técnica de depuração manual, amplamente utilizada no meio acadêmico, mas especialmente útil quando precisamos analisar um código sem ter o apoio de um editor.
  • Criando meu primeiro projeto no Java: Neste curso você aprenderá a criar o seu primeiro programa com Java, e não, ele não será um simples “Hello, World!”.
  • Delphi Exceptions: Trabalhando com exceções em Delphi: Neste curso você aprenderá a realizar o tratamento de exceções no Delphi, técnica que visa garantir o bom funcionamento da aplicação mesmo na ocorrência de certos erros.

Saiba mais sobre Orientação a Objetos ;)

  • Os pilares da Programação Orientada a Objetos: Neste DevCast veremos quais são os quatro pilares da Orientação a Objetos, seu conceito e sua importância na programação.
  • Orientação a Objetos em C#: Neste Guia de Referência você encontrará todo o conteúdo que precisa para aprender a programar Orientado a Objetos na linguagem C#.
  • Orientação a Objetos em Java: Nesse Guia de Referência você encontrará todo o conteúdo que precisa para aprender a programar Orientado a Objetos em Java, paradigma padrão dessa linguagem e que visa o desenvolvimento com base em objetos.

Artigos relacionados