Orientação a Objeto - Regras Básicas de Design

Objetivos da Seção

Regras Básicas de Design

Responsabilidades

Regra 1: Keep It Simple, Stupid (KISS)

Regra 2: Colocar as Responsabilidades com os Dados

Regra 3: Fraco Acoplamento

class Aluno {
     String nome;
     long   matrícula;
     
     public String getNome() { return nome; }
     public long   getMatrícula() { return matrícula; }

     // etc.
}

ListaOrdenada listaDeAlunos = new ListaOrdenada();
Aluno novoAluno = new Aluno(...);
//etc.
listaDeAlunos.add(novoAluno);
class ListaOrdenada {
     Object[] elementosOrdenados = new Object[tamanhoAdequado];

     public void add(Aluno x) {
          // código não mostrado aqui
          // ...
          long matrícula1 = x.getMatrícula();
          long matrícula2 = elementosOrdenados[k].getMatrícula();
          if(matrícula1 < matrícula2) {
               // faça algo
          } else {
               // faça outra coisa
          }
     }
class ListaOrdenada {
     Object[] elementosOrdenados = new Object[tamanhoAdequado];

     public void add(Aluno x) {
          // código não mostrado
          // ...
          if(x.compareTo(elementosOrdenados[K]) < 0) {
               // faça algo
          } else {
               // faça outra coisa
          }
     }
Interface Comparable {
     public int compareTo(Object outro);
}

class Aluno implements Comparable {
     public int compareTo(Object outro) {
          // compare registro de aluno com outro
          return ...
     }
}

class ListaOrdenada {
     Object[] elementosOrdenados = new Object[tamanhoAdequado];

     public void add(Comparable x) {
          // código não mostrado
          if(x.compareTo(elementosOrdenados[K]) < 0) {
               // faça algo
          } else {
               // faça outra coisa
          }
     }
interface SelecionávelPorNome {
    Iterator getIteradorPorNome(String nome);
}

interface Nomeável {
    String getNome();
}

classe ColeçãoDeAlunos implements SelecionávelPorNome {
    // ...
    Iterator getIteradorPorNome(String nome) {
        // ...
    }
}

classe Aluno implements Nomeável {
    // ...
    String getNome() { ... }
}

classe ColeçãoDeProfessores implements SelecionávelPorNome {
    // ...
    Iterator getIteradorPorNome(String nome) {
        // ...
    }
}

classe Professor implements Nomeável {
    // ...
    String getNome() { ... }
}

classe ColeçãoDeDisciplinas implements SelecionávelPorNome {
    // ...
    Iterator getIteradorPorNome(String nome) {
        // ...
    }
}

classe Disciplina implements Nomeável {
    // ...
    String getNome() { ... }
}

classe ComponenteDeSeleção {
    Iterator it;
    // observe o tipo do parâmetro (uma interface)
    public ComponenteDeSeleção(SelecionávelPorNome coleção, String nome) {
        it = coleção.getIteradorPorNome(nome); // chamada polimórfica
    }
    // ...
    void geraListBox() {
        response.out.println("<select name=\"nome\" size=\"1\">");
        while(it.hasNext()) {
                int i = 1;
                // observe o tipo do objeto
                Nomeável obj = (Nomeável)it.next();
                response.out.println("<option value=\"escolha" + i + "\">" +
                                      obj.getNome() + // chamada polimórfica
                                      "</option>");
        }
        response.out.println("</select>");
    }
}

    // Como usar o código acima num servlet:
    // supõe que as coleções usam o padrão Singleton
    ComponenteDeSeleção cds =
        new ComponenteDeSeleção(ColeçãoDeAlunos.getInstance(), "João");
    cds.geraListBox();

    cds = new ComponenteDeSeleção(ColeçãoDeDisciplinas.getInstance(), "Programação");
    cds.geraListBox();

Regra 4: Alta Coesão

class Angu {
    public static int acharPadrão(String texto, String padrão) {
        // ...
    }
    public static int média(List números) {
        // ...
    }
    public static outputStream abreArquivo(string nomeArquivo) {
        // ...
    }
}
class Xpto extends Angu { // quer aproveitar código de Angu
    ...
}

Um Exemplo de Refatoramento

O Programa Original

C:\...\locadora-v1>java Locadora
Registro de Alugueis de Juliana
        O Exorcista                     3.5
        Men in Black                    2.0
        Jurassic Park III               9.0
        Planeta dos Macacos             12.0
        Pateta no Planeta dos Macacos   12.0
        O Rei Leao                      42.0
Valor total devido: 80.5
Voce acumulou 8 pontos de alugador frequente

Locadora.gif (10895 bytes)

public class Fita {
  public static final int NORMAL = 0;
  public static final int LANÇAMENTO = 1;
  public static final int INFANTIL = 2;

  private String título;
  private int códigoDePreço;

  public Fita(String título, int códigoDePreço) {
    this.título = título;
    this.códigoDePreço = códigoDePreço;
  }

  public String getTítulo() {
    return título;
  }

  public int getCódigoDePreço() {
    return códigoDePreço;
  }

  public void setCódigoDePreço(int códigoDePreço) {
    this.códigoDePreço = códigoDePreço;
  }
}
public class Aluguel {
  private Fita fita;
  private int diasAlugada;

  public Aluguel(Fita fita, int diasAlugada) {
    this.fita = fita;
    this.diasAlugada = diasAlugada;
  }

  public Fita getFita() {
    return fita;
  }

  public int getDiasAlugada() {
    return diasAlugada;
  }
}
import java.util.*;

public class Cliente {
  private String nome;
  private Collection fitasAlugadas = new ArrayList();

  public Cliente(String nome) {
    this.nome = nome;
  }

  public String getNome() {
    return nome;
  }

  public void adicionaAluguel(Aluguel aluguel) {
    fitasAlugadas.add(aluguel);
  }

  public String extrato() {
    final String fimDeLinha = System.getProperty("line.separator");
    double valorTotal = 0.0;
    int pontosDeAlugadorFrequente = 0;
    Iterator alugueis = fitasAlugadas.iterator();
    String resultado = "Registro de Alugueis de " + getNome() + fimDeLinha;
    while(alugueis.hasNext()) {
      double valorCorrente = 0.0;
      Aluguel cada = (Aluguel)alugueis.next();

      // determina valores para cada linha
      switch(cada.getFita().getCódigoDePreço()) {
      case Fita.NORMAL:
        valorCorrente += 2;
        if(cada.getDiasAlugada() > 2) {
          valorCorrente += (cada.getDiasAlugada() - 2) * 1.5;
        }
        break;
      case Fita.LANÇAMENTO:
        valorCorrente += cada.getDiasAlugada() * 3;
        break;
      case Fita.INFANTIL:
        valorCorrente += 1.5;
        if(cada.getDiasAlugada() > 3) {
          valorCorrente += (cada.getDiasAlugada() - 3) * 1.5;
        }
        break;
      } //switch
      // trata de pontos de alugador frequente
      pontosDeAlugadorFrequente++;
      // adiciona bonus para aluguel de um lançamento por pelo menos 2 dias
      if(cada.getFita().getCódigoDePreço() == Fita.LANÇAMENTO &&
         cada.getDiasAlugada() > 1) {
         pontosDeAlugadorFrequente++;
      }

      // mostra valores para este aluguel
      resultado += "\t" + cada.getFita().getTítulo() + "\t" + valorCorrente + fimDeLinha;
      valorTotal += valorCorrente;
    } // while
    // adiciona rodapé
    resultado += "Valor total devido: " + valorTotal + fimDeLinha;
    resultado += "Voce acumulou " + pontosDeAlugadorFrequente +
              " pontos de alugador frequente";
    return resultado;
  }
}
public class Locadora {
  public static void main(String[] args) {
    Cliente c1 = new Cliente("Juliana");

    c1.adicionaAluguel(new Aluguel(new Fita("O Exorcista                   ", Fita.NORMAL), 3));
    c1.adicionaAluguel(new Aluguel(new Fita("Men in Black                  ", Fita.NORMAL), 2));
    c1.adicionaAluguel(new Aluguel(new Fita("Jurassic Park III             ", Fita.LANÇAMENTO), 3));
    c1.adicionaAluguel(new Aluguel(new Fita("Planeta dos Macacos           ", Fita.LANÇAMENTO), 4));
    c1.adicionaAluguel(new Aluguel(new Fita("Pateta no Planeta dos Macacos ", Fita.INFANTIL), 10));
    c1.adicionaAluguel(new Aluguel(new Fita("O Rei Leao                    ", Fita.INFANTIL), 30));

    System.out.println(c1.extrato());
  }
}

Comentários sobre o Programa Original

Refatoramento: Decomposição e Redistribuição do método extrato()

  public String extrato() {
    final String fimDeLinha = System.getProperty("line.separator");
    double valorTotal = 0.0;
    int pontosDeAlugadorFrequente = 0;
    Iterator alugueis = fitasAlugadas.iterator();
    String resultado = "Registro de Alugueis de " + getNome() + fimDeLinha;
    while(alugueis.hasNext()) {
      double valorCorrente = 0.0;
      Aluguel cada = (Aluguel)alugueis.next();

      // determina valores para cada linha
      switch(cada.getFita().getCódigoDePreço()) {
      case Fita.NORMAL:
        valorCorrente += 2;
        if(cada.getDiasAlugada() > 2) {
          valorCorrente += (cada.getDiasAlugada() - 2) * 1.5;
        }
        break;
      case Fita.LANÇAMENTO:
        valorCorrente += cada.getDiasAlugada() * 3;
        break;
      case Fita.INFANTIL:
        valorCorrente += 1.5;
        if(cada.getDiasAlugada() > 3) {
          valorCorrente += (cada.getDiasAlugada() - 3) * 1.5;
        }
        break;
      } //switch
      // trata de pontos de alugador frequente
      pontosDeAlugadorFrequente++;
      // adiciona bonus para aluguel de um lançamento por pelo menos 2 dias
      if(cada.getFita().getCódigoDePreço() == Fita.LANÇAMENTO &&
         cada.getDiasAlugada() > 1) {
         pontosDeAlugadorFrequente++;
      }

      // mostra valores para este aluguel
      resultado += "\t" + cada.getFita().getTítulo() + "\t" + valorCorrente + fimDeLinha;
      valorTotal += valorCorrente;
    } // while
    // adiciona rodapé
    resultado += "Valor total devido: " + valorTotal + fimDeLinha;
    resultado += "Voce acumulou " + pontosDeAlugadorFrequente +
              " pontos de alugador frequente";
    return resultado;
  }
  public String extrato() {
    final String fimDeLinha = System.getProperty("line.separator");
    double valorTotal = 0.0;
    int pontosDeAlugadorFrequente = 0;
    Iterator alugueis = fitasAlugadas.iterator();
    String resultado = "Registro de Alugueis de " + getNome() + fimDeLinha;
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();

      valorCorrente = valorDeUmAluguel(cada);

      // trata de pontos de alugador frequente
      pontosDeAlugadorFrequente++;
      // adiciona bonus para aluguel de um lançamento por pelo menos 2 dias
      if(cada.getFita().getCódigoDePreço() == Fita.LANÇAMENTO &&
         cada.getDiasAlugada() > 1) {
         pontosDeAlugadorFrequente++;
      }

      // mostra valores para este aluguel
      resultado += "\t" + cada.getFita().getTítulo() + "\t" + valorCorrente + fimDeLinha;
      valorTotal += valorCorrente;
    } // while
    // adiciona rodapé
    resultado += "Valor total devido: " + valorTotal + fimDeLinha;
    resultado += "Voce acumulou " + pontosDeAlugadorFrequente +
              " pontos de alugador frequente";
    return resultado;
  }
  private int valorDeUmAluguel(Aluguel cada) {
    int valorCorrente = 0;
    // determina valores para cada linha
    switch(cada.getFita().getCódigoDePreço()) {
    case Fita.NORMAL:
      valorCorrente += 2;
      if(cada.getDiasAlugada() > 2) {
        valorCorrente += (cada.getDiasAlugada() - 2) * 1.5;
      }
      break;
    case Fita.LANÇAMENTO:
      valorCorrente += cada.getDiasAlugada() * 3;
      break;
    case Fita.INFANTIL:
      valorCorrente += 1.5;
      if(cada.getDiasAlugada() > 3) {
        valorCorrente += (cada.getDiasAlugada() - 3) * 1.5;
      }
      break;
    } //switch
    return valorCorrente;
  }
  private double valorDeUmAluguel(Aluguel cada) {
    double valorCorrente = 0;
    // determina valores para cada linha
    switch(cada.getFita().getCódigoDePreço()) {
    case Fita.NORMAL:
      valorCorrente += 2;
      if(cada.getDiasAlugada() > 2) {
        valorCorrente += (cada.getDiasAlugada() - 2) * 1.5;
      }
      break;
    case Fita.LANÇAMENTO:
      valorCorrente += cada.getDiasAlugada() * 3;
      break;
    case Fita.INFANTIL:
      valorCorrente += 1.5;
      if(cada.getDiasAlugada() > 3) {
        valorCorrente += (cada.getDiasAlugada() - 3) * 1.5;
      }
      break;
    } //switch
    return valorCorrente;
  }
  private double valorDeUmAluguel(Aluguel umAluguel) {
    double valorDoAluguel = 0;
    // determina valores para cada linha
    switch(umAluguel.getFita().getCódigoDePreço()) {
    case Fita.NORMAL:
      valorDoAluguel += 2;
      if(umAluguel.getDiasAlugada() > 2) {
        valorDoAluguel += (umAluguel.getDiasAlugada() - 2) * 1.5;
      }
      break;
    case Fita.LANÇAMENTO:
      valorDoAluguel += umAluguel.getDiasAlugada() * 3;
      break;
    case Fita.INFANTIL:
      valorDoAluguel += 1.5;
      if(umAluguel.getDiasAlugada() > 3) {
        valorDoAluguel += (umAluguel.getDiasAlugada() - 3) * 1.5;
      }
      break;
    } //switch
    return valorDoAluguel;
  }

Refatoramento: Movendo o Cálculo de Valores

class Cliente ...
  private double valorDeUmAluguel(Aluguel umAluguel) {
    double valorDoAluguel = 0;
    // determina valores para cada linha
    switch(umAluguel.getFita().getCódigoDePreço()) {
    case Fita.NORMAL:
      valorDoAluguel += 2;
      if(umAluguel.getDiasAlugada() > 2) {
        valorDoAluguel += (umAluguel.getDiasAlugada() - 2) * 1.5;
      }
      break;
    case Fita.LANÇAMENTO:
      valorDoAluguel += umAluguel.getDiasAlugada() * 3;
      break;
    case Fita.INFANTIL:
      valorDoAluguel += 1.5;
      if(umAluguel.getDiasAlugada() > 3) {
        valorDoAluguel += (umAluguel.getDiasAlugada() - 3) * 1.5;
      }
      break;
    } //switch
    return valorDoAluguel;
  }
class Aluguel ...
  double getValorDoAluguel() { // observe que o parâmetro umAluguel sumiu
    double valorDoAluguel = 0;
    // determina valores para cada linha
    switch(getFita().getCódigoDePreço()) {
    case Fita.NORMAL:
      valorDoAluguel += 2;
      if(getDiasAlugada() > 2) {
        valorDoAluguel += (getDiasAlugada() - 2) * 1.5;
      }
      break;
    case Fita.LANÇAMENTO:
      valorDoAluguel += getDiasAlugada() * 3;
      break;
    case Fita.INFANTIL:
      valorDoAluguel += 1.5;
      if(getDiasAlugada() > 3) {
        valorDoAluguel += (getDiasAlugada() - 3) * 1.5;
      }
      break;
    } //switch
    return valorDoAluguel;
  }

class Cliente ...
  private double valorDeUmAluguel(Aluguel umAluguel) {
    return umAluguel.getValorDoAluguel();
  }
class Cliente ...
      ...
      valorCorrente = valorDeUmAluguel(cada);
      ...
class Cliente ...
      ...
      valorCorrente = cada.getValorDoAluguel();
      ...
  public String extrato() {
    final String fimDeLinha = System.getProperty("line.separator");
    double valorTotal = 0.0;
    int pontosDeAlugadorFrequente = 0;
    Iterator alugueis = fitasAlugadas.iterator();
    String resultado = "Registro de Alugueis de " + getNome() + fimDeLinha;
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();

      valorCorrente = valorDeUmAluguel(cada);

      // trata de pontos de alugador frequente
      pontosDeAlugadorFrequente++;
      // adiciona bonus para aluguel de um lançamento por pelo menos 2 dias
      if(cada.getFita().getCódigoDePreço() == Fita.LANÇAMENTO &&
         cada.getDiasAlugada() > 1) {
         pontosDeAlugadorFrequente++;
      }

      // mostra valores para este aluguel
      resultado += "\t" + cada.getFita().getTítulo() + "\t" + valorCorrente + fimDeLinha;
      valorTotal += valorCorrente;
    } // while
    // adiciona rodapé
    resultado += "Valor total devido: " + valorTotal + fimDeLinha;
    resultado += "Voce acumulou " + pontosDeAlugadorFrequente +
              " pontos de alugador frequente";
    return resultado;
  }
  public String extrato() {
    final String fimDeLinha = System.getProperty("line.separator");
    double valorTotal = 0.0;
    int pontosDeAlugadorFrequente = 0;
    Iterator alugueis = fitasAlugadas.iterator();
    String resultado = "Registro de Alugueis de " + getNome() + fimDeLinha;
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();

      // trata de pontos de alugador frequente
      pontosDeAlugadorFrequente++;
      // adiciona bonus para aluguel de um lançamento por pelo menos 2 dias
      if(cada.getFita().getCódigoDePreço() == Fita.LANÇAMENTO &&
         cada.getDiasAlugada() > 1) {
         pontosDeAlugadorFrequente++;
      }

      // mostra valores para este aluguel
      resultado += "\t" + cada.getFita().getTítulo() + "\t" +
                   cada.getValorDoAluguel() + fimDeLinha;
      valorTotal += cada.getValorDoAluguel();
    } // while
    // adiciona rodapé
    resultado += "Valor total devido: " + valorTotal + fimDeLinha;
    resultado += "Voce acumulou " + pontosDeAlugadorFrequente +
              " pontos de alugador frequente";
    return resultado;
  }

Refatoramento: Extração de Pontos de Alugador Frequente

  public String extrato() {
    final String fimDeLinha = System.getProperty("line.separator");
    double valorTotal = 0.0;
    int pontosDeAlugadorFrequente = 0;
    Iterator alugueis = fitasAlugadas.iterator();
    String resultado = "Registro de Alugueis de " + getNome() + fimDeLinha;
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();

      // trata de pontos de alugador frequente
      pontosDeAlugadorFrequente++;
      // adiciona bonus para aluguel de um lançamento por pelo menos 2 dias
      if(cada.getFita().getCódigoDePreço() == Fita.LANÇAMENTO &&
         cada.getDiasAlugada() > 1) {
         pontosDeAlugadorFrequente++;
      }

      // mostra valores para este aluguel
      resultado += "\t" + cada.getFita().getTítulo() + "\t" +
                   cada.getValorDoAluguel() + fimDeLinha;
      valorTotal += cada.getValorDoAluguel();
    } // while
    // adiciona rodapé
    resultado += "Valor total devido: " + valorTotal + fimDeLinha;
    resultado += "Voce acumulou " + pontosDeAlugadorFrequente +
              " pontos de alugador frequente";
    return resultado;
  }
class Cliente ...
  public String extrato() {
    final String fimDeLinha = System.getProperty("line.separator");
    double valorTotal = 0.0;
    int pontosDeAlugadorFrequente = 0;
    Iterator alugueis = fitasAlugadas.iterator();
    String resultado = "Registro de Alugueis de " + getNome() + fimDeLinha;
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();
      pontosDeAlugadorFrequente += cada.getPontosDeAlugadorFrequente();

      // mostra valores para este aluguel
      resultado += "\t" + cada.getFita().getTítulo() + "\t" +
                   cada.getValorDoAluguel() + fimDeLinha;
      valorTotal += cada.getValorDoAluguel();
    } // while
    // adiciona rodapé
    resultado += "Valor total devido: " + valorTotal + fimDeLinha;
    resultado += "Voce acumulou " + pontosDeAlugadorFrequente +
              " pontos de alugador frequente";
    return resultado;
  }

class Aluguel ...
  int getPontosDeAlugadorFrequente() {
    if(getFita().getCódigoDePreço() == Fita.LANÇAMENTO && getDiasAlugada() > 1) {
       return 2;
    } else {
       return 1;
    }
  }

Refatoramento: Remoção de Variáveis Temporárias

class Cliente ...
  public String extrato() {
    final String fimDeLinha = System.getProperty("line.separator");
    double valorTotal = 0.0;
    int pontosDeAlugadorFrequente = 0;
    Iterator alugueis = fitasAlugadas.iterator();
    String resultado = "Registro de Alugueis de " + getNome() + fimDeLinha;
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();
      pontosDeAlugadorFrequente += cada.getPontosDeAlugadorFrequente();

      // mostra valores para este aluguel
      resultado += "\t" + cada.getFita().getTítulo() + "\t" +
                   cada.getValorDoAluguel() + fimDeLinha;
      valorTotal += cada.getValorDoAluguel();
    } // while
    // adiciona rodapé
    resultado += "Valor total devido: " + valorTotal + fimDeLinha;
    resultado += "Voce acumulou " + pontosDeAlugadorFrequente +
              " pontos de alugador frequente";
    return resultado;
  }
class Cliente ...
  public String extrato() {
    final String fimDeLinha = System.getProperty("line.separator");
    Iterator alugueis = fitasAlugadas.iterator();
    String resultado = "Registro de Alugueis de " + getNome() + fimDeLinha;
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();

      // mostra valores para este aluguel
      resultado += "\t" + cada.getFita().getTítulo() + "\t" +
                   cada.getValorDoAluguel() + fimDeLinha;
    } // while
    // adiciona rodapé
    resultado += "Valor total devido: " + getValorTotal() + fimDeLinha;
    resultado += "Voce acumulou " + getPontosTotaisDeAlugadorFrequente() +
              " pontos de alugador frequente";
    return resultado;
  }

  private double getValorTotal() {
    double valorTotal = 0.0;
    Iterator alugueis = fitasAlugadas.iterator();
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();
      valorTotal += cada.getValorDoAluguel();
    }
    return valorTotal;    
  }

  private int getPontosTotaisDeAlugadorFrequente() {
    int pontos = 0;
    Iterator alugueis = fitasAlugadas.iterator();
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();
      pontos += cada.getPontosDeAlugadorFrequente();
    }
    return pontos;
  }
class Cliente ...
  public String extratoHTML() {
    Iterator alugueis = fitasAlugadas.iterator();
    String resultado = "<H1>Registro de Alugueis de <EM>" +
                       getNome() + "</EM></H1><P>\n";
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();

      // mostra valores para este aluguel
      resultado += cada.getFita().getTítulo() + ": " +
                   cada.getValorDoAluguel() + "<BR>\n";
    } // while
    // adiciona rodapé
    resultado += "<P>Valor total devido: <EM>" + getValorTotal() + "</EM>\n";
    resultado += "<P>Voce acumulou <EM>" + getPontosTotaisDeAlugadorFrequente() +
              "</EM> pontos de alugador frequente";
    return resultado;
  }

Refatoramento: Responsabilidades onde estão os dados

class Aluguel ...
  double getValorDoAluguel() {
    double valorDoAluguel = 0;
    // determina valores para cada linha
    switch(getFita().getCódigoDePreço()) {
    case Fita.NORMAL:
      valorDoAluguel += 2;
      if(getDiasAlugada() > 2) {
        valorDoAluguel += (getDiasAlugada() - 2) * 1.5;
      }
      break;
    case Fita.LANÇAMENTO:
      valorDoAluguel += getDiasAlugada() * 3;
      break;
    case Fita.INFANTIL:
      valorDoAluguel += 1.5;
      if(getDiasAlugada() > 3) {
        valorDoAluguel += (getDiasAlugada() - 3) * 1.5;
      }
      break;
    } //switch
    return valorDoAluguel;
  }
class Aluguel ...
  double getValorDoAluguel() {
    return fita.getValorDoAluguel(diasAlugada);
  }
}

class Fita ...
  double getValorDoAluguel(int diasAlugada) {
    double valorDoAluguel = 0;
    switch(getCódigoDePreço()) {
    case NORMAL:
      valorDoAluguel += 2;
      if(diasAlugada > 2) {
        valorDoAluguel += (diasAlugada - 2) * 1.5;
      }
      break;
    case LANÇAMENTO:
      valorDoAluguel += diasAlugada * 3;
      break;
    case INFANTIL:
      valorDoAluguel += 1.5;
      if(diasAlugada > 3) {
        valorDoAluguel += (diasAlugada - 3) * 1.5;
      }
      break;
    } //switch
    return valorDoAluguel;
  }
}
class Aluguel ...
  int getPontosDeAlugadorFrequente() {
    if(getFita().getCódigoDePreço() == Fita.LANÇAMENTO && getDiasAlugada() > 1) {
       return 2;
    } else {
       return 1;
    }
  }  
class Aluguel ...
  int getPontosDeAlugadorFrequente() {
    return fita.getPontosDeAlugadorFrequente(diasAlugada);
  }


class Fita ...  
  int getPontosDeAlugadorFrequente(int diasAlugada) {
    if(getCódigoDePreço() == LANÇAMENTO && diasAlugada > 1) {
       return 2;
    } else {
       return 1;
    }
  }

Refatoramento: Uso de Polimorfismo

Refatoramento: Interfaces

public class Aluguel {
  private Fita fita;
  private int diasAlugada;

  public Aluguel(Fita fita, int diasAlugada) {
    this.fita = fita;
    this.diasAlugada = diasAlugada;
  }

  public Fita getFita() {
    return fita;
  }
  ...
}
interface Alugavel {
  String getTítulo();
  double getValorDoAluguel(int diasAlugada);
  int getPontosDeAlugadorFrequente(int diasAlugada);
}

class Fita implements Alugavel {
  ...
}

public class Aluguel {
  private Alugavel fita;
  private int diasAlugada;

  public Aluguel(Alugavel fita, int diasAlugada) {
    this.fita = fita;
    this.diasAlugada = diasAlugada;
  }

  public Alugavel getFita() {
    return fita;
  }
  ...
}

Refatoramento: Herança

Fitas.gif (9073 bytes)

Classificacao.gif (12493 bytes)

class Fita ...
  private int códigoDePreço;

  public Fita(String título, int códigoDePreço) {
    this.título = título;
    this.códigoDePreço = códigoDePreço;
  }

  public int getCódigoDePreço() {
    return códigoDePreço;
  }

  public void setCódigoDePreço(int códigoDePreço) {
    this.códigoDePreço = códigoDePreço;
  }
class Fita ...
  private Classificacao classificação;

  public Fita(String título, int códigoDePreço) {
    this.título = título;
    setCódigoDePreço(códigoDePreço);
  }

  public int getCódigoDePreço() {
    return classificação.getCódigoDePreço();
  }

  public void setCódigoDePreço(int códigoDePreço) {
    switch(códigoDePreço) {
    case NORMAL:
      classificação = new ClassificacaoNormal();
      break;
    case LANÇAMENTO:
      classificação = new ClassificacaoLancamento();
      break;
    case INFANTIL:
      classificação = new ClassificacaoInfantil();
      break;
    }
  }
}

public abstract class Classificacao {
  public abstract int getCódigoDePreço();
}

public class ClassificacaoNormal extends Classificacao {
  public abstract int getCódigoDePreço() {
    return Fita.NORMAL;
  }
}

public class ClassificacaoLancamento extends Classificacao {
  public abstract int getCódigoDePreço() {
    return Fita.LANÇAMENTO;
  }
}

public class ClassificacaoInfantil extends Classificacao {
  public abstract int getCódigoDePreço() {
    return Fita.INFANTIL;
  }
}
class Fita ...
  public double getValorDoAluguel(int diasAlugada) {
    double valorDoAluguel = 0;
    switch(getCódigoDePreço()) {
    case NORMAL:
      valorDoAluguel += 2;
      if(diasAlugada > 2) {
        valorDoAluguel += (diasAlugada - 2) * 1.5;
      }
      break;
    case LANÇAMENTO:
      valorDoAluguel += diasAlugada * 3;
      break;
    case INFANTIL:
      valorDoAluguel += 1.5;
      if(diasAlugada > 3) {
        valorDoAluguel += (diasAlugada - 3) * 1.5;
      }
      break;
    } //switch
    return valorDoAluguel;
  }
}
class Fita ...
  public double getValorDoAluguel(int diasAlugada) {
    return classificação.getValorDoAluguel(diasAlugada);
  }

class Classificacao
  public double getValorDoAluguel(int diasAlugada) {
    double valorDoAluguel = 0;
    switch(getCódigoDePreço()) {
    case Fita.NORMAL:
      valorDoAluguel += 2;
      if(diasAlugada > 2) {
        valorDoAluguel += (diasAlugada - 2) * 1.5;
      }
      break;
    case Fita.LANÇAMENTO:
      valorDoAluguel += diasAlugada * 3;
      break;
    case Fita.INFANTIL:
      valorDoAluguel += 1.5;
      if(diasAlugada > 3) {
        valorDoAluguel += (diasAlugada - 3) * 1.5;
      }
      break;
    } //switch
    return valorDoAluguel;
  }
}
class Classificacao
  public double getValorDoAluguel(int diasAlugada) {
    double valorDoAluguel = 0;
    switch(getCódigoDePreço()) {
    case Fita.NORMAL:
      valorDoAluguel += 2;
      if(diasAlugada > 2) {
        valorDoAluguel += (diasAlugada - 2) * 1.5;
      }
      break;
    case Fita.LANÇAMENTO:
      valorDoAluguel += diasAlugada * 3;
      break;
    case Fita.INFANTIL:
      valorDoAluguel += 1.5;
      if(diasAlugada > 3) {
        valorDoAluguel += (diasAlugada - 3) * 1.5;
      }
      break;
    } //switch
    return valorDoAluguel;
  }
}
abstract class Classificacao
  abstract double getValorDoAluguel(int diasAlugada);

class ClassificacaoNormal extends Classificacao {
  public double getValorDoAluguel(int diasAlugada) {
    double valorDoAluguel = 2;
    if(diasAlugada > 2) {
      valorDoAluguel += (diasAlugada - 2) * 1.5;
    }
    return valorDoAluguel;
  }
}

class ClassificacaoLancamento extends Classificacao {
  public double getValorDoAluguel(int diasAlugada) {
    return diasAlugada * 3;
  }
}

class ClassificacaoInfantil extends Classificacao {
  public double getValorDoAluguel(int diasAlugada) {
    double valorDoAluguel = 1.5;
    if(diasAlugada > 3) {
      valorDoAluguel += (diasAlugada - 3) * 1.5;
    }
    return valorDoAluguel;
  }
}

A solução final

Locadora-final.gif (25693 bytes)

Locadora-sequencia.gif (18537 bytes)

public class Locadora {
  public static void main(String[] args) {
    Cliente c1 = new Cliente("Juliana");

    c1.adicionaAluguel(new Aluguel(new Fita("O Exorcista                   ", Fita.NORMAL), 3));
    c1.adicionaAluguel(new Aluguel(new Fita("Men in Black                  ", Fita.NORMAL), 2));
    c1.adicionaAluguel(new Aluguel(new Fita("Jurassic Park III             ", Fita.LANÇAMENTO), 3));
    c1.adicionaAluguel(new Aluguel(new Fita("Planeta dos Macacos           ", Fita.LANÇAMENTO), 4));
    c1.adicionaAluguel(new Aluguel(new Fita("Pateta no Planeta dos Macacos ", Fita.INFANTIL), 10));
    c1.adicionaAluguel(new Aluguel(new Fita("O Rei Leao                    ", Fita.INFANTIL), 30));

    System.out.println(c1.extrato());
  }
}

import java.util.*;

public class Cliente {
  private String nome;
  private Collection fitasAlugadas = new ArrayList();

  public Cliente(String nome) {
    this.nome = nome;
  }

  public String getNome() {
    return nome;
  }

  public void adicionaAluguel(Aluguel aluguel) {
    fitasAlugadas.add(aluguel);
  }

  public String extrato() {
    final String fimDeLinha = System.getProperty("line.separator");
    Iterator alugueis = fitasAlugadas.iterator();
    String resultado = "Registro de Alugueis de " + getNome() + fimDeLinha;
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();

      // mostra valores para este aluguel
      resultado += "\t" + cada.getFita().getTítulo() + "\t" +
                   cada.getValorDoAluguel() + fimDeLinha;
    } // while
    // adiciona rodapé
    resultado += "Valor total devido: " + getValorTotal() + fimDeLinha;
    resultado += "Voce acumulou " + getPontosTotaisDeAlugadorFrequente() +
              " pontos de alugador frequente";
    return resultado;
  }

  private double getValorTotal() {
    double valorTotal = 0.0;
    Iterator alugueis = fitasAlugadas.iterator();
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();
      valorTotal += cada.getValorDoAluguel();
    }
    return valorTotal;    
  }

  private int getPontosTotaisDeAlugadorFrequente() {
    int pontos = 0;
    Iterator alugueis = fitasAlugadas.iterator();
    while(alugueis.hasNext()) {
      Aluguel cada = (Aluguel)alugueis.next();
      pontos += cada.getPontosDeAlugadorFrequente();
    }
    return pontos;
  }
}

public class Aluguel {
  private Alugavel fita;
  private int diasAlugada;

  public Aluguel(Alugavel fita, int diasAlugada) {
    this.fita = fita;
    this.diasAlugada = diasAlugada;
  }

  public Alugavel getFita() {
    return fita;
  }

  public int getDiasAlugada() {
    return diasAlugada;
  }

  double getValorDoAluguel() {
    return fita.getValorDoAluguel(diasAlugada);
  }

  int getPontosDeAlugadorFrequente() {
    return fita.getPontosDeAlugadorFrequente(diasAlugada);
  }
}

interface Alugavel {
  String getTítulo();
  double getValorDoAluguel(int diasAlugada);
  int getPontosDeAlugadorFrequente(int diasAlugada);
}

public class Fita implements Alugavel {
  public static final int NORMAL = 0;
  public static final int LANÇAMENTO = 1;
  public static final int INFANTIL = 2;

  private String título;
  private Classificacao classificação;

  public Fita(String título, int códigoDePreço) {
    this.título = título;
    setCódigoDePreço(códigoDePreço);
  }

  public String getTítulo() {
    return título;
  }

  public int getCódigoDePreço() {
    return classificação.getCódigoDePreço();
  }

  public void setCódigoDePreço(int códigoDePreço) {
    switch(códigoDePreço) {
    case NORMAL:
      classificação = new ClassificacaoNormal();
      break;
    case LANÇAMENTO:
      classificação = new ClassificacaoLancamento();
      break;
    case INFANTIL:
      classificação = new ClassificacaoInfantil();
      break;
    }
  }
  public double getValorDoAluguel(int diasAlugada) {
    return classificação.getValorDoAluguel(diasAlugada);
  }
  public int getPontosDeAlugadorFrequente(int diasAlugada) {
    return classificação.getPontosDeAlugadorFrequente(diasAlugada);
  }
}

public abstract class Classificacao {
  abstract int getCódigoDePreço();
  abstract double getValorDoAluguel(int diasAlugada);

  int getPontosDeAlugadorFrequente(int diasAlugadas) {
    return 1;
  }
}

class ClassificacaoNormal extends Classificacao {
  int getCódigoDePreço() {
    return Fita.NORMAL;
  }

  double getValorDoAluguel(int diasAlugada) {
    double valorDoAluguel = 2;
    if(diasAlugada > 2) {
      valorDoAluguel += (diasAlugada - 2) * 1.5;
    }
    return valorDoAluguel;
  }
}

class ClassificacaoLancamento extends Classificacao {
  int getCódigoDePreço() {
    return Fita.LANÇAMENTO;
  }

  double getValorDoAluguel(int diasAlugada) {
    return diasAlugada * 3;
  }

  int getPontosDeAlugadorFrequente(int diasAlugadas) {
    return (diasAlugadas > 1) ? 2 : 1;
  }
}

class ClassificacaoInfantil extends Classificacao {
  int getCódigoDePreço() {
    return Fita.INFANTIL;
  }

  double getValorDoAluguel(int diasAlugada) {
    double valorDoAluguel = 1.5;
    if(diasAlugada > 3) {
      valorDoAluguel += (diasAlugada - 3) * 1.5;
    }
    return valorDoAluguel;
  }
}

Palavras Finais

oo-9 programa anterior próxima