Slide 1 - Wiki LES PUC-Rio

Propaganda
Impacto do uso de design by
contract no desenvolvimento de
sistemas: um estudo de caso
Frederico Silva Guimarães
04/11/2005
[email protected]
Agenda
• Motivação e Objetivo
• Design by Contract
• Assertivas e Tratamento de Erros
– Java
– C/C++
• Estudo de Caso
• Conclusões
Frederico Silva Guimarães <[email protected]>
Motivação e Objetivo
• Mostrar a experiência que tivemos com o uso de assertivas
no desenvolvimento de aplicações comerciais
– Por que Java e C++?
• Pois são largamente utilizadas hoje em dia (e são as linguagem que
usamos normalmente)
– Por que essa experiência é importante?
• Mostrar que assertivas têm grande utilidade, mesmo sendo pouco
utilizadas
• Mostrar que o uso de assertivas pode fazer a diferença entre o
sucesso e o fracasso de um sistema
• Mostrar um caso real de uso de assertivas (o assunto é bastante
explorado no meio acadêmico, entretanto no meio comercial o
mesmo não acontece)
Frederico Silva Guimarães <[email protected]>
Design By Contract (DBC)
• Contrato estabelecido entre um construto e seus clientes
– Cliente: garantir condições ou propriedades antes de invocar
métodos do construto
– Construto: depois da execução, garantir que condições ou
propriedades serão verdadeiras
• Contratos são formado por um conjunto de assertivas que
definem o comportamento de um construto e de seus
métodos
• No DBC os contratos são executáveis, escritos na própria
linguagem de programação, ou em alguma meta-linguagem
• Conjunto de assertivas executáveis
Frederico Silva Guimarães <[email protected]>
Design by Contract (DBC)
•
Um contrato tem três elementos essenciais
– Pré-condições
– Pós-condições
– Invariantes
Frederico Silva Guimarães <[email protected]>
Assertivas - Java
• Possui uma estrutura própria para o uso de assertivas
– assert <expressão booleana> [: <mensagem de erro> ]
• assert value != null;
• assert value instanceof List : values.getClass().getName();
– Quando a expressão booleana é falsa, levanta um
AssertionError com a mensagem de erro
– Os asserts normalmente estão desligados, deve-se executar o
programa passando um parâmetro extra (-ea) para ligar as
assertivas
• java –ea –cp <CLASSPATH> <MAIN CLASS> <ARGS>
– Só disponível a partir da J2SE1.4
• Códigos usando J2SE 1.3 ou menor
– Usamos “ifs” e levantamos exceções em caso de falha
• Normalmente IllegalArgumentException e
IllegalStateException
Frederico Silva Guimarães <[email protected]>
Assertivas – C/C++
• Não possui nenhuma estrutura própria para o uso de
assertivas
• Usamos “ifs” e chamamos uma função em caso de falha
– Normalmente essa função loga/mostra o erro e aborta o
programa
Frederico Silva Guimarães <[email protected]>
Tratamento de erros - Java
• Padronizado: todos os erros levantam um Throwable
– Exception
• RuntimeException
– Error
• Estrutura try..catch..finally permite pegar um Throwable
(um qualquer ou um especifico), e tratar o erro
• O Throwable traz informações sobre o erro:
– Possui um stacktrace mostrando a execução (inclui linhas e
métodos) até o momento que o erro ocorreu
– O próprio nome da classe diz, de modo geral, qual foi o erro
• IOException, ArrayIndexOutOfBounds, NullPointerException,
OutOfMemoryError
– Possui uma mensagem sobre o erro
• por exemplo o índice inválido no ArrayIndexOutOfBounds
Frederico Silva Guimarães <[email protected]>
Tratamento de Erros – C/C++
• Os erros fazem:
– O programa “voar”
• Ex: acessar uma variável nula
– ou funcionar de maneira errada
• Ex: acessar um índice inválido de um array, mas ainda assim
acessar um espaço válido de memória
– Em ambos os caso, não se tem nenhuma informação sobre o
que aconteceu ou onde aconteceu
• Executando em modo debug em algumas IDEs é possível ter uma
idéia de onde ocorreu o erro (caso ele não ocorra em uma
biblioteca externa), mas ainda é impossível saber que erro ocorreu
Frederico Silva Guimarães <[email protected]>
Tratamento de Erros – C/C++
• Em C++ existe a estrutura try..catch, que não costuma ser
usada
– Sua utilização pode gerar problemas como vazamentos de
memória
– A maior parte das bibliotecas (incluindo a std) não fazem uso
do throws
• A bibliotecas XercesC faz uso dela
– Erros, como acesso inválido de memória, fazem o programa
“voar” ao invés de levantar uma exceção
– Não há padronização:
• Qualquer coisa, até mesmo um int ou string pode ser levantado
• Métodos não são requeridos a declarar os tipos que podem levantar
como exceção
Frederico Silva Guimarães <[email protected]>
Tratamento de Erros – C/C++
• Em C/C++, as funções/métodos costumam ter código de
retorno para informar se houve algum erro durante a
execução (ao invés de levantar exceções)
Frederico Silva Guimarães <[email protected]>
Exemplo - Java
public static void copyFile( File sourceFile, File targetFile )
throws IOException
{
assert sourceFile != null;
assert sourceFile.exists() : “File doesn’t exists: ”+sourceFile.
getAbsolutePath();
assert sourceFile.isFile() : “File is a directory: ”+sourceFile.
getAbsolutePath();
assert targetFile != null;
...
}
• Caso não utilizássemos assertivas, se algum dos argumentos fosse
inválido, o código levantaria uma exceção (NullPointerException,
FileNotFoundException, etc..)
– Essa exceção nos daria quase as mesmas informações que o
AssertionError
Frederico Silva Guimarães <[email protected]>
Exemplo - C++
int32 UtiltitesIO::copyFile( QFile* sourceFile, QFile* targetFile )
{
if(sourceFile == NULL)
{
return RET_CODE_SOURCE_FILE_IS_NULL; //1
}
else if(targetFile == NULL)
{
return RET_CODE_TARGET_FILE_IS_NULL; //-1
}
else if(!sourceFile->exists())
{
return RET_CODE_SOURCE_FILE_DOES_NOT_EXIST; //3
}
...
return RET_CODE_OK; //0
}
•
Caso não houvesse assertivas, se algum dos argumentos fosse inválido, o programa
“voaria”
Frederico Silva Guimarães <[email protected]>
Estudos de casos
• XmlParser
• Gip-View
Frederico Silva Guimarães <[email protected]>
XmlParser
• Projeto consistiu em traduzir, de Java para C++, um código
que lê e escreve certas classes em xml (cerca de 170
classes)
– Usando bibliotecas Log4cxx e XercesC
• O projeto teve uma equipe de 4 pessoas, e o tempo era
escasso
– Houve uma preocupação em traduzir o código rapidamente,
para depois testá-lo
• O código foi traduzido sem qualquer código extra em relação a
versão Java
• Em especial, não foi colocada nenhuma assertiva ou tratamento de
erro além do que existia no código Java (que eram poucos)
Frederico Silva Guimarães <[email protected]>
XmlParser
• A tradução foi rápida
– Parte foi automatizada por um programa (em Java) que gerava
um esboço do .cpp e do .h a partir dos .java
• Quando começaram os testes, começaram os problemas.
Muitos erros, mas que em Java:
– Não aconteciam
• EX: variáveis não inicializadas
– Em C++, elas recebem um valor aleatório
– Em Java, recebem null, 0 ou false;
– Geravam erro de compilação
• Ex: funções que devem retornar valores, mas não retornam valores
em todos os caminho possíveis
Frederico Silva Guimarães <[email protected]>
XmlParser
double getVal(int code)
{
switch (code)
{
case 0:
return ...;
case 1:
return ...;
case 2:
return ...;
case 3:
return ...;
}
//Cadê o return para code != {0, 1, 2, 3}
}
Frederico Silva Guimarães <[email protected]>
XmlParser
• Quando começaram os testes, começaram os problemas.
Muitos erros mas que em Java:
– Lançam exceções específicas:
• Acessar uma variável nula
– Em C/C++ o programa “voa”
• Typecast errado
– Em C/C++ o programa vai “voar” (ou não!!!!) quando algum método ou
atributo for acessado
• Acesso inválido de memória (índice inválido de um array)
– Em C/C++, o comportamento do programa é indeterminado
Frederico Silva Guimarães <[email protected]>
XmlParser
• Gastou-se uma boa parte do projeto (2 meses), apenas na
descoberta e tratamento dos erros
• Como normalmente o programa “voava” em funções das
bibliotecas externas, era necessário depurar o programa
passo a passo, não só para tentar descobrir o que houve,
mas também para descobrir aonde ocorreu o erro
– E, normalmente, a real causa era um dos erros citados
anteriormente
• Muitos desses erros poderiam ter sido evitados (ou mais
facilmente descobertos) se tivessemos inserido assertivas
pelo menos em alguns pontos chave do código durante a
tradução
Frederico Silva Guimarães <[email protected]>
Gip-View
• Sistema para inspeção de dutos
– Para comparação:
• O sistema possui cerca de 210 classes
– Dessas, 80-90 foram parcialmente reutilizadas de outro projeto que
também foi desenvolvido utilizando assertivas
• O sistema foi desenvolvido por 3 possoas
– 2 delas comuns aos dois projetos
• Foi desenvolvido desde o início com o uso de assertivas
• Em 2 meses, já havia uma versão beta do sistema
• Desses dois messes foram gastos 2-3 semanas em testes e
integração com a ferramenta de inspeção
Frederico Silva Guimarães <[email protected]>
Gip-View
• Em todo o desenvolvimento o programa “voou” apenas 5
vezes (mas caiu várias vezes em assertivas ), apenas 1
vez na mão do cliente (ainda durante a fase de testes)
• Em cada uma dessas vezes que o programa “voou”, o tempo
gasto com a descoberta e o conserto do erro foi
drasticamente maior do que quando alguma assertiva
falhava
• E, normalmente, quando a causa do erro era descoberta,
via-se que ele poderia ter sido evitado (ou encontrado mais
facilmente) com o uso de assertivas
Frederico Silva Guimarães <[email protected]>
Gip-View
• O sistema continua em desenvolvimento
– Novas funcionalidades estão sendo adicionadas
• Ele já se encontra em uso a alguns meses e tem se
mostrado bastante estável
– Já foi inclusive usado em inspeções reais
Frederico Silva Guimarães <[email protected]>
Gip-View – Ligando/Desligando Assertivas
• Maneira mais comum: Usando DEFINES
#ifdef GIP_USE_ASSERTS
if ( value == NULL )
{
ClErrorManager::abortProgram(“mensagem“);
} // if
#endif
• Problemas
– 8 ou 80: Ou todas as assertivas ou nenhuma
• Seria possível usar vários defines para níveis de assertivas e para
classes ou grupos de classes, mas isso dificultaria muito a
manutenção e configuração
– Necessidade de re-compilação do código para ligar/desligar
assertivas
Frederico Silva Guimarães <[email protected]>
Gip-View – Ligando/Desligando Assertivas
• Solução: Usar a configuração do log da aplicação como
configuração das assertivas
• O log é feito através da biblioteca log4cxx que é uma
implementação em C do log4j da Apache
– É configurado em arquivo de configuração (log4j.properties)
– Mensagens logadas podem ser de 5 níveis
• FATAL > ERROR > WARN > INFO > DEBUG
• É possivel desligar todas as mensagens, ou desligar apenas
mensagens abaixo de um determinado nível
Frederico Silva Guimarães <[email protected]>
Gip-View – Ligando/Desligando Assertivas
– Configuração hierarquica dos Loggers
• Cada Logger recebe um nome na criação
– Logger * LOG = Logger::getLogger(<nome>);
• Logger com o nome br.com.gip.Utilities herda configurações de:
– br.com.gip, br.com, br, e do Logger raiz (root)
• Permite facilmente ligar ou desligar um grupo de Loggers ao alterar
o nível permitido para as mensagens
Frederico Silva Guimarães <[email protected]>
Gip-View – Ligando/Desligando Assertivas
• Definimos um nível para cada assertiva
• Se as mensagens daquele nível não estiverem habilitadas
no Logger daquela classe, aquela assertiva não é testada
Frederico Silva Guimarães <[email protected]>
Exemplo
int32 UtiltitesIO::copyFile( QFile* sourceFile, QFile* targetFile )
{
if(LOG->isInfoEnabled())
{
if(sourceFile == NULL)
{
return RET_CODE_SOURCE_FILE_IS_NULL; //1
}
else if(targetFile == NULL)
{
return RET_CODE_TARGET_FILE_IS_NULL; //-1
}
else if(LOG->isDebugEnabled())
{
if(!sourceFile->exists())
{
return RET_CODE_SOURCE_FILE_DOES_NOT_EXIST; //3
}
}
}
...
return COPY_FILE_OK; //0
}
Frederico Silva Guimarães <[email protected]>
Conclusões
• Em Java é possível ter boas informações sobre os erros
ocorridos sem o uso de assertivas (principalmente
assertivas simples), no entanto o uso de assertivas pode
ajudar ainda mais na descoberta da causa e localização dos
erros
• Já em C/C++ o uso de assertivas é uma necessidade, sem
elas fica muito difícil/trabalhoso a descoberta da causa e a
localização de erros
Frederico Silva Guimarães <[email protected]>
Referências
• DBC: Bertrand Meyer. Applying Design by Contract. In
Computer IEEE 25 (10), October 1992, pag 40-51.
• DBC: B. Venners, Contract-Driven Development - A
Conversation with Bertrand Meyer, Part III, Artima Software
site, March 2004.
• QT: http://www.trolltech.com/products/qt/index.html
• XercesC: http://xml.apache.org/xerces-c/
• Log4j: http://logging.apache.org/log4j/docs/
• Log4cxx: http://logging.apache.org/log4cxx/
Frederico Silva Guimarães <[email protected]>
Download