Integração de modelos formais e semiformais: o caso de UML e VDM++ João Pascoal Faria Prof. Auxiliar, FEUP Coimbra, Abril de 2004 Resumo A linguagem de modelação visual UML é utilizada correntemente para efeito de especificação, análise, documentação e geração automática de partes da implementação Esta comunicação aborda o enriquecimento dos modelos visuais em UML (semi-formais) através de: especificação formal de restrições (por invariantes), semântica de operações (por pré e pós-condições) e concorrência, para obter modelos formais completos, rigorosos e verificáveis especificação do corpo algorítmico de operações através de linguagens de acções de alto nível, para obter modelos executáveis e traduzíveis Resumo Em torno de UML têm surgido linguagens que permitem criar modelos formais - caso de OCL (Object Constraint Language) - e modelos executáveis e traduzíveis - caso de XTUML - mas ainda não de forma perfeitamente integrada Em contrapartida, VDM++ é uma linguagem de especificação formal orientada por objectos, que é baseada num standard maduro (VDM-SL), que permite criar modelos formais executáveis e traduzíveis, sendo suportada por ferramentas (VDMTools) que permitem sincronizar com Rational Rose, executar os modelos e gerar Java e C++ Índice Introdução Modelos visuais (semi-formais) Dos modelos visuais aos modelos formais Dos modelos visuais aos modelos executáveis Dos modelos visuais aos modelos traduzíveis Modelos visuais, formais, executáveis e traduzíveis Breve introdução às técnicas de especificação formal Apresentação de um caso de estudo Conclusões Modelos visuais (semi-formais) Consensual e prática cada vez mais corrente: modelação visual (semi-formal) com diagramas UML durante as fases de análise e especificação de requisitos, desenho de alto nível (arquitectura) e desenho detalhado geração de algum código (Java, C#, SQL, XSD, etc.) a partir de UML e vice-versa normalmente esqueletos de código, e não código completamente funcional Modelos visuais são muito úteis para compreender e visualizar um sistema Dos modelos visuais aos modelos formais É possível enriquecer os modelos visuais (semiformais) com especificações formais de restrições (por invariantes) semântica de operações (por pré e pós-condições) aspectos de concorrência Obtém-se um modelo formal que funciona como especificação completa, rigorosa (sem ambiguidades nem inconsistências) e verificável Formal não quer dizer executável Modelos formais são importantes porque a análise detalhada e o rigor necessários à sua construção permitem prevenir e detectar problemas mais cedo, quando a sua correcção é menos dispendiosa Dos modelos visuais aos modelos executáveis É possível enriquecer os modelos visuais com especificações do corpo algorítmico de operações (e actividades) em linguagens de acções de alto nível Obtém-se um modelo executável (com a ajuda de ferramentas) Modelos executáveis são importantes para efeito de teste e validação precoce do sistema (ainda na forma de modelo) Dos modelos visuais aos modelos traduzíveis É de esperar que os modelos executáveis sejam também traduzíveis (com a ajuda de ferramentas) para uma linguagem de implementação-alvo (Java, C++, C#, ...) Geração automática de código completamente funcional e não só esqueletos de classes a partir de modelos de alto nível Particularidades das linguagens, tecnologias e plataformas-alvo são embebidas nos geradores Aumento de produtividade Acabam-se os programadores? Não! Apenas se programa a um nível mais abstracto, e separa-se melhor o domínio do problema das tecnologias de implementação! Modelos visuais, formais, executáveis e traduzíveis Que solução integrada? Em torno de UML têm surgido linguagens que permitem criar modelos formais - caso de OCL (Object Constraint Language) - e modelos executáveis e traduzíveis - caso de XTUML - mas ainda não de forma perfeitamente integrada Em contrapartida, o VDM++ é uma linguagem de especificação formal orientada por objectos que permite criar modelos formais, executáveis e traduzíveis, sendo suportada por ferramentas (VDMTools) que permitem sincronizar com Rational Rose, executar os modelos e gerar código Java e C++ Índice Introdução Breve introdução às técnicas de especificação formal O que são métodos formais? Especificação formal baseada em modelos Especificação formal baseada em propriedades Especificação formal baseada em comportamento Conclusões das técnicas de especificação formal Apresentação de um caso de estudo Conclusões O que são métodos formais? Método Formal = Especificação Formal + Raciocínio Formal (verificação formal, ...) As técnicas são suportadas por Matemática precisa Ferramentas de análise poderosas Constituem um mecanismo rigoroso e efectivo para modelação, síntese e análise de sistemas especificação/ modelação Especificação /Modelo formal síntese análise Implementação Classificação de linguagens de especificação formal Baseadas em Modelos Baseadas em Propriedades Sistema especificado em termos de um modelo de estado, que é construído usando construções matemáticas como conjuntos e sequências, e de operações sobre esse estado VDM, Z, VDM++, Object Z, ... Particularmente adequadas para especificar sistemas de informação Sistema especificado em termos das suas operações e das relações entre essas operações Algébricas – definem operações através de uma colecção de relações de equivalência (axiomas equacionais) - Obj, Larch, ... Axiomáticas – definem operações através de predicados da lógica de primeira ordem - Larch, ... Particularmente adequadas para especificar tipos de dados abstractos Baseadas no Comportamento Sistema especificado em termos de sequências possíveis de estados em vez de tipos de dados - Petri Nets, CSP, ... Particularmente adequadas para especificar sistemas concorrentes e distribuídos Exemplo de especificação formal baseada em modelos (VDM++) class Stack instance variables elems : seq of int := []; operations Stack () == elems := [] ext wr elems post elems = []; Push (i: int) == elems := [i] ^ elems ext wr elems post stack = [i] ^ ~elems; Pop () == elems := tl elems ext wr elems pre elems <> [] post elems = tl ~elems; Top () res: int == return hd elems pre elems <> [] post res = hd elems; end Stack Exemplo de especificação formal baseada em modelos (VDM++) instance variables elems : seq of int := []; Escolhe-se uma representação (modelo) do estado baseada em construções matemáticas como conjuntos e sequências Se fosse necessário, podiam-se especificar invariantes (restrições nos estados válidos). Exemplo: inv len elems <= 1000; Exemplo de especificação formal baseada em modelos (VDM++) Pop () == elems := tl elems Corpo algorítmico (executável!) ext wr elems Variáveis de instância cujo valor pode ser alterado pela operação pre elems <> [] Pré-condição: condição nos argumentos de chamada e estado inicial do objecto a que tem de obedecer qualquer chamada válida. especificam semântica post elems = tl ~elems; tail Valor antigo da variável de instância Pós-condição: relaciona resultado da operação e estado final do objecto com argumentos de chamada e estado inicial Exemplo de especificação formal baseada em propriedades (OBJ) Spec: Stack; Extend Nat by Sorts: Stack; Operations: newstack: Stack push: Stack Nat Stack pop: Stack Stack top: Stack Nat Variables: s: Stack; n: Nat Axioms: pop(newstack) = newstack; top(newstack) = zero; pop(push(s,n)) = s; top(push(s,n)) = n; Exemplo de especificação formal baseada em propriedades (OBJ) Mais abstracta: especifica semântica de operações por axiomas, sem necessidade de escolher uma representação de dados/estado interna (cf. noção de ADT) Mas é mais difícil saber quando é que a especificação está completa! Uma stack é sempre representada por uma expressão de construção push(3, push(2, push(1, newstack))) Axiomas permitem simplificar/avaliar expressões top(pop(push(2, push(1, newstack)))) = = top(push(1, newstack)) =1 Exemplo de especificação formal baseada em comportamento (CSP) envia a mensagem "EmptyStackExpression" pelo canal "top" e depois comporta-se como EmptyStack Stack = EmptyStack processos EmptyStack = top! EmptyStackException EmptyStack pop! EmptyStackException EmptyStack push? x OneElemStack(x); EmptyStack "ou" Mais operacional! pop top push OneElemStack(x) = top! x OneElemStack(x) pop! x Skip push? y OneElemStack(y); OneElemStack(x) recebe um valor (mensagem) para x pelo canal "push" e depois comporta-se como a composição sequencial dos processos OneElemStack(x) e EmptyStack Técnicas de especificação formal: conclusões e reflexões As linguagens de especificação formal baseadas em modelos e orientadas por objectos (como VDM++ ou Object-Z) são as que se integram mais naturalmente com UML Uma vantagem de VDM++ sobre Object-Z é que permite obter mais facilmente modelos executáveis e permite especificar mais naturalmente algoritmos A linguagem OCL baseia-se (e enquadra-se) neste tipo de linguagens, sem ter ainda atingido o mesmo grau de maturidade Índice Objectivos e motivação Breve introdução às técnicas de especificação formal Apresentação de um caso de estudo (agenda corporativa) Caracterização sumária do sistema Lista de requisitos Modelo de casos de uso em UML Modelo de domínio em UML Tradução de UML para VDM++ Definição de tipos de dados em VDM++ Definição de classes em VDM++ Especificação de aspectos de concorrência em VDM++ Teste da especificação Documentação Geração de código Conclusões Caracterização sumária do sistema Agenda "corporativa" Cada recurso (pessoa, espaço ou equipamento) tem uma agenda associada (date book), com compromissos (appointments) e disponibilidades (slots) Um compromisso pode envolver vários recursos e pode ocupar um conjunto de intervalos de tempo Os compromissos marcados por terceiros estão sujeitos às disponibilidade manifestadas O sistema deve ajudar o utilizador a marcar compromissos, mostrando hipóteses de marcação Exemplos de aplicações: marcação de consultas, marcação de reuniões, reserva de equipamentos, etc. Lista de requisitos (1) 1. 2. 3. 4. 5. 6. 7. 8. O sistema deve permitir gerir a marcação de compromissos envolvendo vários recursos numa organização. Os recursos englobam pessoas, espaços e equipamentos Cada recurso tem uma agenda associada. Cada agenda tem um dono, que é uma pessoa da organização. O dono da agenda de uma pessoa é a própria pessoa Uma agenda (datebook) tem elementos de dois tipos: compromissos (appointments) e disponibilidades (slots). Um compromisso é de um certo tipo (reunião, aula, etc.), envolve um ou mais recursos e ocupa um intervalo de tempo ou um conjunto de intervalos de tempo, com resolução ao minuto. Deve ficar registado quem marcou um compromisso (autor do compromisso). Lista de requisitos (2) 9. 10. 11. 12. 13. Um recurso não pode ter dois compromissos ao mesmo tempo. Uma disponibilidade refere-se a um recurso, um tipo de compromisso e um intervalo de tempo ou um conjunto de intervalos de tempo. Uma pessoa só pode marcar um compromisso numa agenda de que não é dona, dentro das disponibilidades definidas pelo dono da agenda. Só o dono da agenda pode marcar compromissos sem verificação de disponibilidades. As agendas podem ser consultadas por todas as pessoas da organização. Os compromissos podem ser públicos ou privados. Um compromisso privado só pode ser consultado pelo autor do compromisso, pelas pessoas envolvidas no compromisso e pelos donos das agendas dos recursos envolvidos. Lista de requisitos (3) 14. 15. O sistema deve ajudar o utilizador a marcar compromissos da seguinte forma: o utilizador indica o tipo de compromisso a marcar (exemplo: reunião), os recursos envolvidos, a duração do compromisso e restrições temporais para a marcação do compromisso; o sistema deve fornecer uma lista de hipóteses de marcação; cada hipótese mostra um intervalo de tempo dentro do qual é possível marcar o compromisso em todos os recursos (porque têm declarada disponibilidade para o tipo de compromisso especificado e não têm marcados compromissos de qualquer tipo). Compete ao administrador do sistema registar os recursos da organização (criando automaticamente as respectivas agendas) e definir os tipos de compromissos possíveis. Modelo de casos de uso em UML (funcionalidades e perfis de utilizadores) Agenda Corporativa RegisterResourcesWithDatebooks SystemAdministr ator CreateApointmentTypes actor generalização DatebookOwner fronteira do sistema CreateSlotsForApointments Can't create apointments in datebooks not owned by the user outside the slots previously defined MakePrivateApointment <<extend>> MakeApointment EmployeeActor SuggestSlots MakePublicApointment Can't see the details of private apointments in which he/she does not participate and was not the author and is not the owner of the datebook caso de uso (use case) QueryDatebook restrição Modelo de domínio em UML (1) (modelação do estado do sistema) classe Faltam os invariantes ... atributo agregação multiplicidade generalização associação role name para facilitar mapeamento para VDM++, associação bidireccional é representada por duas associações unidireccionais tipos de dados e valores por omissão definidos em VDM++ Modelo de domínio em UML (2) (modelação das operações sobre o estado) Agora com as operações mais importantes (assinaturas omitidas por falta de espaço) Principais transacções e consultas Falta a semântica das operações ... Tradução de UML para VDM++ (1) Tradução de UML para VDM++ (2) Definição de tipos de dados e classes em VDM++ Em VDM++, tipos e classes são conceitos diferentes Tipos são usados para representar tipos de valores de atributos (tipos de dados) Classes são usadas para representar o estado do sistema Instâncias de um tipo são valores puros Instâncias de uma classe são objectos Definição de um tipo pode ser acompanhada de definição de valores (constantes) e funções (cf. paradigma funcional) Definição de uma classe compreende a definição de variáveis de instância e operações (cf. paradigma de orientação por objectos) Definição de tipos de dados em VDM++ (1) Tipos de dados a definir neste caso (*): Tipo Descrição StringT cadeia de um ou mais caracteres Date data representada pelo número de dias decorridos desde uma data base, útil para processamento interno UserDate data representada por uma combinação de dia, mês e ano Time instante de tempo, representado pelo número de minutos decorridos desde as 0 horas da data base UserTime instante de tempo, representado por uma combinação de minutos, horas, dia, mês e ano TimeInterval intervalo entre dois instantes de tempo, fechado à esquerda e aberto à direita, representado por 2 valores do tipo Time Duration uma duração em minutos (*) Normalmente estes tipos de dados estariam definidos numa biblioteca Definição de tipos de dados em VDM++ (2) Tipos de dados a definir neste caso (cont.) Tipo Descrição UserTimeInterval intervalo entre dois instantes de tempo, fechado à esquerda e aberto à direita, representado por um par de valores do tipo UserTime TimeRegion região de tempo (conjunto de intervalos de tempo) representada por um conjunto de intervalos de tempo unitários (com 1 min de duração), representados pelos seus inícios (valores do tipo Time) representação abstracta pouco eficiente, mas que permite efectuar operações com regiões de tempo usando directamente os operadores de conjuntos (reunião, intersecção e diferença), simplificando a especificação ResourceType tipo enumerado que admite os valores <EMPLOYEE>, <EQUIPMENT> ou <SPACE> VisibilityType tipo enumerado que admite os valores <PUBLIC> ou <PRIVATE> Exemplo de definição de tipos de dados em VDM++ types public UserDate :: year : nat1 definição de tipo de record month: nat1 day : nat1 invariante inv d == d.month <= 12 and d.year >= Year0 and d.day <= DaysOfMonth(d.year, d.month); constantes values construção de record private Year0 : nat1 = 1900; private UserDate0 : UserDate = mk_UserDate(Year0,1,1); functions private static IsLeapYear(year: nat1) res : bool == year mod 4 = 0 and year mod 100 <> 0 or year mod 400 = 0; private static DaysOfMonth(year, month: nat1) res : nat == (cases month : 1, 3, 5, 7, 8, 10, 12 -> 31, 4, 6, 9, 11 -> 30, 2 -> if IsLeapYear(year) then 29 else 28 pré-condição end) pre month <= 12; (...) Definição de classes em VDM++ Em VDM++, as classes são usadas para modelar o estado do sistema e as operações de consulta e alteração de estado É possível gerar o esqueleto das classes em VDM++ a partir de um diagrama de classes UML (usando as ferramentas Rational Rose e VDMTools), e vice-versa VDM++ permite completar a especificação das classes com invariantes de estado, pré e póscondições das operações e corpo algorítmico das operações Exemplo de definição de classe em VDM++ (variáveis de instância) class Company instance variables só para facilitar testes public name : StringT; public apointmentTypes : set of ApointmentType := {}; -- Must be initialized because of invariant. conjunto de referências para objectos public resources : set of Resource := {}; -- Must be initialized because of invariant. ... public apointments : set of Apointment := {}; -- Because there is a many to many relationship between -- datebooks and apointments, they "belong" to the company -- and are referenced by datebooks (and vice versa) Exemplo de definição de classe em VDM++ (invariantes) ... -- Key constraints -- uniqueness of Resource.rid inv forall r1, r2 in set resources & r1<> r2 => r1.rid <> r2.rid; ... -- Limitation: not checked if r.rid is changed -- Referential integrity constraints -- referential integrity of Apointment.type inv forall a in set apointments & a.type in set apointmentTypes; ... ... -- Limitation: not checked if a.type is changed -- Generic constraints Exemplo de definição de classe em VDM++ (operações de alteração) operations public AddApointmentType(desc: StringT) res : ApointmentType == ( dcl t: ApointmentType := new ApointmentType(desc); apointmentTypes := apointmentTypes union {t}; return t variável de instância que a operação pode alterar ) ext wr apointmentTypes pre desc not in set {at.description | at in set apointmentTypes} post apointmentTypes = apointmentTypes~ union {res} and res.description = desc; ... pré-condição implicada por invariante (pode-se efectuar verificação formal) "~" designa valor antigo da variável Exemplo de definição de classe em VDM++ (operações de consulta) public SuggestSlots(type: ApointmentType, rs: set of Resource, duration: TimeDurationT, within: TimeRegionT) res: set of UserTimeIntervalT == return {TimeIntervalToUserTimeInterval(ti) | ti in set TimeRegionToTimeIntervals( dinter { let r_slots = r.GetDatebook().GetSlotsOfType(type), r_apoints = dunion {a.when | a in set r.GetDatebook().GetApointments()} in (within inter r_slots \ r_apoints) | r in set rs {f(x) | x in set ...} – define conjunto } em compreensão ) dinter {...} – intersecção de elementos & Duration(ti)>=duration de conjunto }; dunion {...} – união de elementos & - tal que de conjunto Especificação de aspectos de concorrência Para além de suportar a definição de objectos activos (donos de threads), a linguagem VDM++ permite especificar declarativamente condições de sincronização sync per nome_operação => condição_de_guarda a execução da operação fica suspensa até a condição de guardar se tornar verdadeira a condição de guarda pode basear-se no estado do objecto e em contadores de execuções de operações (finalizadas, em espera, etc.) Teste da especificação Especificação pode ser testada interactivamente (com interpretador de VDM++) ou com base em casos de teste pré-definidos Pode-se activar a verificação automática de invariantes, pré-condições e pós-condições Um caso de teste tc1 é especificado por: ficheiro tc1.arg – com o comando a executar pelo interpretador ficheiro tc1.arg.exp – com o resultado esperado da execução do comando É produzida informação dos testes que sucederam e dos testes que falharam É produzida informação de cobertura dos testes o pretty printer "pinta" as partes da especificação que foram de facto executadas são geradas tabelas com percentagem de cobertura e número de chamadas de cada operação e função Documentação Cada classe VDM++ é especificada num ficheiro em RTF, usando estilos pré-definidos para assinalar as partes de VDM++ (cf. programação literária) Pretty printer formata as partes de VDM++ e gera versão mais bonita Pode-se juntar tudo num documento final com paste link Geração de código Ferramentas geram código completamente funcional em Java e C++ Incluindo código para verificar pré-condições e póscondições Facilmente legível no caso de Java Código geralmente pouco eficiente tanto em Java como em C++ Principal dificuldade tem a ver com utilizar bibliotecas pré-existentes da linguagem alvo desde a fase de especificação Limitações e melhoramentos a VDM++ Verificar invariantes quando muda o estado de objectos referenciados Definição de transacções permitindo diferir a verificação de invariantes para o fim das transacções questão de conveniência, para suportar restrições interobjecto questão de necessidade, para suportar restrições interobjecto Na pós-condição de uma operação, poder referir mensagens enviadas (chamada de operações ou envio de sinais) para poder especificar o comportamento de operações que sinalizam eventos (exemplo: TextBox.setText) OCL já permite referir mensagens enviadas, ainda que com limitações problema: em geral, como se deve especificar o comportamento de operações envolvendo callbacks? Índice Introdução Breve introdução às técnicas de especificação formal Apresentação de um caso de estudo Conclusões Conclusões A integração de UML com uma linguagem como VDM++ permite enriquecer os modelos visuais criados em UML com: especificação formal de restrições de estado e semântica de operações especificação do corpo algorítmico de operações a um nível de abstracção elevado especificação declarativa de aspectos de concorrência e sincronização Obtém-se rapidamente uma especificação completa (sintaxe + semântica) e rigorosa do sistema a desenvolver a um nível de abstracção elevado Obtém-se rapidamente um modelo que pode ser executado, testado e validado Suporta-se a geração automática de código completamente funcional Facilita-se a verificação de conformidade da implementação com a especificação Ganha-se em qualidade e produtividade (desde que se consiga evitar a duplicação de esforços) Mais informação www.ifad.dk/ - sobre VDM++ e VDMTools www.projtech.com – sobre UML executável www.uml.org – sobre UML, OCL, etc. www.fe.up.pt/~jpf – mais detalhes sobre o caso de estudo apresentado