This example is adapted from (a portion of) the Online Bookstore Domain Case Study given in Appendix B of the book Executable UML: A Foundation for Model Driven Architecture by Stephen J. Mellor and Marc J. Balcer (Addison-Wesley, 2002).
Graphical Model for Ordering
This example will focus only on the Ordering package for the Online Bookstore. Figure B -103 shows a class diagram for this package.
Figure B 103 Ordering Subsystem Class Diagram
As indicated in Figure B -103, each of the classes shown is an active class. This means that each one has a classifier behavior triggered by a number of signal events. Only the class Order from the Ordering package will be further detailed in this example. Figure B -104 shows the state machine that is the classifier behavior for class Order.
Figure B 104 State Machine Classifier Behavior for Class Order
The triggers on the transitions in this state machine are all for signal events. Of the five different signals shown, only CheckOut and SubmitCharge carry data. For simplicity, each of these signals has a single attribute whose type is a data type that contains the appropriate data for the signal.
As discussed in Annex A.1, the trigger notation “CheckOut(checkOutData)” is used here to indicate that the effect behavior for the transition has a single in parameter corresponding to the single attribute of the CheckOut signal. As shown in Figure B -104 the effect behavior for the transition assigns the parameter to a correspondingly named attribute of class Order, which can then be accessed by the entry behavior of the target state. A similar strategy is used for the transitions triggered by the SubmitCharge signal. (Note that a tool could provide support for automatically generating effect behaviors with such attribute assignments and then suppressing the behavior from the diagram, thus effectively providing the “assignment specification” semantics intended in the UML Specification, Subclause 15.3.14.)
Figure B -105 shows a complete model for class Order, including the (private) attributes checkOutData and chargeSubmissionData. This model also includes receptions (indicated by the «signal» keyword) that declare the ability of Order to receive each of the signals used in the triggers in the state machine shown in Figure B -104. Note that these receptions have the same names as their respective signals.
Figure B 105 Active Class Order
Each of the states shown in Figure B -104 has an entry behavior that is executed whenever the state is entered. The bodies of these entry behaviors are specified below using Alf notation. The names shown in the entry notation are the names of the behaviors (see also Annex A.1 on this notation).
Alf Representation of Entry Behaviors
This section includes an Alf representation for each of the entry behaviors identified in Figure B -104. Each behavior is defined as an Alf unit for an activity that is attached to the appropriate state as its entry behavior.
NOTE. As discussed in Subclause 9.1, a modeling tool could also define each of the entry behaviors as opaque behaviors, with the Alf text for the behavior as the body of the opaque behavior. In this case, the Alf text would only include the statement sequences from the activity definitions given below, not the full unit definition, which would not require the extended conformance level notation for units.
Activity EstablishCustomer
private import TIM;
/** // See Note 1
Entry action for State 1. Establishing Customer and Verifying Payment
*/
activity EstablishCustomer()
{
R2.createLink ( // See Note 2
'selections are purchased in' => this,
'is a purchase of selections in' => this.checkOutData.cart
);
// Create a Customer if one does not already exist
// with the given email address
matchingCustomers = Customer -> select c // See Note 3
(c.email == this.checkOutData.customerEmail);
if (matchingCustomers->isEmpty()) {
customer = new Customer(); // See Note 4
customer.email = this.checkOutData.customerEmail;
} else {
customer = matchingCustomers[1]; // See Note 5
}
// Use the name, address, etc. to update Customer
// whether new or existing
customer.name = this.checkOutData.customerName;
customer.shippingAddress = this.checkOutData.shippingAddress;
customer.phone = this.checkOutData.customerPhone;
// Link the order to the customer
R3.createLink (
places => this,
'is placed by' => customer
);
// Set the date order placed to today
this.dateOrderPlaced = TIM::current_date;
// Create a credit card charge and submit it
// to the credit card company
this.SubmitCharge( // See Note 6
new ChargeSubmissionData ( // See Note 7
accountNumber => this.checkOutData.accountNumber,
billingAddress => this.checkOutData.billingAddress,
cardExpirationDate => this.checkOutData.cardExpirationDate,
cardholderName => this.checkOutData.cardholderName
)
);
}
Notes
-
Text (possibly across multiple lines) bracketed by “/**” and “*/” denotes a documentation comment that is attached to the model element in which it appears. On the other hand, text that begins with “//” is a lexical comment that is not mapped into any model element.
-
Links are instances of associations. The notation “R1.createLink” indicates the creation of a new link of the association R1, with the following tuple giving the association end data. This maps to a create link action.
-
The class name “Customer” here denotes the current extent of that class. In this context, it is a shorthand for the notation “Customer.allInstances()”. The select expression then selects the customers with e-mail addresses matching the one given in the received event.
-
There is no constructor operation defined for class Customer, so the notation “new Customer()” is a “constructorless” instance creation expression the simply creates a new instance of Customer. Further, since Customer is an active class, the active behavior of the new instance is also automatically started.
-
Alf uses an array-like notation for indexing ordered collections. In this case, the notation “matchingCustomers[1]” denotes getting the first element of the list of matching customers. Note that lists are indexed starting at 1.
-
Alf uses the same invocation notation for sending a signal as for calling an operation. Sending a signal is indicated by the selected feature (“SubmitCharge” in this case) being the name of a reception of the given target object (“this”).
-
The SubmitCharge signal has a single attribute of type ChargeSubmissionData. Since ChargeSubmissionData is a data type, an instance expression for it has as its arguments values for each of the attributes of the newly created data value. Alf allows for a named-parameter notation, used here, in which the names “accountNumber”, “billingAddress”, etc. are interpreted as the names of arguments for constructing the ChargeSubmissionData value.
Activity ProcessCharge
/**
Entry behavior for State 2. Submitting Charge
*/
activity ProcessCharge()
{
// Create a Credit Card Charge and submit it
// to the credit card company
creditCardCharge = new CreditCardCharge();
creditCardCharge.MakeCharge(
new ChargeData (
accountNumber => this.chargeSubmissionData.accountNumber,
billingAddress => this.chargeSubmissionData.billingAddress,
cardExpirationDate => this.chargeSubmissionData.cardExpirationDate,
chargeAmount => this.totalValue,
order => this
)
);
}
Activity DeclineCharge
private import EE_OnlineCustomer;
/**
Entry behavior for State 3. Payment Not Approved
*/
activity DeclineCharge()
{
// Notify the customer that the charge was rejected
customer = this.'is placed by';
EE_OnlineCustomer.ChargeDeclined(customerEmail => customer.email);
}
Activity PackAndShip
private import EE_OnlineCustomer;
private import Shipping::Shipment;
/**
Entry behavior for State 4. Being Packed and Shipped
*/
activity PackAndShip()
{
// Notify the customer that the charge was approved
// and the order will be shipped
customer = this.'is placed by';
EE_OnlineCustomer.ChargeApproved(customerEmail => customer.email);
// Create a shipment to send the order to the customer
shipment = new Shipment();
shipment.RequestShipment(order => this);
}
Activity NotifyOfDelivery
private import EE_OnlineCustomer;
/**
Entry behavior for State 5. Delivered to Customer
*/
activity NotifyOfDelivery()
{
// Notify the customer that the Order
// has been delivered
customer = this.'is placed by';
EE_OnlineCustomer.OrderReportedDelivered(customerEmail => customer.email);
}
Alf Representation of the Ordering Model
The previous subsections describe a typical scenarion in which an overall UML model is represented largely graphically, with various behavioral snippets represented textually in Alf. However, at the extended conformance level, Alf also includes notation for structural models, such as packages, classes and associations (see Clause 10). This subsection shows how the models for the package Ordering and the class Order, represented graphically in Annex B.2.1, can alternatively be represented textually in Alf.
Package Ordering
private import EE_OnlineCustomer;
private import ProductSpecification::Product;
private import DomainDataTypes::*; // See Note 1
/**
Online Bookstore, Ordering Subsystem
*/
package Ordering
{
public active class ShoppingCart; // See Note 2
public active class Order;
public active class CreditCardCharge;
public active class Customer;
public active class ProductSelection;
public assoc R1
{
public 'selections are added to': ShoppingCart[0..*]; // See Note 3
public 'includes selections of': Product[1..*];
public 'product selection is': ProductSelection;
}
public assoc R2 {
public 'selections are purchased in': Order[0..1];
public 'is a purchase of selections in': ShoppingCart;
}
public assoc R3 {
public places: Order[1..*];
public 'is placed by': Customer[0..1];
}
public assoc R4 {
public 'is an attempt to pay for': Order;
public 'payment is attempted by': CreditCardCharge[0..*];
}
public assoc R5 {
public 'pays for': Order[0..1];
public 'is paid for by': CreditCardCharge[0..1];
}
}
Notes
-
The clause “private import ProductSpecification::Product;” denotes an element import of the Product class in the ProductSpecification package. Use of “private” indicates a private import—“public import ProduceSpecification::Product;” would indicate a public import. Similarly. “private import DomainDataTypes::*;” denotes the (private) package import of the package DomainDataTypes. That is, it is not just the package DomainDataTypes that is being imported, but all the elements of that package.
-
All the classes in this package have classifier behaviors and are thus active. Only stubs are included in this package specification, with the full definition of the class written separately. The use of stubs is a general option to allow long namespace definitions to be split up into multiple files. In contrast to the class definitions, the shorter association definitions are included in-line in this example.
-
Names do not have to conform to the usual identifier lexical structure. Such general names are enclosed in single quotes, as in “'selections are added to'”. Note that every character within the quotes is significant in the name (including spaces, but tabs and line breaks are not allowed).
Class Order
namespace Ordering;
/**
Active class for managing an order
*/
active class Order
{
public orderID: arbitrary_id;
public dateOrderPlaced: date;
public totalValue: Money;
public recipient: PersonalName;
public deliveryAddress: MailingAddress;
public contactPhone: TelephoneNumber;
private checkOutData: CheckOutData;
private chargeSubmissionData: ChargeSubmissionData;
public datatype CheckOutData
{
public cart: ShoppingCart;
public accountNumber: BankCardAccountNumber;
public billingAddress: MailingAddress;
public cardExpirationDate: MonthYear;
public cardholderName: PersonalName;
public customerEmail: InternetEmailAddress;
public customerName: PersonalName;
public customerPhone: TelephoneNumber;
public shippingAddress: MailingAddress;
}
public datatype ChargeSubmissionData
{
public accountNumber: BankCardAccountNumber;
public billingAddress: MailingAddress;
public cardExpirationDate: MonthYear;
public cardholderName: PersonalName;
}
public receive signal CheckOut // See Note 1
{
public data: CheckOutData;
}
public receive signal SubmitCharge
{
public data: ChargeSubmissionData;
}
public receive signal PaymentDeclined{}
public receive signal PaymentApproved{}
public receive signal OrderDelivered{}
private activity EstablishCustomer(); // See Note 2
private activity ProcessCharge();
private activity DeclineCharge();
private activity PackAndShip();
private activity NotifyOfDelivery();
}
do // See Note 3
{
/** 0. Waiting for Check Out */ // See Note 4
accept (checkOut: CheckOut); // See Note 5
this.checkOutData = checkOut.data;
/** 1. Establishing Customer and Verifying Payment */
EstablishCustomer(); // See Note 6
while (true) { // See Note 7
accept (chargeSubmission: SubmitCharge);
this.chargeSubmissionData = chargeSubmission.data;
/** 2. Submitting Charge */
ProcessCharge();
accept (PaymentDeclined) {
/** 3. Payment Not Approved */
DeclineCharge();
} or accept (PaymentApproved) { // See Note 8
break; // See Note 9
}
}
/** 4. Being Packed and Shipped */
PackAndShip();
accept (OrderDelivery);
/** 5. Delivered to Customer */
NotifyOfDelivery();
}
Notes
-
The notation “receive signal Checkout” indicates the definition of both a signal called Checkout within the Order namespace and the definition of a reception of that signal as a feature of the Order class. A signal definition alone would be denoted “signal Checkout{…}”and a definition of a reception for that signal would be “receive Checkout”. Note that, unlike the graphical UML notation, in Alf the attributes (“parameters”) of the signal would not be repeated in the reception definition.
-
Rather than standalone activities, the effective “entry behaviors” are now specified as privately owned activities of class Order. However, other than the different containing namespace, the definitions of these activities are exactly the same as given in Annex B.2.2. (Note that fUML semantics ensures that the correct context object for “this” references is propagated to activities called from the body of a classifier behavior.)
-
The “do” part of an active class definition gives its classifier behavior. Since Alf does not provide a notation for state machines, the classifier behavior for Order is specified here as an equivalent activity.
-
A documentation comment in this position attaches to the model element mapped from the following statement.
-
The “accept” statement waits for reception of the indicated signal and then continues. The name checkOut is given to the signal instance that is received.
-
Alf uses the usual object-oriented procedural notation for operation invocation.
-
The while statement provides structured iteration.
-
This is a compound accept statement. The additional “or accept” clause indicates waiting for reception of either a PaymentDeclined or a PaymentApproved signal. If a PaymentDeclined signal is received, then the statements associated with that accept clause (“this.DeclineCharge();”) are executed. If a PaymentAccepted signal is received, then the statements associated with its accept clause (“break;”) are executed.
-
The break statement terminates the enclosing while loop.
Share with your friends: |