290 likes | 449 Views
Impacto do uso de design by contract no desenvolvimento de sistemas: um estudo de caso. Frederico Silva Guimarães 04/11/2005. kid@teccomm.les.inf.puc-rio.br. Agenda. Motivação e Objetivo Design by Contract Assertivas e Tratamento de Erros Java C/C++ Estudo de Caso Conclusões.
E N D
Impacto do uso de design by contract no desenvolvimento de sistemas: um estudo de caso Frederico Silva Guimarães 04/11/2005 kid@teccomm.les.inf.puc-rio.br
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
Design by Contract (DBC) • Um contrato tem três elementos essenciais • Pré-condições • Pós-condições • Invariantes Frederico Silva Guimarães <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
Estudos de casos • XmlParser • Gip-View Frederico Silva Guimarães <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>
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 <kid@teccomm.les.inf.puc-rio.br>