Commit c0799323 authored by Gerson Sunyé's avatar Gerson Sunyé
Browse files

More migration

parent 80bd979b
Pipeline #14341 passed with stages
in 2 minutes and 24 seconds
......@@ -6,11 +6,12 @@ bundle exec asciidoctor-revealjs $SOURCE/*.adoc -D $TARGET
rsync -r src/images/ $TARGET/images
if test ! -d $TARGET/reveal.js; then
CURDIR=$pwd
cd $TARGET
git clone -b 3.8.0 --depth 1 https://github.com/hakimel/reveal.js.git
rm -rf reveal.js/.git
cd reveal.js
wget https://gitlab.univ-nantes.fr/bousse-e/stereopticon/raw/master/install-or-update-stereopticon.sh
bash install-or-update-stereopticon.sh
cd ../../..
cd $CURDIR
fi
:revealjs_center: false
:revealjs_display: flex
:revealjs_transition: none
:revealjs_slideNumber: c/t
:revealjs_theme: stereopticon
:revealjs_width: 1920
:revealjs_height: 1080
:revealjs_history: true
:revealjs_margin: 0
:source-highlighter: highlightjs
:imagesdir: images
:includedir: includes
:sectids!:
= Mapping UML Designs to Code
Behavioral Aspects
[.impact]
== Step 8
State Machine Implementation
[%notitle]
== Open Parenthesis
image::open-parenthesis.png[align=center]
== State Machines
* 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_
== States and Transitions
image::uml-states-transitions.png[align=center]
== Transitions receive and send events, have guards and actions
image::uml-events.png[align=center]
[%notitle]
== Open Parenthesis
image::close-parenthesis.png[align=center]
== Implementation Approaches
. Enumerations/Integers.
. _State_ design pattern.
== State Machine example
.Book States
image::sm-book.png[align=center]
== Enumeration Approach
.Rationale:
* Use an enumeration to represent states.
* Verify and change states inside each concerned method.
== Enumeration Approach (Cont.)
[.columns]
--
[.col-8]
image::sm-book.png[align=center]
[.col-4]
image::enum-state.png[align=center]
--
== Enumeration implementation
[.columns]
--
[.col-4]
image::enum-state.png[align=center]
[.col-8]
[source,java]
----
public enum State {
oredered, available, borrowed, reserved
}
----
--
== Operation Implementation
image::sm-book.png[align=center,width=600px]
[source,java]
----
public void returnBook() {
if (state != State.borrowed) throw new IllegalStateException();
// TODO
if(state != State.available && state != State.reserved)
throw new IllegalStateException();
}
----
== Enumeration Approach Wrap-up
* Easy to implement, fast
* Hard to modify: new states or new transitions impact the whole code
== State Pattern Approach
.Apply the _State_ design pattern:
* 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.
== Create a class for each state
[.columns]
--
[.col-6]
image::sm-book.png[align=center]
[.col-6]
image::state-pattern.png[align=center]
--
== Delegate the behavior to the sate class
[.stretch]
[source,java]
----
public class BookWithStates implements Book {
private BookState state = new Ordered();
@Override
public boolean reserve(Reader aReader) {
return state.reserve(aReader);
}
@Override
public void deliver() {
state.deliver();
}
@Override
public void borrow() {
state.borrow();
}
@Override
public void returnBook() {
state.returnBook();
}
}
----
== Default behavior
[.stretch]
[source,java]
----
public class DefaultBookState implements BookState {
@Override
public boolean 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();
}
}
----
== The _Ordered_ State
[.stretch]
[source,java]
----
class Ordered extends DefaultBookState {
@Override
public void deliver() {
// TODO
}
@Override
public boolean reserve(Reader aReader) {
// TODO
return false;
}
}
----
== State pattern Approach Wrap-up
* Harder to implement: class proliferation (1 class per state).
* Easier to modify: new states or new transitions have a limited impact.
[.impact]
== Step 9
Operation Implementation
== UML Operation Specification
.Alternatives:
* Operation pre- and post-conditions
* Activity diagrams
== Operation pre- and post-conditions
* 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.
== Operation pre- and post-conditions (Cont.)
* *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.
== Pre- and post-conditions in UML
* In UML pre- and post-conditions are precisely specified in https://sunye.github.io/ocl/#/[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.
== Example: library operations
[.columns]
--
[.col-4]
image::library-interface.png[align=center]
[.col-8]
* Operation `Library::return(bookId:Integer)`
* *post-conditions*:
** the book becomes available.
** the member no longer has the book.
--
== In OCL
[source,ocl]
----
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)
----
== In OCL (Cont.)
[.columns]
--
[.col-4]
image::library-interface.png[align=center]
[.col-8]
* Operation `Library::borrow(bookId:Integer, memeberId: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.
--
== In OCL (Cont.)
[source,ocl]
----
context Library::borrow(bookId:Integer, memeberId: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())
----
== Implementing pre- and post-conditions
.Rationale:
* Raise exceptions when pre-conditions are not respected
* Deduce implementation from post-conditions
== Book return operation
[.columns]
--
[.col-6]
[source,ocl]
----
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)
----
[.col-6]
[source,java]
----
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());
}
----
--
== Pre- and post-conditions Wrap-up
* Precise and easy to specify (if you know OCL), for simple operations.
* Not adapted for more complex operations: concurrency, distribution, etc.
== Activities diagram
* Activity diagrams specify the behavior of a UML Operation.
* They are composed of a set of *Activities* and (data and/or control) *Flows*.
[%notitle]
== Open Parenthesis
image::open-parenthesis.png[align=center]
== UML Activitiy Diagram
image::uml-activities.png[align=center]
—-
== Parameters, pre- and post-conditions
image::uml-activities-parameter.png[align=center]
== Data and control flows
image::uml-activities-flow.png[align=center]
[%notitle]
== Close Parenthesis
image::close-parenthesis.png[align=center]
== Book borrow
image::borrow-book.png[align=center,height=800px]
== Borrow implementation
[.columns]
--
[.col-4]
image::borrow-book.png[align=center]
[.col-8]
[source,java]
----
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;
}
----
--
== Activities diagram Wrap-up
* Useful to specify complex algorithms.
* Can be very precise.
* Hard to use.
== Last Step
Use Sequence Diagrams to implement tests
[%notitle]
== Open Parenthesis
image::open-parenthesis.png[align=center]
== Sequence diagrams
* 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.
== Sequence diagrams Example
image::uml-sequence.png[align=center,height=800px]
[%notitle]
== Close Parenthesis
image::close-parenthesis.png[align=center]
== Book reservation
image::sd-reservation.png[align=center]
== Map Sequence diagrams to test cases
[.columns]
--
[.col-4]
image::sd-reservation.png[align=center]
[.col-8]
[source,java]
----
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 Wrap-up
* Sequence diagrams represent execution traces.
* They illustrate the collaboration among objects.
* They are not adapted to specify algorithms (conditionals, loops, etc.).
== Conclusion
* 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.
This diff is collapsed.
......@@ -30,8 +30,9 @@ Icons made by http://www.freepik.com[Freepik] from http://www.flaticon.com[Flati
== Main Topics
* link:construction.html[Software Construction]
* link:mapping.html[Mapping Designs to Code]
* Software Evolution
* link:mapping.html[Mapping Designs to Code - Part I]
* link:behavior.html[Mapping Designs to Code - Part II]
* link:evolution.html[Software Evolution]
* Refactoring
* Unit Testing
* Test Driven Development
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment