Programação Orientada a Objetos

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.

Listagem 1. Classe 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;
        }
        
  }

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.

Listagem 2. Definição dos objetos do tipo Empresa.

   
  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();
   
        }
   
  }

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.

Listagem 3. Classe Divisao.

  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;
              }
        }
        
  }

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.

Listagem 4. Classe Conta

  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);
        }
   
  }
  

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.

Listagem 5. Associação entre as classes Pessoa e 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;
        }
   
  }

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.

Listagem 6. Classe Pessoa com relacionamento um para N.

  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;
        }
   
  }
  

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.

Listagem 7. Classes Funcionario e Aluno, filhas da classe Pessoa

  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;
        }
        
  }
  

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.

Listagem 8. Classe Pessoa com os atributos protegidos.

  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;
        }
   
  }

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.

Listagem 9. Hierarquia de classes Conta.

  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;
        }
   
  }

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.

Listagem 10. Utilização das classes conta.

  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();
        }
   
  }

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.