class RegularFlight {
flightNumber;
}
Class SpecificFlight {
* -- 1 RegularFlight;
flightNumber = {getRegularFlight().getFullNumber()}
}
class X {
Integer i;
[! (i == 10)]
}
At any given point in time, the system is in one state.
It will remain in this state until an event occurs that causes it to change state.
A state is represented by a rounded rectangle containing the name of the state.
Special states:
class GarageDoor{
status {
Open {
buttonOrObstacle -> Closing;
}
Closing {
buttonOrObstacle -> Opening;
reachBottom -> Closed;
}
Closed {
buttonOrObstacle -> Opening;
}
Opening {
buttonOrObstacle -> HalfOpen;
reachTop -> Open;
}
HalfOpen {
buttonOrObstacle -> Opening;
}
}
}
These start in a separate thread as they are instantiated.
Declared with the keyword
active
Solve the threading problem:
Umple syntax: queued
before the state machine declaration
We will look at examples in the manual
pooled
stereotype:
Matches any event that is not listed
Can be in any state, e.g.
unspecified -> error;
class AutomatedTellerMachine{
queued sm {
idle {
cardInserted -> active; maintain -> maintenance;
unspecified -> error1;
}
maintenance { isMaintained -> idle; }
active {
entry /{addLog("Card is read");}
exit /{addLog("Card is ejected");}
validating {
validated -> selecting;
unspecified -> error2;
}
selecting {select -> processing; }
processing {
selectAnotherTransiction -> selecting;
finish -> printing;
}
printing {receiptPrinted -> idle;}
cancel -> idle;
}
error1 {entry / {printError1();} ->idle;}
error2 {entry / {printError2();} ->validating;}
}
}
class phone {
state {
onHook {
startDialing -> dialling;
incomingCall -> ringing;
}
ringing {
pickUp -> communicating;
otherPartyHangUp -> onHook;
}
communicating {
hangUp -> onHook;
otherPartyHangUp -> waitForHook;
putOnHold -> onHold;
}
onHold {
hangUp -> onHook;
otherPartyHangUp -> waitForHook;
takeOffHold -> communicating;
}
dialing {
completeNumber ->
waitingForConnection;
hangUp -> onHook;
}
waitingForConnection {
otherPartyPickUp -> communicating;
hangUp -> onHook;
timeOut -> onHook;
}
waitForHook {
hangUp -> onHook;
}
}
}
Product variants have long been important for
Product lines/families, whose members target different:
Feature-oriented development (separation of concerns)
Mixins allow including attributes, associations, state
machines, groups of states, stereotypes, etc
Example:
class X { a; }
class X { b; }
It doesn’t matter whether the mixins are
Separate groups of classes for
model (classes, attributes, associations)
Methods operating on the model
Allows a clearer view of the core model
Another possibility
Advantages:
Disadvantage
Or
Or
Create a pointcut that specifies (advises) where to inject code at multiple points elsewhere in a system
But: There is potentially acute sensitivity to change
Drawback : Delocalization even stronger than for mixins
It is common to limit a pointcuts a single class
class Person {
name;
before setName {
if (aName != null && aName.length() > 20) { return false;
}
}
}
We may want to inject similar elements into unrelated classes
Elements can be
Methods
Attributes
Associations
States or state machines
.. Anything
trait Identifiable {
firstName;
lastName;
address;
phoneNumber;
fullName = {firstName + " " + lastName}
Boolean isLongName() {return lastName.length() > 1;}
}
class Person {
isA Identifiable;
}
trait T1{
abstract void method1(); /* required method */
abstract void method2();
void method4(){/*implementation – provided method*/ }
}
trait T2{
isA T1;
void method3();
void method1(){/*implementation*/ }
void method2(){/*implementation*/ }
}
class C1{
void method3(){/*implementation*/ }
}
class C2{ isA C1; isA T2;
void method2(){/*implementation*/ }
}
trait T1< TP isA I1 > {
abstract TP method2(TP data);
String method3(TP data){ /*implementation*/ }
}
interface I1{
void method1();
}
class C1{ isA I1;
isA T1<TP = C1>;
void method1(){/*implementation*/}
C1 method2(C1 data){ /*implementation*/ }
}
class C2{
isA I1;
isA T1< TP = C2 >;
void method1(){/*implementation*/}
C2 method2(C2 data){ /*implementation*/ }
}
trait T1 <TP>{
String method1();
String method2(){
#TP# instance = new #TP#();
return method1() +":"+instance.process();
}
}
class C1{
String process(){/*implementation*/}
}
class C2{
isA T1< TP = C1 >;
String method1(){/*implementation*/ }
}
trait T1{
abstract method1();
void method2(){/*implementation*/}
void method3(){/*implementation*/}
void method4(){/*implementation*/}
void method5(){/*implementation*/}
}
class C1{
isA T1<-method2() , -method3()>;
void method1() {/*implementation related to C1*/}
}
class C2{
isA T1<+method5()>;
void method1() {
/*implementation related to C2*/}
}
trait T1{
abstract method1();
void method2(){/*implementation*/}
void method3(){/*implementation*/}
void method4(){/*implementation*/}
void method5(Integer data){/* implementation*/}
}
class C1{
isA T1< method2() as function2 >;
void method1() {/*implementation related to C1*/}
}
class C2{
isA T1< method3() as private function3 >;
void method1() {/*implementation related to C2*/}
}
class C3{
isA T1< +method5(Integer) as function5 >;
void method1() {/*implementation related to C3*/}
}
class Dashboard{
void update (Sensor sensor){ /*implementation*/ }
}
class Sensor{
isA Subject< Observer = Dashboard >;
}
trait Subject <Observer>{
0..1 -> * Observer;
void notifyObservers() { /*implementation*/ }
}
trait T1 {
sm1{
s0 {e1-> s1;}
s1 {e0-> s0;}
}
}
trait T2 {
isA T1;
sm2{
s0 {e1-> s1;}
s1 {e0-> s0;}
}
}
class C1 {
isA T2;
}
trait T1{
Boolean m1(String input);
Boolean m2();
sm1{
s1{
e1(String data) -> /{ m1(data); } s2; }
s2{
e2 -> /{ m2(); } s1; }
}
}
class C1{
isA T1;
sm2{
s1{ m1(String str) -> s2;}
s2{ m2 -> s1;}
}
}
trait T1{
sm {
s1{
r1{ e1-> r11; }
r11{}
||
r2{ e2-> r21; }
r21{}
}
}
}
class C1{
isA T1<sm.s1.r1 as region1,sm.s1.r2 as region2>;
}
trait T1 {
sm1{
s0 { e1(Integer index)-> s1;}
s1 {e0-> s0;}
}
sm2{
t0 {e1(Integer index)-> t1;}
t1 {e0-> t0;}
}
}
class C1 {
isA T1<sm1.e1(Integer) as event1, *.e0() as event0>;
}
A feature or variant needs to inject or alter code in many
places
There is also a need to
mixset Name {
// Anything valid in Umple at top level
}
mixset Name class Classname {
}
use Name;
virtual file
that is composed of aanother mixset
mixset Name1 {class X { a; } }
mixset Name1 {class X { b; } }
use Name1;
class X { a; b;}
class X {
mixset Name2 {a;}
b;
}
mixset Name2 class X {a;}
class X {b;}
require [Mixset1 or Mixset2];
Allowed operators
and, or, xor
not
n..m of {…}
Parentheses allowed
opt X (means 0..1 of {X})
ant -f build.xml sandbox
Umplification: ‘amplication’ + converting into Umple.
Produces a program with behavior identical to the original one but written in Umple.
Eliminates the distinction between code and model. Proceeds incrementally until the desired level of abstraction is achieved.
Weka
Args4J- Modernization
# LOC in files containing modeling constructs (X.ump) is 312.
# LOC in files with algorithmic/logic code (X code.ump) is 1668.
The developer must then translate 1518 lines of code rather than 2223 lines of code.