Outra maneira de melhorar a especificação de um diagrama de classes é a inclusão de invariantes. Invariantes são expressões booleanas que determinam uma condição que precisa ser verdadeira em todos os estados consistentes do sistema para todas as instâncias de seu contexto. No exemplo a seguir, criamos uma restrição que afirma que o número de amoras em uma cesta deve ser sempre menor ou igual à sua capacidade. Nomear invariantes é opcional, mas pode ser extremamente útil, por exemplo, na validação automática de modelos, onde é importante que se possa ter uma forma simples de referenciar as invariantes violadas.
context Cesta
inv maximoDeAmorasNaCesta: nºDeAmoras <= capacidade 2.3.2.5. Précondições e póscondições
Précondições e póscondições em operações são maneiras de definir precisamente a interface de um sistema, pois elas não especificam como a operação é implementada. Uma pré
condição é uma expressão booleana que precisa ser verdadeira no momento em que a operação inicia sua execução. Já uma póscondição é uma expressão booleana que precisa ser verdadeira no momento em que a operação termina sua execução. O seguinte exemplo mostra a utilização de pré/póscondições na operação Colheita::adicionarCestas(c : Cesta), onde a précondição especifica que não interessam cestas com capacidade menor que 20 e a póscondição especifica que a nova cesta deve estar relacionada com a colheita:
context Colheita::adicionarCestas(c : Cesta) pre: c.capacidade >= 20
post: self.utiliza = self.utiliza@pre>including(c)
O operador @pre é utilizado concatenandose o mesmo ao nome do atributo/relação, para
obter o valor que o atributo/relação possuía no momento de início da operação.
2.3.2.6. Mensagens em póscondições
Um aspecto muito útil em póscondições é o fato de que elas podem indicar, pelo envio de mensagens, que uma certa operação foi chamada. Por exemplo, quando o número total de amoras colhidas for 300, podese chamar a função alertar() em todas as pessoas relacionadas á colheita, para avisar que já foram colhidas amoras suficientes.
context Pessoa::colherAmora(a : Amora)
post: if totalDeAmoras >= 300 then self.colheita.emprega>forAll(p : Pessoa | p^alertar()) else true endif
2.3.2.7. Definição de classes derivadas
Uma visão é um conceito bem conhecido em sistemas relacionais de banco de dados. O conceito de classes derivadas, em modelagem UML/OCL, é um conceito similar ao conceito de visão. Uma classe derivada é uma classe cujas propriedades podem ser completamente derivadas de classes existentes.
2.3.2.8. Multiplicidade dinâmica
Associações em diagramas de classe podem ser especificações imprecisas do sistema. Esse é o caso quando a multiplicidade de uma associação não é fixada, mas deveria ser determinada de acordo com outro valor no sistema. Isso é chamado de multiplicidade dinâmica. Um exemplo ocorre no modelo da colheita de amoras onde uma associação entre as classes Colheita e Amora, indicando que um certo número de amoras são colhidas, tem multiplicidade muitos (1..*) no lado da classe Amora. Isso significa que o número cardinal máximo de amoras é ilimitado, porém o número cardinal de amoras é limitado pelo número cardinal resultante da soma das capacidades das cestas relacionadas com a colheita. Essa restrição não pode ser expressa no diagrama. Uma forma de especificar essa cardinalidade é adicionar a seguinte restrição OCL ao modelo:
context Colheita
inv maximoDeAmoras: obtém>size() <= utiliza>collect(capacidade)>sum() 2.3.2.9 Multiplicidade opcional
Uma multiplicidade opcional de uma associação em um diagrama de classes é, geralmente,
uma especificação parcial do que é realmente intencionado. Nesses casos, uma associação opcional
não é realmente livre e sua presença depende do estado de outros objetos. Em geral, quando há uma
multiplicidade opcional em um diagrama de classes, devese utilizar invariantes OCL para descrever
precisamente em que circunstâncias a associação opcional pode ser vazia ou não.
2.3.2.10. Restrições ou
Um diagrama de classes pode conter uma restrição ou entre duas associações, como mostrado no exemplo da figura 20. Essa restrição significa que somente uma das associações potenciais pode ser instanciada em um instante de tempo para qualquer objeto. Essa restrição é representada no diagrama como uma seta tracejada conectando duas ou mais associações (que possuam ao menos uma classe em comum) com a string {or} rotulando a linha. A multiplicidade das associações precisa ser opcional, senão elas não poderão ser vazias.
No exemplo da colheita de amoras, supõese que uma pessoa possa participar da colheita sendo ou proprietário de um número qualquer de colheitas ou funcionário de de um número qualquer de colheitas. Também é suposto que uma colheita possa ter qualquer número de proprietários (exemplos de colheitas sem proprietários poderiam ser os kibutzim de Israel, há algumas gerações) e que uma colheita possa ter qualquer número de funcionários. A escolha de interpretação parece óbvia, mas como todas as associação têm multiplicidade opcional, uma transformação automática MDA poderia interpretar o modelo de outra forma, intersubstituindo os operadores lógicos grifados nas frases anteriores.
Especificar a restrição ou visual como uma restrição OCL resolve a ambigüidade. Quando desejarse a primeira interpretação, devese anexar a seguinte invariante ao diagrama:
context Pessoa
inv: self.proprietário>isEmpty() or self.trabalha>isEmpty() A invariante para a segunda interpretação é:
context Colheita
inv: self.pertence>isEmpty() or self.emprega>isEmpty()
2.3.3. Estilos de modelagem
Além de servir para complementar a linguagem UML na escrita de restrições e consultas, a linguagem OCL também pode ser utilizada para expressar informações iguais de maneiras diferentes. Há vários estilos de modelagem e nessa seção serão explicados alguns deles.
2.3.3.1. Definição de atributos ou operações
Atributos e operações podem ser definidos em um diagrama de classes adicionandose os
mesmos a um tipo. Mas eles também podem ser definidos por uma expressão OCL, não sendo
necessário mostrálos no diagrama. No exemplo a seguir, criouse o atributo capacidadeRestante e a
operação pararColheita:
context Cesta
def: capacidadeRestante : Integer = self.capacidade – self.nºDeAmoras context Colheita
def: pararColheita() : Boolean = if totalDeAmoras >= 300 then true else false endif
A expressão após o sinal de igual, em uma definição de atributo, é uma regra de derivação e indica como o valor do atributo será calculado. Em uma definição de operação, a expressão que segue o sinal de igual especifica o resultado da operação.
2.3.3.2. A restrição de subconjunto
Um diagrama de classe pode conter restrições de subconjunto, como mostrado na figura 20.
Elas são representadas por setas tracejadas que vão do subconjunto ao conjunto e que possuem a string {subset} rotulando as mesmas. Esse tipo de restrição informa que o conjunto de links de uma associação é um subconjunto do conjunto de links de outra associação.
Mostrar todas restrições de subconjunto no diagrama pode tornálo difícil de ler. Nesse caso, podese especificar essas restrições utilizando expressões OCL. As duas restrições de subconjunto mostradas no modelo de colheita poderiam ser expressas em OCL da seguinte maneira:
context Pessoa
inv: self.participa>includesAll(self.proprietário) inv: self.participa>includesAll(self.trabalha) context Colheita
inv: self.equipe>includesAll(self.pertence) inv: self.equipe>includesAll(self.emprega) 2.3.3.3. Herança X Invariantes
Novamente será referido o modelo de colheita de amoras, mais precisamente a associação entre Amora e Cesta. Essa associação é especializada nas associações entre os subtipos AmoraVerde e CestaTipo1 e AmoraMadura e CestaTipo2, que impõem que instâncias de AmoraVerde só podem ser armazenadas em instâncias de CestaTipo1 e instâncias de AmoraMadura só podem ser armazenadas em instâncias de CestaTipo2. Essas associações entre os subtipos de Amora e Cesta podem ser vistas como restrições à associação entre Amora e Cesta. Essas restrições podem ser escritas em OCL para diminuir a complexidade do diagrama. No modelo, poderiase retirar as sub
associações do diagrama e adicionar as seguintes invariantes:
context AmoraVerde
inv: self.contida>forAll(c : Cesta | c.oclIsKindOf(CestaTipo1)) context AmoraMadura
inv: self.contida>forAll(c : Cesta | c.oclIsKindOf(CestaTipo2))
Assim criase um diagrama de mais fácil leitura, mas com a mesma expressividade. Porém, se a única razão para a existência das subclasses de Cesta for diferenciar os tipos de cesta para os diferentes tipos de amoras, poderiase excluir as subclasses de Cesta e criar um DataType TipoCesta que as especificasse, colocandose um atributo tipo:TipoCesta na classe Cesta e adicionandose as seguintes invariantes:
context AmoraVerde
inv: self.contida>forAll(c : Cesta | c.tipo = TipoCesta::tipo1)) context AmoraMadura
inv: self.contida>forAll(c : Cesta | c.tipo = TipoCesta::tipo2))
Também poderiase excluir as subclasses de Amora e criar um DataType TipoAmora com amoraVerde e amoraMadura, e criar um atributo tipo:TipoAmora na classe Amora. As novas invariantes seriam:
context Amora
inv: self.tipo = TipoAmora::amoraVerde implies self.contida>forAll(c : Cesta | c.tipo = TipoCesta::tipo1))
context Amora
inv: self.tipo = TipoAmora::amoraMadura implies self.contida>forAll(c : Cesta | c.tipo = TipoCesta::tipo2))
Nesse exemplo, podese transformar as subclasses em DataTypes por que as mesmas não
possuíam atributos ou comportamentos específicos. Então, nem sempre modelos poderão ser
transformados dessa forma, e também não será sempre o caso que essa técnica seja a melhor
alternativa. A arte de balancear diagramas com anotações OCL dependerá da finalidade do modelo.
2.4. MDA, UML e OCL
A essência do MDA é que os modelos são a base para o desenvolvimento de software.
Portanto, os modelos devem ser bons, sólidos, consistentes e coerentes.
As linguagens de modelagem são fundamentais em MDA. Como tanto PIM's, como PSM's são transformados automaticamente, eles devem ser escritos em uma linguagem padrão, bem definida, que possa ser processada automaticamente por ferramentas.
Para que se possa aplicar o processo MDA, os modelos devem estar no nível 4 de maturidade (seção 2.1.1.5.). Pois nesse nível eles são precisos o suficiente para serem transformados de PIM's para PSM's.
2.4.1. Modelagem com UML/OCL
Muitos dos problemas em modelagem são provocados pelas limitações dos diagramas. Um diagrama é incapaz de expressar todos os enunciados que devem ser partes de uma especificação.
Por exemplo, o caso de multiplicidade dinâmica mostrado na seção 2.3.2.8.
Expressões escritas em uma linguagem precisa e com base matemática, como OCL, oferecem muitos benefícios em relação aos diagramas: não são ambíguas, não podendo ser interpretadas de várias formas; podem ser checadas automaticamente; permitem a geração de modelos derivados, utilizandose transformações MDA; permitem geração de código. Para que essas tarefas possam ser automatizadas, o modelo deve conter toda a informação necessária. Uma ferramenta computacional não consegue interpretar regras em linguagem natural. Regras escritas em OCL incluem toda a informação necessária para que possam ser utilizadas por ferramentas automáticas MDA. Desse modo, a implementação é muito mais rápida e eficiente do que se fosse feita manualmente.
No entanto, modelos escritos em linguagens que utilizam expressões para representação geralmente não são facilmente compreendidos. Basta recordar que um código fonte pode ser considerado um modelo último de um sistema. A combinação de UML com OCL oferece a possibilidade da utilização da linguagem UML em modelos diagramáticos, contornandose parte das limitações da linguagem UML pela utilização de expressões OCL.
Como explicitado no exemplo anterior, modelos em diagramas UML, sem expressões OCL,
podem estar severamente mal especificados. E sem diagramas UML, as expressões OCL farão
referência a elementos de modelagem não existentes, pois não há como especificar classes e
associações em OCL. Assim, somente combinandose diagramas e restrições é possível especificar
completamente um modelo.
2.4.2. Metamodelagem com UML/OCL
A construção de metamodelos é a definição de uma linguagem. As mesmas vantagens da utilização de UML/OCL na construção de modelos são conseguidas na escrita de metamodelos.
No framework MDA novas linguagens precisam ser definidas, mas o mais importante é obter a especificação de linguagens existentes. Muitas linguagens de programação ainda não foram formalmente definidas. Normalmente a única parte formal em suas especificações é sua gramática, escrita em BNF.
Novas linguagens também podem ser definidas como perfis UML, desobrigandose a criação de um metamodelo completamente novo para a linguagem. Em vez disso, o metamodelo da UML é utilizado juntamente com regras extras e um mapeamento dos conceitos da linguagem com estereótipos UML.
2.4.3. Transformações definidas com UML/OCL
Uma definição de transformação descreve como um modelo escrito em uma linguagem pode ser transformado em um modelo escrito em outra linguagem. Essa descrição é genérica quando é independente dos modelos atuais. Ela tem que utilizar conceitos definidos em ambas as linguagens, ou seja, ela é construída utilizandose as metaclasses dos metamodelos de ambas as linguagens.
Uma definição de transformação relaciona metaclasses da linguagem fonte com metaclasses da linguagem destino.
Como transformações devem ser executadas por ferramentas automáticas, definições de
transformações devem ser escritas de um forma precisa e não ambígua. Nesse contexto, uma
expressão OCL pode precisamente indicar quais elementos do metamodelo fonte são utilizados em
uma certa transformação em determinados elementos do metamodelo destino.
2.5. Metamodelagem utilizandose o Eclipse
Na realização dessa monografia utilizouse a IDE Eclipse, principalmente devido ao fato dela apresentar, na forma de plugins, uma série de funcionalidades interessantes ao projeto em questão, como ferramentas que auxiliam nas tarefas de metamodelagem, na realização de transformações MDA, na criação de editores gráficos e na validação de modelos.
O Eclipse é uma comunidade cujos projetos são direcionados à construção, em código aberto, de uma plataforma extensível de desenvolvimento para a construção e gerência de software em todo o ciclo de vida do último.
No Eclipse, diversas funcionalidades são providas por frameworks independentes, implementados em plugins. Por exemplo, no presente projeto, foram utilizados os plugins:
●
EMF (Eclipse Modeling Framework) [http://www.eclipse.org/modeling/emf];
●
GMF (Graphical Modeling Framework) [http://www.eclipse.org/modeling/gmf];
●
MDT (Model Development Tools) [http://www.eclipse.org/modeling/mdt].
Para a criação dos metamodelos revisados da UML 2.0, utilizouse o framework EMF. Esse plugin provê meios para a criação e validação de modelos escritos em ECore com restrições em OCL, via integração com o framework MDT, que provê uma implementação da linguagem OCL para a avaliação de consultas e restrições em (meta)modelos. ECore é o nome dado ao metamodelo, implementado em Java, do núcleo da EMF. Ele é praticamente igual à linguagem EMOF (Essential MOF) [9], com apenas algumas pequenas diferenças, de nomenclatura em sua maioria. Assim, para evitar confusões, decidiuse utilizar nomes distintos para esses metamodelos. A linguagem EMOF é um subset da linguagem MOF 2.0 (Meta Object Facility) [http://www.omg.org/mof], assunto da subseção 2.5.1.
Na escrita das restrições no ambiente EMF, foi utilizado um subset da linguagem OCL 2.0, que é definido unicamente baseado no núcleo comum entre UML 2.0 e MOF 2.0 (OCLMOF subset), pois a especificação completa da linguagem OCL só pode ser utilizada com a linguagem UML.
O framework GMF é utilizado para a criação de editores gráficos baseados nos frameworks EMF e GEF (Graphical Editing Framework) [http://www.eclipse.org/gef]. Esse último possibilita a construção de editores gráficos a partir de metamodelos, mas devido à sua difícil utilização, foi criado o GMF, que serve como mediador entre os frameworks EMF e GEF no processo de criação de um editor gráfico.
A seguir, a subseção 2.5.1 apresentará uma breve exposição da linguagem MOF 2.0.
2.5.1. MOF
O MOF, criado pelo OMG, provê um framework de gerenciamento de metadados e um conjunto de serviços para habilitar o desenvolvimento e interoperabilidade de sistemas dirigidos a modelos e metadados.
A criação do MOF foi incentivada por um cenário onde a Internet, ao conectar fontes de dados e aplicações em todo o planeta, dirigia a expectativa de um fácil e transparente intercâmbio de dados entre aplicações. Mas, metadados incompatíveis entre diferentes sistemas limitavam o intercâmbio de dados. Muitas aplicações, ao utilizar modelos proprietários de metadados, impediam o intercâmbio de dados entre as fronteiras de aplicações, por causa das diferenças entre esses modelos.
A partir da fundação de modelagem estabelecida pela UML, MOF introduziu os conceitos de metamodelos formais e modelos de metadados independentes de plataforma (PIM's).
2.5.2. EMF
O framework EMF permite a escrita de metamodelos e, a partir de algumas transformações, a criação de um editor. É importante salientar que todas as transformações de modelos feitas no presente projeto não são apresentadas explicitamente como modelos de transformação, e sim como funcionalidades providas pelo Eclipse, ou seja, para realizar uma transformação entre modelos, o usuário deve acionar um comando no Eclipse, que mostrará uma caixa de diálogo, e a partir da interação com o usuário, o Eclipse coletará as informações adicionais mínimas necessárias à transformação. Nesse framework, um metamodelo ECore pode ser transformado em um modelo Gen, que contém as informações necessárias à criação de um editor. Dependendo do metamodelo ECore utilizado, poderá haver a necessidade de alteração manual do modelo Gen, para que o mesmo seja capaz de ser transformado em um editor. Por exemplo, no projeto em questão, o metamodelo ECore possui algumas operações e metaatributos especificados em OCL, e em função disso, houve a necessidade de alterar manualmente o modelo Gen gerado a partir do metamodelo ECore, para que o processo de transformação do modelo Gen no editor seja capaz de interpretar as expressões OCL.
2.5.3. GMF
O framework GMF está acima do EMF e permite a transformação de um editor em árvore,
gerado no framework EMF, em um editor gráfico. O Processo de transformação é semelhante ao
descrito anteriormente. A partir de um metamodelo ECore, são gerados dois modelos, um modelo
GMFGraph, que contém informações relativas à visualização de elementos do metamodelo, e um
modelo GMFTool, que contém informações relativas à barra de ferramentas do editor gráfico. No
projeto atual, foi necessário alterar o modelo GMFGraph gerado a partir do metamodelo ECore,
para que as decorações propostas nos perfis de modelagem exibidos no capítulo 3 possam ser implementadas. A partir dos modelos ECore, GMFGraph e GMFTool foi gerado um modelo GMFMap, que tem a função de criar mapeamentos entre os três modelos anteriores. Não há como adicionar as restrições OCL, mostradas nos perfis, no processo de transformação que cria o modelo GMFMap. Então, houve a necessidade de adicionar essas restrições posteriormente. A partir do modelo GMFMap, pôde ser criado o modelo GMFGen, que tem a função de gerar o editor gráfico.
Novamente, houve a necessidade de alterar o modelo GMFGen após a sua criação, para habilitar a
funcionalidade de validação de modelos no editor gráfico. Feito isso, o modelo GMFGen pôde ser
transformado no editor e classes Java que implementam as decorações foram adicionadas ao código
fonte do editor gerado.
3. Das Limitações da Modelagem Conceitual Utilizandose UML 2.0
Em [Guizzardi, 2005], é exposto um framework para avaliação de linguagens de modelagem.
Esse framework propõe que seja feito um mapeamento entre o metamodelo da linguagem a ser avaliada e o metamodelo de uma ontologia fundacional escolhida.
Ontologias são modelos conceituais consensuais. Ontologias fundacionais são meta
ontologias independentes de domínio e que provêem conceitos que serão utilizados na escrita de outras ontologias.
Uma linguagem será considerada adequada quando todos os seus conceitos possuírem uma única contraparte na ontologia fundacional. Se existirem conceitos na ontologia que não forem representados em algum elemento da linguagem, será o caso de incompletude ontológica no nível da linguagem em questão. Se existirem conceitos na linguagem avaliada que não são representados na ontologia, eles deverão ser descartados. Se existirem diferentes conceitos ontológicos representados em um mesmo elemento na linguagem, será o caso de sobrecarga de construtor e a linguagem será considerada ambígua. E, finalmente, se existirem conceitos ontológicos representados em mais de um elemento na linguagem, será o caso de excesso de construtores na linguagem.
O presente capítulo mostra os resultados obtidos em [Guizzardi, 2005] pela aplicação do framework supracitado na avaliação da linguagem UML 2.0, utilizandose como ontologia fundacional a ontologia UFO (Unified Foundational Ontology) proposta nos caps. 4 ao 7 de [Guizzardi, 2005]. O objetivo dessa avaliação e reconstrução da linguagem UML 2.0 foi obter uma linguagem ontologicamente bem fundada para a construção de ontologias de domínio e modelagem conceitual.
A UFO, representada parcialmente nas figuras abaixo, é uma teoria de tipos. Na avaliação da linguagem UML 2.0, foi utilizado somente o fragmento da ontologia UFO relativo à endurantes, ou seja, objetos. Assim como em [Guizzardi, 2005], serão apresentadas as análises dos três fragmentos do metamodelo da UML separadamente, nas seções a seguir.
3.1. Classes e Generalização
A figura 16 representa o fragmento do metamodelo da UFO referente à classes e
generalização. Nesse fragmento, universais (Universals) são padrões de características
A figura 16 representa o fragmento do metamodelo da UFO referente à classes e
generalização. Nesse fragmento, universais (Universals) são padrões de características
No documento
Uma Ferramenta baseada em Modelos para Modelagem Conceitual ontologicamente bem fundada
(páginas 39-162)