OCL - Object Constraint Language Extratos do livro: Object Constraint Language de Wermer&Kleppe Modelagem de SI 1 2013/2 Prof. Eber Object Constraint Language (OCL) • “Add-on” da Unified Modeling Language (UML) • Toda expressão OCL usa tipos definidos em um diagrama de classes da UML. – uso da OCL implica na aceitação de alguns padrões da UML. • A constraint is a restriction on one or more values of (partof) an object-oriented model or system. • Versão corrente: OCL 2.0 – formalmente definida em Object Constraint Language Specification (www.omg.org) . Modelos e restrições Classes Atributos Operações Papéis context Flight inv: passengers->size() <= plane.numberOfSeats Modelos e Restrições • • • • • R1-Uma pessoa (person) somente pode obter uma hipoteca (mortgage) contra uma casa (house) de sua propriedade. R2-A data de inicio de uma hipoteca deve ser anterior a sua data final. R3-Um número de seguro social deve ser atribuído a apenas uma pessoa. R4-Uma nova hipoteca deve ser concedida somente a pessoas com rendimento suficiente para paga-la. R5-Uma nova hipoteca deve ser concedida somente se o valor da revenda da casa for suficiente para cobrir a hipoteca. Modelos e Restrições • R1-Uma pessoa (person) somente pode obter uma hipoteca (mortgage) contra uma casa (house) de sua propriedade. • context Mortgage inv: borrower = security.owner Modelos e Restrições • R2-A data de inicio de uma hipoteca deve ser anterior a sua data final. • context Mortgage inv: startDate < endDate Modelos e Restrições • R3-Um número de seguro social deve ser atribuído a apenas uma pessoa. context Person inv: Person.allInstances() -> isUnique (socSecNr) Modelos e Restrições • R4-Uma nova hipoteca deve ser concedida somente a pessoas com rendimento suficiente para paga-la. context Person::getMortgage(sum : Money, security : House) pre: self.mortgages->collect(monthlyPayment)->sum() <= self.salary * 0.30 Modelos e Restrições • R5-Uma nova hipoteca deve ser concedida somente se o valor da revenda da casa for suficiente para cobrir a hipoteca. context Person::getMortgage(sum : Money, security : House) pre: security.value >= security.mortgages->collect(principal)->sum() The Royal&Loyal Company The Royal&Loyal Company Royal&Loyal (1) • Modelo conceitual de uma empresa fictícia chamada Royal and Loyal (R&L). • R&L opera programas de fidelidade para empresas que oferecem bônus a seus clientes. • Os bônus tomam a forma de pontos ou milhas, mas outros tipos de bonificações são possíveis tais como: tarifas reduzidas, melhor carro, melhor serviço de viagem etc. • Qualquer coisa que uma empresa deseje oferecer pode se tornar um serviço oferecido num programa de fidelidade. Royal&Loyal (2) • A classe central do modelo é LoyaltyProgram. Um sistema que administre somente um único programa de fidelidade conterá uma única instancia desta classe. R&L terá muitas. • A empresa que oferece a seus clientes a adesão a um programa de fidelidade é chamada programPartner. Mais de uma empresa pode participar de um mesmo programa. • • • Objetos da classe Customer representam as pessoas que entraram em um loyaltyProgram. customers que participam do programa podem se beneficiar dos serviços providos por qualquer uma das empresas participantes. Todo customer de um programPartner pode entrar num programa de fidelidade preenchendo um formulário e obtendo um customerCard. Cada customerCard é atribuído a somente uma pessoa. Royal&Loyal (3) • Uso do customercard não é verificado de forma que ele pode ser usado por toda uma família. • A maioria dos programas de fidelidade permite que clientes acumulem points. Cada programPartner decide quando e quantos points de bônus são ganhos (earned) em cada compra (transaction). • Earned points podem ser usados para “comprar'' serviços (services) específicos de um dos programPartners. • Para contabilizar os pontos ganhos por cada customer, toda associação a um programa deve ser associada a uma loyaltyAccount. The Royal&Loyal Company Valores iniciais e Regras de derivação • context LoyaltyAccount::points init: 0 • context CustomerCard::valid init: true • context CustomerCard::printedName derive: owner.title.concat(‘ ‘).concat(owner.name) • Estas construções NÃO fazem parte do OCL-USE. • Como vc. faria esta especificação no USE? The Royal&Loyal Company Consultas (queries) • operação getServices da classe LoyaltyProgram: retorna todos os serviços oferecidos por todos os partners no programa • context LoyaltyProgram • operations • getServices(): Set(Service) = • partners->collect(deliveredServices)->asSet() • OBS.: • Partners.deliveredServices é um bag • Operador ->asSet: converte bag para set • “parodiando” em SQL: • getServices() select distinct (deliveredServices) from partners Consulta parametrizada operação getServices da classe LoyaltyProgram: retorna os serviços oferecidos por um partner no programa context LoyaltyProgram operations getServices(pp: ProgramPartner) : Set(Service)= if partners->includes(pp) then pp.deliveredServices else Set{} endif. Atributos derivados • atributo derivado turnover de LoyaltyAccount: soma dos atributos amount das transações da conta. • USE não tem esta construção • Deve ser especificado no USE como uma função; context LoyaltyAccount def: turnover : Real = transactions.amount->sum() USE: context LoyaltyAccount operations turnover():Real=transactions->collect(amount)->sum() Invariantes • Invariante é uma restrição que deve ser avaliada como verdadeira durante toda a vida do objeto. • Invariantes representam regras aplicáveis a objetos do mundo real. • Algumas regras de negócio podem ser expressas como invariantes. Exemplos de invariantes (A) Sobre valores de atributos context Customer inv ofAge: age >= 18 (B) Sobre tipos não-primitivos context CustomerCard inv checkDates: validFrom.isBefore(goodThru) Notas (i) validFrom e goodThru são do tipo Date (fora da OCL) (ii) isBefore é um método do tipo Date (A) Sobre classes associadas context CustomerCard inv ofAge: owner.age >= 18 Invariantes com referência a classes associativas (1) context LoyaltyProgram inv knownServiceLevel:levels->includesAll(Membership.currentLevel) Notas (i)Usa a classe Membership para estabelecer que o nível de serviço de cada associação deve ser um serviço conhecido do programa de fidelidade para a qual o invariante se aplica. Invariantes com referência a classes associativas (1) o nível de serviço de cada membership deve ser conhecido do respectivo programa de fidelidade context LoyaltyProgram inv knownServiceLevel:levels->includesAll(Membership.currentLevel) Invariantes com referência a classes associativas (2) o cartão da membership é um dos cartões do respectivo customer context Membership inv correctCard: participants->collect(cards)->includes(self.card) Operações em Coleções (collections) • associações cujas cardinalidades sejam maiores do que 1 relacionam um objeto a uma coleção de objetos. • sempre que a navegação resultar numa coleção de objetos, pode-se usar uma operação de coleção. Para indicar o uso de uma operação de coleção usa-se uma seta (->) entre o nome do papel e a operação. Tipos de Coleção • Set (não há repetição, nem ordem de elementos). • Bag (pode haver repetição, mas não existe ordem dos elementos). • Sequence (pode haver repetição, elementos em sequência – bag ordenado). • OrderedSet (não há repetição, elementos em sequência – set ordenado). Sets, Bags, OrderedSets e Sequences context ProgramPartner inv nrOfParticipants: numberOfCustomers = programs->collect(participants)->size() context ProgramPartner inv nrOfParticipants: numberOfCustomers = Programs->collect(participants)->asSet()->size() Notas: (i)-Bag: elementos podem aparecer mais de uma vez (ii)-Ordered set (conjunto ordenado) (iii)-Sequence= ordered bag (bag ordenado) Sets, Bags, OrderedSets e Sequences context ProgramPartner inv nrOfParticipants: numberOfCustomers = programs.participants->size() context ProgramPartner inv nrOfParticipants: numberOfCustomers = programs.participants->asSet()->size() Operações em Coleções Operation Description count( object ) The number of occurrences of the object in the collection excludes( object ) True if the object is not an element of the collection excludesAll( collection) True if all elements of the parameter collection are not present in the current collection includes( object ) True if the object is an element of the collection includesAll( collection) True if all elements of the parameter collection are present in the current collection isEmpty() True if the collection contains no elements notEmpty() True if the collection contains one or more elements size() The number of elements in the collection sum() The addition of all elements in the collection. The elements must be of a type supporting addition. Iteradores Operações que são aplicáveis a todos os membros de uma collection Iteradores mais utilizados: Select (<bool expr>): <collection> Forall (<bool expr>): true Collect (<expr>):<collection select Para todo customer: número de seus cartões válidos deve ser igual ao número de programas dos quais participa context Customer inv sizesAgree: programs->size() /* number of programs for customer*/ = cards->select( valid = true )->size() /* number of valid cards*/ Notas (i)-select recebe uma expressão OCL como parâmetro. (ii)-retorna um subconjunto da coleção ao qual é aplicado contendo somente elementos para os quais a expressão é verdadeira. The Royal&Loyal Company forall se nenhum dos serviços oferecidos por um programa credita ou debita pontos em LoyaltyAccount, então as respectivas instâncias são inúteis e não devem estar presentes context LoyaltyProgram inv noAccounts: Partners->collect(deliveredServices)-> forAll( pointsEarned = 0 and pointsBurned = 0 ) implies Membership.account->isEmpty() Notas: (i)-forAll recebe uma expressão OCL como parâmetro. (ii)-retorna verdadeiro se a expressão for avaliada como verdadeira para todos elementos da coleção collect No contexto LoyaltyProgram partners->collect( numberOfCustomers) ) ) pode ser escrito de forma abreviada como: partners.numberOfCustomers Notas: (i)-forma uma nova collection contendo os valores da expressão aplicada a cada um dos objetos da coleção. (ii)-Normalmente usado de forma abreviada pelo operador dot(.) Pré e pós condições de operações context LoyaltyAccount::isEmpty(): Boolean pre : -- none post: result = (points = 0) context Service::upgradePointsEarned(amount: Integer) Pre: -- amount>0 post: pointsEarned = pointsEarned@pre() + amount Notas: (i)-Pré e pós condições: especificam o efeito de uma operação sem especificar o algoritmo. (ii)-Aplicável também a: métodos de classe, atividades e estados. (iii)-Operador @pre<attr>, representa o valor do atributo <attr> antes da operação.Só pode ser usado em nas cláusulas post. Referências [1] Warmer, J., Kleppe, A.: The Object Constraint Language. Precise Modeling with UML. Addison-Wesley, 1999 [2] Warmer, J., Kleppe, A.: The Object Constraint Language Second Edition. Getting Your Models Ready For MDA. Addison-Wesley, 2003 [3] OMG UML specification, www.omg.org Exercícios 1-Invariantes: cliente pode participar no máximo em N programas 2-O número máximo de pontos cedidos por um partner em um programa de fidelidade deve ser menor que 10.000 2-Consultas 2.1-quantidade de pontos ganhos por um cliente em todos os programas onde ele participa 2.2-idem ao 2.1 para quantidade de pontos consumidos 2.3-total de vendas de um partner em todos os programas onde participa 2.4-cliente com o maior número de pontos ganhos em um dado programa de fidelidade 2.5- número total de cartões emitidos por um programa de fidelidade