public enum State {
oredered, available, borrowed, reserved
}Behavioral Aspects
Gerson Sunyé gerson.sunye@univ-nantes.fr
State Machine Implementation

Represent the behavior of a class (or component) in terms of its reactions to changes form its environment.
Composed of States, connected by Transitions, triggered by Events



Enumerations/Integers.
State design pattern.
Use an enumeration to represent states.
Verify and change states inside each concerned method.
public enum State {
oredered, available, borrowed, reserved
}cannot be executed if state is ≠ Borrowed
sets new state to either Available or Reserved
public void returnBook() {
if (state != State.borrowed)
throw new IllegalStateException();
// Manage return
// Set State to Available or Reserved
if(state != State.available &&
state != State.reserved)
throw new IllegalStateException();
}Easy to implement, fast
Hard to modify: new states or new transitions impact the whole code
Create a class for each State.
Delegate the behavior to the these classes
Throw an exception when an operation is called in the wrong state.
public class BookWithStates implements Book {
private BookState state = new Ordered();
@Override
public void reserve(Reader aReader) {
state.reserve(aReader);
}
@Override
public void deliver() {
state.deliver();
}
@Override
public void borrow() {
state.borrow();
}
@Override
public void returnBook() {
state.returnBook();
}
}public class DefaultBookState implements BookState {
@Override
public void reserve(Reader aReader) {
throw new IllegalStateException();
}
@Override
public void deliver() {
throw new IllegalStateException();
}
@Override
public void borrow() {
throw new IllegalStateException();
}
@Override
public void returnBook() {
throw new IllegalStateException();
}
}class Ordered extends DefaultBookState {
@Override
public void deliver() {
// Manager deliver
// Change state to Available or Reserved
}
@Override
public void reserve(Reader aReader) {
// Manage reservation
// State remains Ordered
}
}Harder to implement: class proliferation (1 class per state).
Easier to modify: new states or new transitions have a limited impact.
Operation Implementation
Operation pre- and post-conditions
Activity diagrams
Based on «procedural abstractions».
Technique to document/specify what the operation does, without indicating how it works.
Pre-condition: a statement or set of statements that outlines a condition that should be true, or conditions that should be true, when the operation is called.
The operation is not guaranteed to perform as it should unless the pre-conditions have been met.
Post-condition: is a statement or statements describing the condition that will be true when the operation has completed its task.
If the operation is correct and the pre-condition(s) met, then the post-condition is guaranteed to be true.
In UML pre- and post-conditions are precisely specified in OCL
OCL states for "The Object Constraint Language"
It is a pure expression language:
An OCL expression is guaranteed to be without side effect.
It cannot change anything in the model.

Operation Library::return(bookId:Integer)
post-conditions:
the book becomes available.
the member no longer has the book.
context Library::return(bookId:Integer)
pre:
bookId > 0
post:
let borrowing = self.borrowings->
select(each| each.book.id = bookId)->last() in
-- the book becomes available
borrowing.book.oclInState(Available) and
-- the member no longer has the book
borrowing.member.books->excludes(borrowing.book)
Operation Library::borrow(bookId:Integer, memberId:Integer):Boolean
post-conditions:
the book is unavailable
the member has the book in his borrowed books.
the library keeps a trace of the borrowing.
context Library::borrow(bookId:Integer, memberId:Integer):Boolean
post:
let book = self.books[bookId] in
let member = self.members[memberId] in
-- the book is unavailable
book.oclInState(Unavailable) and
-- the member has the book in his borrowed books.
member.borrowed->includes(book) and
-- the library keeps a trace of the borrowing.
self.borrowings->select(each | each.member = member and
each.book = book)->exists(each | each.oclIsNew())Raise exceptions when pre-conditions are not respected
Deduce implementation from post-conditions
context Library::return(bookId:Integer)
pre:
bookId > 0
post:
let borrowing = self.borrowings->
select(each| each.book.id = bookId)->last() in
-- the book becomes available
borrowing.book.oclInState(Available) and
-- the member no longer has the book
borrowing.member.books->excludes(borrowing.book)public void returnBook(Integer bookId) {
Validate.isTrue(bookId > 0);
Borrowing borrowing = borrowings.stream()
.filter(each -> each.book().id() == bookId)
.findFirst()
.get();
borrowing.book().returnBook();
borrowing.member().borrowed().remove(borrowing.book());
}Precise and easy to specify (if you know OCL), for simple operations.
Not adapted for more complex operations: concurrency, distribution, etc.
Activity diagrams specify the behavior of a UML Operation.
They are composed of a set of Activities and (data and/or control) Flows.







public boolean borrow(Integer memberId, Integer bookId) {
Member member;
if (!books.containsKey(bookId)) {return false;}
Book book = books.get(bookId);
if (!book.isAvailable()) { return false;}
if (!members.containsKey(memberId)) {
member = this.registerMember(memberId);
} else {
member = members.get(memberId);
if (member.isMaxQuotaExceed()) {
return false;
}
}
Borrow borrow = new Borrow(member, book);
this.borrows.add(borrow);
return true;
}Useful to specify complex algorithms.
Can be very precise.
Hard to use.
Use Sequence Diagrams to implement tests

Examples of cooperation between objects.
Illustrate the dynamic sequence of a process through messages exchanged between objects.
Time is represented as an explicit (vertical) dimension.




class LibraryTest {
private Library lib = new Library();
@BeforeEach
void setUp() {
lib.createMember(42, "Philippe");
lib.createBook(1, "Baudolino");
}
@Test
void reserve() {
Calendar date = Calendar.getInstance();
date.set(2017, 2, 1);
lib.reserve("Baudolino", 42, date);
Assertions.assertTrue(lib.reservations()
.stream()
.anyMatch(each -> each.member().id() == 42 &&
each.book().title().equals("Baudolino") &&
each.date().equals(date))
);
}
}Sequence diagrams represent execution traces.
They illustrate the collaboration among objects.
They are not adapted to specify algorithms (conditionals, loops, etc.).
UML (and its different diagrams) can be useful to specify design details and clarify choices.
Each diagram has a different proposal, but they all represent the same model.
Designers and developers must have a clear and common idea about how to map designs to code.