Solutions

197 downloads 3021 Views 94KB Size Report
els that go beyond the expressiveness of UML class diagrams. Then, you ... is the following domain model, which is one possible solution to exercise 3 of that.
Home Assignment 4 – OCL This home assignment is about writing formal specifications using the Object Constraint Language. You will exercise formulating conditions/invariants on domain models that go beyond the expressiveness of UML class diagrams. Then, you will use OCL for writing contracts for methods, i.e., to specify behaviour. The main purpose of doing that is to get a feeling for textual formal specification languages like OCL, JML or Alloy, which can be used, amongst others, to provide better and more precise documentation of software systems. Comments for the Corrector This week, we have added comments about how to correct only to some of the questions. Generally, when correcting you should not care too much about purely syntactic issues. It is more important that the constraints are logically correct, e.g., that the right collection operations are used (in the right way). Give comments when you subtract points, because the person that wrote the solution will not have the opportunity to ask you for comments personally.

Exercise 1

(4p)

This question is about strengthening the given domain model by formulating further properties in the Object Constraint Language.

Book

Order - sum:double - fee:double

* - orderedBooks

*

- price:double - idNumber:int

a)

(1p)

Give an OCL invariant that specifies that the sum attribute cannot be negative. Solution context Order inv: sum >= 0 b)

(1p) 1

Give an OCL invariant that specifies that the sum attribute will be zero if no books are ordered. Solution context Order inv: orderedBooks->isEmpty() implies sum = 0 (1p)

c)

Give an OCL invariant that specifies that the sum attribute really describes the price of the books ordered. Solution context Order inv: sum = orderedBooks->collect(price)->sum() using short-hand notation: context Order inv: sum = orderedBooks.price->sum() (1p)

d)

Give an OCL invariant that specifies that different instances of Book have different idNumbers. Solution A number of different solutions here: context Book inv: Book.allInstances()->forAll(b1, b2 | b1 b2 implies b1.idNumber b2.idNumber) context Book inv: Book.allInstances()->forAll(b1, b2 | b1.idNumber = b2.idNumber implies b1 = b2) context Book inv: Book.allInstances()->forAll(b | b self implies 2

b.idNumber self.idNumber) context Book inv: Book.allInstances()->isUnique(idNumber) You can make use of the following OCL operations: OclType: allInstances() OclAny: object(object2) String: = ... Integer: ... Real: +, >=, * ... Boolean: b1 implies b2, b1 and b2 (b1,b2:Boolean) Collection, Set, Bag, Sequence: collection-> sum() collection-> size() collection->collect(element:Type | ) collection-> select (element:Type | ) collection-> forAll(element:Type | ) collection-> exists(element:Type | ) collection->isUnique(element:Type | ) collection-> iterate(element:Typ1; result:Type2 = | )

Exercise 2

(12p)

This question continues exercise 3 and 4 from the first home assignment. The context is the following domain model, which is one possible solution to exercise 3 of that assignment:

3

One possible solution:

PaymentRemarks date info ...

Address SteetName StreetNumber city country ...

remarks *

Bank name ...

1..*

Account balance interest ...

address 1

1

* associate * * Customer 0..1 status customer name 0..1 ... warrantor 1 * account

loan * *

Income income amount date ...

Loan amount interest payment ...

own * *

0..1 security

*

0..1

House taxValue ...

loanFor 0..1

With help of the multiplicity one can constrain the domain model. But there are other conditions, business rules, which cannot be described by only a domain model using multiplicities. Such rules can be written in a precise manner using OCL. a) Invariants (1)

(2p)

Write the following constraint as an OCL invariant of concept Loan: If a loan has a warrantor, then the warrantor is an associate of the customer who has the loan. Solution context Loan inv: warrantor->notEmpty() implies customer.associate->includes(warrantor) b) Invariants (2)

(2p)

4

Write the following constraint as an OCL invariant of concept Loan: If a Loan has a House as security, then the Customer having the Loan must own that House. Solution context Loan inv: security->notEmpty() implies security.customer = customer or context Loan inv: security->notEmpty() implies customer.own->includes(self.security) (2p)

c) Invariants (3)

Write the following constraint as an OCL invariant of concept Loan: If a Loan has a House as security, then that House must have a taxValue (assessed value) of at least 20% of the amount of the Loan. Solution context Loan inv: security->notEmpty() implies (security.taxValue / amount) * 100 >= 20 d) Pre-/Postconditions: Customer::getLoan()

(2p)

In order to simplify the following question, we assume that the number of instances of concept Income that are related with a customer shows how many times during the last 12 months the person got a salary. If there are, for example, 9 instances of Income related to a customer, then the customer got a salary in 9 out of 12 months. We start with refining the domain model into a class diagram. Therefore, an operation getLoan():Boolean is added to class Customer.1 This operation is supposed to return whether a customer can be given a loan (true) or not (false). As a business decision, a customer can be given a loan if and only if the following conditions hold (unless houses as securities or warrantors are involved, in this case the whole situation gets more complicated): 1

Whether this is good design is questionable, but this is not the point of this exercise . . .

5

• The customer got a salary in each of the last 12 months. • In each of the last 12 months, the salary was more than 14 000 kr. Give an OCL specification of Customer::getLoan() that expresses these conditions. Solution context Customer::getLoan():Boolean post: result = (income->size() = 12 and income->forAll(amount>14000)) e) Pre-/Postconditions: Customer::addLoan()

(4p)

The following question is a difficult one . . . Good luck! As the next step, we create an operation addLoan(amount:Real, interest:Real, payment:Real):Loan[0..1] for really adding a loan. The operation will again be added to class Customer. The result of the operation is either the loan instance that was created (if adding the loan was successful) or the empty set (otherwise). The preconditions of the operation have to express that the arguments have reasonable values. Decide yourself about what is reasonable, but also give a short justification for the conditions that you came up with. The effect of the operation is supposed to be the following: • If the customer is directly allowed to get a loan (i.e., the conditions from d) are satisfied): – A Loan object has been created, its attributes have the values that were given as arguments of addLoan(), and the object has been related with the customer. • Otherwise: If the customer has an associate customer that is allowed to get a loan (again according to the conditions from d)): – A Loan object has been created like in the first case, but in addition the associate has been added as a warrantor. • Otherwise: No loan is added. Give an OCL specification of Customer::addLoan(...) that expresses all these conditions. Hint: Note that you can use the query Customer::getLoan() in OCL expressions. Solution We consider the following values of the arguments as reasonable: 6

• amount is positive. • interest must not be negative, because the bank does not want to pay its customers for taking loans. The value 0 is possible and might occur for very special customers . . . • The monthly/yearly payment (whatever) must not be negative, and must not be greater than the amount. context Customer::addLoan(p_amount:Real, p_interest:Real, p_payment:Real):Loan[0..1] pre: p_amount > 0 and p_interest >= 0 and p_payment >= 0 and p_payment forAll(amount = p_amount interest = p_interest payment = p_payment security->isEmpty() loanFor->isEmpty()) and if getLoan() then result->notEmpty() and result.oclIsNew() loan = loan@pre->including(result) result.warrantor->isEmpty() else if associate->exists(getLoan()) then result->notEmpty() and result.oclIsNew() loan = loan@pre->including(result) associate->exists (war | war.getLoan() and result.warrantor = war) else loan = loan@pre and result->isEmpty() endif endif

and and and and

and and and and

Comments for the Corrector • Give up to 1p for reasonable pre-conditions that are correctly formalised (which must not exactly be the pre-conditions given as solution here). • Give up to 1p for correctly distinguishing the different possible cases (warrantor, no warrantor, no loan at all).

7

• Give up to 1p for correctly creating a Loan object, for assigning the attributes/associations amount, interest, payment, security, loanFor, and for adding it to self.loan. • Give up to 1p for correctly setting up the warrantor field of the newly created Loan object.

(total 16p)

8