Você programa orientado a objetos?

October 29, 2010

De vez em quando alguem me pergunta se eu programo orientado a objetos.

Isso sempre me faz refletir: Afinal, o que é programar orientado a objetos? Existe uma divisão clara entre o que é considerado código OO e o que não é? Basta escrever código em classes? Francamente, eu não sei qual o limiar entre código OO e não OO. Alias, creio que isso seja bastante subjetivo, o que eu considero como OO, outra pessoa pode não considerar.

Outra frase que frequentemente chega aos meus ouvidos é que “em Java você sempre programa OO, não tem como fugir”. Ah, então programar orientado a objetos é isso? Basta enfiar tudo dentro de classes? Eu particularmente me recuso a considerar um projeto como sendo OO apenas por que usa classes.

Então o que eu considero como OO, afinal de contas? Para responder a essa pergunta, antes devemos responder a outra: Que vantagens você espera ao programar orientado a objetos? Parecer cool não conta, por favor…
Vamos lá: Melhorar o reuso de código, design mais limpo, facilidade de manutenção, testabilidade, etc.
(Apenas para deixar claro, não acho que orientação a objetos seja a única forma de escrever código limpo, reutilizavel, etc).

Então agora posso responder àquela outra pergunta: Considero um código como sendo OO se ele foi escrito de forma a deliberadamente utilizar os conceitos de orientação a objetos (herança, polimorfismo, etc) tendo em vista atingir os objetivos que citei acima, e mais especificamente, quando estes objetivos de fato começam a ser alcançados.

Não adianta eu forçar a barra para enfiar todo meu código dentro de classes, bolar hierarquias de herança mirabolantes com 500 classes, se meu código continua não sendo reutilizavel, é de dificil manutenção, é de dificil entendimento, é acoplado, etc. Então voltando à questão do Java, eu digo que tem muito código procedural em Java por aí que está apenas maquiado para parecer OO. Apenas com a experiência nós iremos aprender a programar direito, não importa em qual paradigma.

Podemos decorar dúzias de design patterns, saber na ponta da lingua as definições de polimorfismo, encapsulamento, herança, conhecer UML, etc. Para quem vem de outros paradigmas, a orientação a objetos é uma forma muito diferente de pensar, então necessariamente no inicio a gente vai fazer merda!

O importante é não estacionar. Não podemos pensar que nosso design está uma maravilha só por que utilizamos heranças, padrões de projeto e coisas assim. É importante ser auto-crítico sempre, avaliar a qualidade do nosso código, ver se as nossas decisões de design estão de fato ajudando em algo. É bom tambem buscar a opinião alheia e ter humildade para recebe-la, pois temos dificuldade em enxergar nossas próprias falhas. Com o tempo nossa forma de pensar irá se aperfeiçoando, os conceitos vão sendo melhor entendidos, passamos a ter experiência para avaliar as possíveis consequencias de um determinado design, e aí sim vamos começar a fazer algo legal.

Então… Se eu programo orientado a objetos? Não sei, e não me importo com isso. Eu só me importo em utilizar os recursos que conheço (sejam de programação procedural, OO, funcional ou o que quer que seja) para facilitar a minha vida como programador.

6

Agile com a Giran no III InovaTI

October 26, 2010

Acabo de chegar do III InovaTI, um evento anual promovido pelo Centro Universitário São Camilo, aqui em Cachoeiro.

Assisti a duas palestras, ambas sobre desenvolvimento ágil e ambas ministradas pelo pessoal da Giran.

Inicialmente o @makoto_vix e @wbotelhos deram um apanhado geral em XP e Scrum. Após um intervalo para lanche de 10 minutos onde eu fui forçado a tomar um banho de chuva por que tive a brilhante idéia de esquecer o guarda-chuva no carro, assistimos ao @jeveaux e @leohackin apresentando o case da própria Giran, as práticas que eles utilizam ao longo destes ~ 1,5 ano de história, o que deu certo e o que deu errado para eles.

Quem foi, teve uma excelente oportunidade para conhecer práticas modernas e positivas, bem como para ver a confirmação de que elas funcionam sim no mundo real. Para quem não foi, apenas deixo o meu conselho para não perder a próxima oportunidade.

2

Cuidado com a duplicidade de código

October 24, 2010

Duplicar código tem o potencial de trazer uma série de problemas, acho que todos concordamos com isso.

O problema é que em alguns casos essa duplicidade se manifesta de uma forma bem sutil, em que alguns programadores podem simplesmente nem perceber, ou achar que por ser algo bem simples não trará problema nenhum, ou então que simplesmente não tem como resolver.

Um exemplo simples, em Delphi:

procedure TForm1.FazAlgo1;
begin
  A;
  BeginTransaction;
  try
    B;
    Commit;
  except
    Rollback;
    raise;
  end;
  C;
end;

procedure TForm1.FazAlgo2;
begin
  D;
  BeginTransaction;
  try
    E;
    Commit;
  except
    Rollback;
    raise;
  end;
  F;
end;

Considere A, B, C, D, E e F como sendo blocos de código do seu software, todos diferentes uns dos outros.

Boa parte do código destes dois métodos não tem qualquer semelhança, mas ainda assim podemos ver a duplicidade: Em ambos os casos temos a abertura de uma transação, um processamento e finalmente a confirmação da transação. Caso ocorra algum erro no processamento, desfazemos a transação.

Como resolver isso?

Existem diversas formas. Em Delphi, utilizando os métodos anonimos introduzidos no Delphi 2009, poderíamos ter algo assim:

procedure TForm1.FazAlgo1;
begin
  A;
  ExecutarEmTransacao(procedure
  begin
    B;
  end);
  C;
end;

procedure ExecutarEmTransacao(Proc: TProc);
begin
  BeginTransaction;
  try
    Proc;
    Commit;
  except
    Rollback;
    raise;
  end;
end;

Observe que o método FazAlgo1 ficou quatro linhas menor, mas isso pouco importa, o que estamos discutindo aqui não é sobre economizar linhas de código, mas sim sobre não repetir rotinas. Em outras palavras: No exemplo que acabei de dar, o meu objetivo foi evitar a repetição da rotina “Abre transação, executa o processamento, confirma a transação. Se houver um erro, desfaz a transação e solta o erro”.

Se eu não me preocupo em reduzir linhas de código, por que estou tão preocupado em repetir aquela rotina então? Vamos lá:

  • Pense: Toda vez que você precisar repetir aquele código de controle transacional, qual a possibilidade de você:
    1. Trocar o try-except por um try-finally
    2. Esquecer de abrir a transação
    3. Esquecer de confirmar a transação
    4. Esquecer de desfazer a transação
    5. Engolir a exceção após desfazer a transação (Isto é, esquecer de chamar o “raise” para continuar levantando a exceção para quem mais quiser pegar)
  • Poluição do código: Quem estiver olhando aqueles métodos FazAlgo1 e FazAlgo2 quer saber o que eles fazem. A pessoa vai querer saber que uma determinada parte do mesmo é executada no contexto de uma transação. Mas a pessoa não quer e não precisa saber em detalhes como esse controle transacional é implementado, pois isso só vai poluir e dificultar o seu entendimento do código
  • Manutenção: Se amanhã você decidir habilitar um log de abertura/fechamento de transações, você teria que sair alterando dezenas ou centenas de lugares que utilizam transações

Por estes motivos, eu digo que preferiria a segunda abordagem que mostrei independentemente de qualquer redução do número de linhas de código.

A título de informação: Utilizando AOP, isso poderia ser resolvido de forma ainda mais elegante. Em Java poderíamos contar com o AspectJ por exemplo.

Ainda em Delphi, uma outra forma sutil de repetição de código é quando precisamos iterar sobre todos os registros de um dataset. Normalmente é algo assim:

procedure FazAlgoA;
begin
  DataSet1.First;
  while not DataSet1.Eof do
  begin
    { Algum processamento aqui... }
    DataSet1.Next;
  end;
end;

Ou pior ainda, se este DataSet estiver ligado a algum componente visual e você quer iterar sem que o usuário perceba a movimentação e depois você precisa posicionar o dataset no registro onde ele estava inicialmente:

procedure FazAlgoA;
var
  BM: String;
begin
  DataSet1.DisableControls;
  try
    BM := DataSet1.Bookmark;
    DataSet1.First;
    try
      while not DataSet1.Eof do
      begin
        { Algum processamento aqui... }
        DataSet1.Next;
      end;
    finally
      DataSet1.Bookmark := BM;
    end;
  finally
    DataSet1.EnableControls;
  end;
end;

Agora pense em ter este mesmo comportamento, com isso:

procedure TForm1.FazAlgoA;
begin
  IterarEmDataSet(DataSet1, procedure
  begin
    { Algum processamento aqui... }
  end);
end;

“Ah, mas eu uso uma linguagem que não tem essas facilidades”. Paciência, irmão… Use a sua criatividade e resolva seus problemas com as ferramentas que você tem em mãos! Na nossa profissão, dependemos da criatividade tanto quanto um músico ou um pintor, então comece a utiliza-la! Uma boa forma de obter inspiração é estudando outras linguagens e também o código fonte escrito por outros programadores.

Por exemplo, eu já implementei essa iteração em Delphi utilizando recursos que estavam disponíveis desde uma das primeiras versões do mesmo. Embora não tenha ficado tão elegante quanto a solução com métodos anônimos, resolveu grande parte da repetição de código.

O que quero dizer aqui é que você não deve abandonar boas práticas de programação apenas por que o seu ambiente de desenvolvimento é limitado. É claro que há certas barreiras impostas pela linguagem que não podem ser quebradas, mas isso não é motivo para desistir sem ao menos se esforçar para solucionar o problema.

0

Introdução ao TDD – III Jacitec

October 22, 2010
Tags: , ,

No dia 20, eu e o @martinusso apresentamos a palestra “Introdução ao TDD – Desenvolvimento guiado por testes”, durante as atividades do III Jacitec – Jornada Academica de Informação, Tecnologia e Cultura, no IFES de Cachoeiro de Itapemirim.

Foi a primeira palestra de ambos (Apesar de que no dia anterior fizemos um test-drive para alguns parceiros da Gerpos), e felizmente obtivemos um retorno positivo!

Slides da palestra

O projeto que desenvolvemos durante a palestra está disponível no GitHub

Deixei o blog de lado por um tempo devido às coisas estarem um pouco tumultuadas para mim, mas já tenho vários posts no forno esperando para serem publicados!


Editado para incluir os slides

2

Não conviva com janelas quebradas

August 29, 2010

Um dos conselhos mais interessantes que li no The Pragmatic Programmers é esse: Não conviva com janelas quebradas.

O que isso quer dizer? Bom, os autores se apoiaram em um artigo (WK82) que cita a seguinte experiência: Em 1969, Philip Zimbardo, um psicólogo de Stanford, deixou um automovel não emplacado no Bronx, e outro equivalente em Palo Alto – California. O primeiro começou a ser atacado por vandalos em 10 minutos, e em 24 horas havia sido completamente depenado. Já o carro de Palo Alto permaneceu intocado por uma semana, quando então Zimbardo quebrou uma parte do carro com uma marreta e após isso, em poucas horas o veículo havia sido totalmente destruído por vandalos.

Enquanto algo está sendo bem cuidado, as pessoas tendem a mante-lo assim por que sabem que existe alguem preocupado com a integridade daquilo. Mas à partir do momento em que veem algum problema que foi deixado de lado (uma janela quebrada), as pessoas começam a se preocupar menos por que tem a impressão de que ninguem está se importando.

Em desenvolvimento de software, isso tambem vale. Uma janela quebrada seria as famosas gambiarras, uma falha na arquitetura, na documentação, um ponto do projeto que não se adequa aos padrões estabelecidos pela equipe, um bug, uma decisão tomada equivocadamente no passado, um problema de usabilidade, etc. Se a equipe enxerga um problema e não o resolve imediatamente, outros passarão a ter menos cuidado com o projeto e consequentemente outros problemas aparecerão.

1

Obtenha o máximo de informações das exceções

August 15, 2010

Estou experimentando o Google Guice em um projeto que estou desenvolvendo em Java, estou tentando configura-lo para injetar o Session do Hibernate nos meus serviços, e por algum motivo estou recebendo uma exceção ao instanciar o SessionFactory e esta é a mensagem que estou recebendo:

[ERROR]1) Error in custom provider, org.hibernate.InvalidMappingException: Could not parse mapping document from resource Cliente.hbm.xml
[ERROR] at br.com.crystalpoll.hibernate.HibernateModule.configure(HibernateModule.java:10)
[ERROR] while locating org.hibernate.Session
[ERROR] for parameter 0 at br.com.crystalpoll.domain.cliente.Clientes.(Clientes.java:12)
[ERROR] while locating br.com.crystalpoll.domain.cliente.Clientes
[ERROR] for parameter 0 at br.com.crystalpoll.ui.server.customer.listing.GetCustomersHandler.(GetCustomersHandler.java:45)
[ERROR] while locating br.com.crystalpoll.ui.server.customer.listing.GetCustomersHandler

Tudo que ele me permite saber sobre o erro do Hibernate é “org.hibernate.InvalidMappingException: Could not parse mapping document from resource Cliente.hbm.xml”.

E quanto ao stack trace da exceção? E quando às exceções primárias (a cadeia de exceções que foi sendo lançada e que culminou nessa org.hibernate.InvalidMappingException)?

Não sei se quem engoliu a exceção foi o Guice (aparentemente foi) ou o Gwt-Platform, um outro framework que uso nesse projeto e que em outros momentos já mostrou esse comportamento. De qualquer forma, aí vão duas recomendações:

  • Sempre preserve a causa primária das suas exceções. Isto é, se você capturou uma exceção X e em função disso você disparou uma exceção Y, procure manter uma referência a X em Y. Java sempre suportou isso, e o Delphi recentemente passou a suportar tambem. Isso ajuda muito na hora de interpretar o erro e descobrir o que está errado
  • Se você for logar uma exceção, jamais se esqueça de logar a pilha de chamadas que levou àquela exceção e tambem às que as originaram. O Delphi infelizmente não tem esse recurso out-of-the-box (o que para mim é uma vergonha), mas é possível conseguir isso com pacotes de terceiros como o madExcept ou EurekaLog.
0

Seja poliglota

July 31, 2010

Eu já li em alguns lugares que aprender um novo idioma faz bem para a nossa saúde mental. E aprender uma nova linguagem de programação também pode ser benéfico para a carreira profissional.

A idéia é que linguagens diferentes te obrigam a pensar de formas diferentes para resolver o mesmo problema, ou seja, isso evita que você fique preso a uma única forma de pensar. e eventualmente você aproveitará coisas de uma linguagem na outra. Eu iria pegar a minha edição de “Pragmatic Programmers” para copiar o trecho onde um dos autores comenta sobre isso (Se bem me lembro, ele falava sobre C++ e Smalltalk), mas vou ficar devendo pois o livro está guardado em alguma caixa por aí (Preciso comprar uma estante logo).

Mas se você já sabe Java, não adianta querer estudar C# (ao menos não em relação ao que estamos falando aqui). A idéia é que sejam linguagens de paradígmas diferentes daqueles que você já domina. E não precisa ser algo que você vá aplicar no dia-a-dia, o objetivo é apenas expandir os seus horizontes.

6

Minhas impressões sobre TDD

July 4, 2010
Tags:

Como diz o ditado: Antes tarde do que nunca. O Test-Driven Development (Desenvolvimento orientado a testes) já existe há uns bons anos e eu só lia a respeito sem nunca ter colocado em prática.

Após participar do Agile Brazil 2010 e ter assistido a algumas palestras sobre o tema, decidi começar a aplicar essa técnica em um projeto que já venho desenvolvendo há algum tempo. Após ficar nisso por uma semana, decidi expor as minhas impressões sobre essa experiência:

  • O design do código que eu fiz com TDD é muito superior ao código que eu fazia antes. O código é mais fácil de ler e dar manutenção.
  • TDD requer uma mudança de pensamento. Não dá pra usar TDD mantendo a mesma mentalidade que usávamos antes.
  • Até agora me saí melhor usando TDD em um framework, do que em projetos de aplicações web ou desktop que possuem interface com o usuário. Preciso estudar mais MVP ou algo do gênero!
  • 2

Campanha anti-if

June 18, 2010

A campanha anti-if visa eliminar o uso inadequado da instrução IF. Pode parecer bizarro a princípio, mas se pararmos para pensar a respeito vemos que faz sentido. Vejamos um exemplo retirado do site da campanha.
Primeiro, com if:

/* Exemplo em Java */
// Bond class
double calculateValue() {
  if(_type == BTP)  {
    return calculateBTPValue();
  } else if(_type == BOT) {
    return calculateBOTValue();
  } else {
    return calculateEUBValue();
  }
}
{ Exemplo em Delphi }
// TBond class
function TBond.calculateValue: Extended;
begin
  if _type = BTP then
    Result := calculateBTPValue
  else if _type = BOT then
    Result := calculateBOTValue
  else
    Result := calculateEUBValue;
end;

E agora sem o dito-cujo:

/* Exemplo em Java */
// Bond class
double calculateValue() {
  _bondProfile.calculate();
}

// AbstractBondProfile class
abstract double calculate();

// classe BTPBondProfile >> AbstractBondProfile
double calculate() {
  ...
}

// classe BOTBondProfile >> AbstractBondProfile
double calculate() {
  ...
}

// classe EUBondProfile >> AbstractBondProfile
double calculate() {
  ...
}
{ Exemplo em Delphi }
// TBond class
function TBond.calculateValue: Extended;
begin
  Result := _bondProfile.calculate;
end;

// TAbstractBondProfile class
type
  TAbstractBondProfile = class abstract
  public
    function calculate: Extended; virtual; abstract;
  end;

// classe TBTPBondProfile >> AbstractBondProfile
function TBTPBondProfile.calculate: Extended;
begin
  ...
end;

// classe TBOTBondProfile >> AbstractBondProfile
function TBOTBondProfile.calculate: Extended;
begin
  ...
end;

// classe TEUBBondProfile >> AbstractBondProfile
function TEUBBondProfile.calculate: Extended;
begin
  ...
end;

Através da orientação a objetos, eliminamos a necessidade do IF naquele código. Vantagens? Vamos lá:

  • Reusabilidade: A rotina de cálculo de cada “bond profile” (não me pergunte o que é isso, só estou utilizando a terminologia do exemplo retirado do site da campanha) foi implementada separadamente umas das outras, e separadamente da classe Bond. Isso permite que estes cálculos sejam reutilizados de uma forma que não seria possível no primeiro exemplo.
  • Legibilidade: O método calculateValue() da classe Bond ficou muito mais simples. Antes haviam vários possíveis caminhos de execução, e agora temos apenas um. Isso torna muito mais fácil ler e entender o código.
  • Adaptabilidade: Ficou muito mais simples adaptar o código para a inclusão de novos “bond profiles”. Olhe no método calculateValue() da classe Bond e imagine que aquele _bondProfile é obtido dinamicamente, podemos então adicionar novos tipos de “bond profile” sem modificar a classe Bond, isso torna muito simples adaptar o sistema a novas regras de negócio. Com isso, estamos aderindo ao chamado “Princípio aberto/fechado”, que diz que uma classe deve ser aberta para extensão, mas fechada para modificações. Ou, em outras palavras: Deve ser possível extender uma classe sem a necessidade de modificar o código fonte da mesma.

Vale ressaltar que o objetivo da campanha não é eliminar o uso do IF, pois existem situações onde seu uso é correto. Por exemplo:

/* Extemplo em Java */
public void efetuarVenda(Cliente cliente) {
  if (cliente.bloqueado())
    throw new ClienteBloqueadoException();

  // Prossegue com a venda
 .....
}
procedure TVenda.efetuarVenda(Cliente cliente);
begin
  if cliente.bloqueado then
    raise EClienteBloqueado.Create;

  // Prossegue com a venda
  ....
end;

Para quem se interessar, neste link pode ser obtido o código para inserir o logotipo em seu site.

Editado em 19/06/2010 para adicionar exemplos em Delphi

4

Boas práticas sobre exceções

June 13, 2010

O suporte a exceções se tornou comum nas linguagens de programação modernas. Este mecanismo permite tratar erros e situações inesperadas de forma muito elegante.

Sem esse recurso, as funções sinalizavam erros através do seu retorno ou pelo uso de alguma variável global, era mais ou menos assim:

const
  ERRO_DIVISAO_POR_ZERO = 0;

function Dividir(Dividendo, Divisor: Integer): Extended;
begin
  if Divisor = 0 then
    Result := ERRO_DIVISAO_POR_ZERO
  else
    Result := Dividendo/Divisor;
end;

Ou seja, convencionou-se um valor que ao ser retornado pela função, indica uma condição de divisão por zero.

Toda vez que a função for chamada, devemos testar esse valor. No final, nosso código vira algo como:

procedure Teste;
begin
  Retorno = FazAlgo;
  if Retorno = ERRO then
  begin
    ShowMessage('Ocorreu um erro');
    Exit;
  end;
end;

Eu estou lendo o livro “The Pragmatic Progammer – From journeyman to mater” (Em português, “O Programador Pragmático – De aprendiz a Mestre”). Hoje cheguei num ponto onde ele fala justamente do que eu escrevi acima. Vou transcrever o exemplo do livro:

if (socket.read(name) != OK) {
  retcode = BAD_READ;
}
else {
  processName(name);
  if (socket.read(address) != OK) {
    retcode = BAD_READ;
  }
  else {
    processAddress(address);
    if (socket.read(telNo) != OK) {
      retcode = BAD_READ;
    }
    else {
      // etc, etc...
    }
  }
}
return retcode;

Como diz o livro: “A lógica normal do seu programa pode acabar sendo totalmente obscurecida por tratamentos de erros”.
Usando exceções, isso poderia ser reescrito assim: (Novamente, transcrevendo código do livro)

retcode = OK;
try {
  socket.read(name);
  process(name);

  socket.read(address);
  processAddress(address);

  socket.read(telNo);
  // etc, etc
}
catch (IOException e) {
  retcode = BAD_READ;
  Logger.log("Error reading individual: " + e.getMessage());
}
return retcode;

Eu iria um pouco mais além: Repare que a rotina acima ainda usa códigos de retorno para sinalizar erros. Ou seja, quem estiver chamando ela, terá que fazer um tratamento de condições de erro semelhante ao que vimos antes, o que poderia acabar levando ao mesmo tipo de código confuso do primeiro exemplo do livro. Eu adotaria uma das duas estratégias seguintes:

  • Não capturar IOException. Ou seja, quando alguma daquelas chamadas gerar um IOException, a exceção poderia ser capturada pelo chamador da rotina. Ficaria mais ou menos assim:
    public void processIndividualData(Socket socket) {
      socket.read(name);
      process(name);
    
      socket.read(address);
      processAddress(address);
    
      socket.read(telNo);
      // etc, etc
    }
    
    public void teste() {
      Socket socket = ....
      try {
        processIndividualData(socket);
      }
      catch (IOException e) {
        // trata a exceção aqui
      }
    }
    
  • Encapsular IOException em alguma outra exceção. Ficaria assim:
    public void processIndividualData(Socket socket) {
      try {
        socket.read(name);
        process(name);
    
        socket.read(address);
        processAddress(address);
    
        socket.read(telNo);
        // etc, etc
      }
      catch (IOException e) {
        throw new OutraException(e);
      }
    }
    
    public void teste() {
      Socket socket = ....
      try {
        processIndividualData(socket);
      }
      catch (OutraException e) {
        // trata a exceção aqui
      }
    }
    

    Isso pode parecer desnecessario, mas vou explicar a importancia de esconder certas exceções:
    Imagine que você implementou um DAO (Data Access Object) para abstrair a persistencia de objetos Cliente no seu sistema. Você teria uma interface assim:

    public interface ClienteDao {
      Cliente[] listarTodos();
    }
    

    E digamos que você tenha uma implementação desse DAO que usa um banco de dados relacional como mecanismo de persistencia. Para acessar o banco de dados, utiliza JDBC.

    public class ClienteDaoJdbcImpl implements ClienteDao {
      public Cliente[] listarTodos() {
        // Implementação aqui
      }
    }
    

    As chamadas ao JDBC podem gerar exceções do tipo SQLException. Então o seu método ClienteDaoJdbcImpl vai repassar essa exceção para o chamador? Isso significa que o usuário do seu DAO saberá que o DAO é implementado para bancos de dados relacionais. O que aconteceria se você quisesse utilizar uma implementação da interface que usa arquivos Xml para persistir as informações? Não haveria nenhum SQLException mais, mas aí como ficaria o seu código que já conta com diversos tratamentos de SQLException onde o antigo DAO era utilizado? Uma das grandes vantagens do DAO é abstrair o mecanismo de peristencia (mas isso não é assunto para este post), só que esta vantagem foi perdida nesse caso. Para resolver este problema, podemos fazer algo assim:

    public class ClienteDaoJdbcImpl implements ClienteDao {
      public Cliente[] listarTodos() {
        try {
          // Implementação aqui
        }
        catch (SQLException e) {
          throw new DaoException(e);
        }
      }
    }
    

    Assim, não importa o que a implementação do DAO faça, você irá sempre se preocupar apenas em tratar DaoException.

Como vimos, o uso de exceções ajuda a melhorar a clareza do nosso código ao separar o tratamento de erros do fluxo normal do código.

Para finalizar, algumas recomendações:

  • Jamais “engula” exceções. Se você capturou uma exceção e não é capaz de trata-la, repasse-a para o chamador
  • Tenha um conjunto rico de exceções.
  • Exceções de baixo nível não devem ser passadas para camadas de mais alto nível. É o exemplo do ClienteDao acima.
0