Java and UML Interaction Diagrams

Download Report

Transcript Java and UML Interaction Diagrams

Diagramy interakcji rodzaj diagramów dynamicznych
Autor: Magdalena Maksymowicz
Diagramy interakcji - rodzaj diagramów dynamicznych
Diagramy interakcji pozwalają pokazać realizacje przypadków użycia jako
współdziałanie obiektów klas. Obiekty wysyłają do siebie komunikaty (jest
obiekt-nadawca komunikatu i obiekt-odbiorca). W wyniku odebrania
komunikatu obiekt-odbiorca wykonuje swoją operację (można powiedzieć, że
komunikat jest wywołaniem operacji) lub powstaje sygnał. Działanie
programu jest sekwencją komunikatów.
Nie dla wszystkich przypadków użycia może zachodzić potrzeba
konstruowania diagramów interakcji, lecz dla przypadków „trudnych", dla
których może zachodzić potrzeba rozważenia alternatywnych sposobów
realizacji. Niektóre narzędzia CASE potrafią wykorzystać te diagramy do
generacji kodu, co może stanowić ważny powód dla ich konstruowania.
UML definiuje dwa rodzaje diagramów interakcji:
diagramy kooperacji (współdziałania),
diagramy sekwencji (przebiegu).
Oba rodzaje diagramów przedstawiają prawie tą samą informację,
w nieco inny sposób.
Diagramy interakcji - diagram kooperacji
Diagram kooperacji.
Obiekty klas na diagramie, między którymi są przekazywane komunikaty są
połączone. Stanowią one tzw. grupę współdziałania (kooperację, kolaborację)
realizującą dany przypadek użycia. Połączenia odpowiadają powiązaniom
między danymi klasami na diagramie klas. Na diagramie interakcji może
wystąpić więcej niż jeden obiekt danej klasy.
Diagramy interakcji - diagram kooperacji
Diagram kooperacji.
Obiekty klas mogą być oczywiście przedstawione jako prostokąty. Oznaczenie
obiektów kółkami podkreśla, że rozważa się realizacje przypadku użycia jako
zadanie analizy, a nie projektowania.
Komunikaty przedstawiane są tu w postaci etykiet strzałek rysowanych
wzdłuż połączeń między współpracującymi przy realizacji przypadku użycia
obiektami.
Na diagramach interakcji z reguły nie pokazuje się odpowiedzi na wysyłane
komunikaty.
Komunikaty mogą być numerowane albo kolejnymi liczbami naturalnymi (jak
na poprzednim diagramie), albo stosując tzw. numerację zagnieżdżoną.
Diagramy interakcji - diagram kooperacji
Numeracja zagnieżdżona, np. 2.1 ...oznacza, że obiekt otrzymał komunikat o numerze 2 i
w odpowiedzi na ten komunikat podejmuje akcje, które zostają ponumerowane zgodnie z
kolejnością występowania, a numery poprzedzone prefiksem - 2, wskazującym na ich
„powód".
Diagramy interakcji - diagram sekwencji (przebiegu)
Diagram posiada dwa wymiary :
poziomy - stanowią obiekty biorące udział w PU; kolejność obiektów nie ma
znaczenia;
pionowy - przyrost czasu, kolejność w czasie;
Obiekt jest przedstawiany jako „linia życia" (linia pionowa przerywana) z
prostokątem u góry z podkreśloną nazwą obiektu.
Strzałki prostopadłe do „linii życia" symbolizują komunikaty przesyłane od
obiektu-nadawcy do obiektu-odbiorcy. Komunikaty są oznaczone nazwą akcji.
Nie ma konieczności ich numerowania, bo pokazana jest kolejność w czasie.
Wydłużony prostokąt na „linii życia" oznacza stan aktywny obiektu
(najczęściej oznacza to, że odbiera lub wysyła komunikat, jest w trakcie
odpowiedzi na odebrany komunikat).
Jeśli obiekt jest tworzony w czasie trwania modelowanego przypadku użycia,
prostokąt wieńczący „linię życia" pojawia się w odpowiednim punkcie
czasowym.
Diagramy interakcji - diagram sekwencji (przebiegu)
Jeśli obiekt jest likwidowany w czasie trwania przypadku użycia, „linia życia"
jest urywana w odpowiednim miejscu ze znakiem "X" na końcu.
X
Diagramy interakcji - diagram sekwencji (przebiegu)
Dwa sposoby opisywania czasu: oznaczanie skali czasowej lub nakładanie
ograniczeń na upływ czasu. Ograniczenia oznacza się w nawiasach klamrowych,
np. {< 5 sek.} może oznaczać, ze reakcja na komunikat nie powinna trwać dłużej
niż 5 sekund.
Czasami przydaje się uwidocznienie wartości zwracanej przez komunikat, poprzez
instrukcję przypisania. Umożliwia to późniejsze wykorzystanie tej wartości, np.
jako argumentu dla innego komunikatu. Może też być wykorzystana do
specyfikowania warunku czy iteracji.
Diagram interakcji może wystąpić w dwóch formach:
opisującej jeden z przebiegów,
ogólnej, stanowiącej opis wszystkich przebiegów przypadku użycia (z pokazaniem
warunków iteracji).
Diagramy interakcji - diagram sekwencji (przebiegu)
Przykłady iteracji:
*[i := 1..10] - komunikat będzie wysłany 10 razy,
*[x < 10] - komunikat będzie wysyłany dopóki x będzie < 10,
*[pozycja znaleziona] - komunikat będzie wysyłany do momentu, gdy pozycja nie zostanie znaleziona
(gdy warunek przyjmie wartość FALSE)
Jeśli w trakcie wielokrotnego wysyłania komunikatu x, będzie wysyłany także komunikaty, to zostanie on
wysłany tyle razy, ile razy wysyłane jest x. Zachowanie spójności diagramów
nie wymaga powtarzania symbolu iteracji dla komunikatu y.
Dalej żywcem skopiowany artykuł, którego autorem jest Steven Palmer:
Java and UML Interaction Diagrams.
Dalej w podróży tej towarzyszyć nam będzie Niedźwiadek z Rowerkiem
Java and UML Interaction Diagrams
Interaction diagrams depict a specific set of interactions between a set of objects. In this fourth article
introducing UML from a programmer's perspective, Stephen Palmer compares UML sequence and
collaboration diagrams with equivalent Java source code constructs.
We will be using a very simplistic and partially complete sales and tracking system to illustrate sequence and
collaboration diagrams. The system consists of six Java classes with the following major methods, in addition
to the usual accessor methods for properties and collections:


Sale

calcPayments—Total the amount of all payments made for the sale

calcTotal—Total the cost of all items purchased as part of the sale

complete—Mark the sale transaction as finished
LineItem


calcTotal—Total the cost of one type of item purchased
Product

calcTotal—Total the cost of one type of item purchased

Payment

CreditCardPayment


authorize—Authorize the use of the credit card for this payment
CashPayment

calcChange—Calculate the amount of change to be returned
to the purchaser
Java and UML Interaction Diagrams
Figure 1 shows a class diagram for our sales- and payment-tracking classes.
Figure 1 Class diagram showing the structure of a simply sales and payment tracking system.
Java and UML Interaction Diagrams
Interaction Diagrams
If we trace through an execution of any Java program, we see that it contains
one or more sequences of method invocations on objects and classes. We
invoke a method on an object of a class to answer a specific question or
perform a specific action. Often, that method will invoke other methods—
either on itself, on objects of the same class, or on objects of other classes.
These methods, in turn, may invoke other methods and so on until the question
is completely answered or the requested action is completely performed (or an
exception occurs that prevents the question being answered or the action being
performed).
UML interaction diagrams graphically represent sequences of method
invocations, and come in two flavors: sequence diagrams and collaboration
diagrams.
Java and UML Interaction Diagrams
Sequence Diagrams
UML sequence diagrams typically show some sequence of method invocations
that achieve some specific purpose. Figure 2 shows a sequence diagram for
calculating the total of a sale transaction. It starts with a call to the method
calcTotal() of the Sale class. The relevant snippets of source code are shown
below the diagram.
Terminology Note
UML defines an operation as the signature of a method. The term method is
reserved for the code that provides the implementation of an operation. In the
Java world, it is usual to use term method in both contexts. In sequence
diagrams, the invoking of an operation is called sending a message. Sequence
diagrams are essentially about the implementation of operations, so I have
used the term method throughout this article (occasionally resorting to the term
message when describing a particular UML diagram).
Java and UML Interaction Diagrams
Figure 2 A sequence diagram for calculating the total of a sale.
Java and UML Interaction Diagrams
/**
* From the Sale class:
* calculates the total of the sale from the lineItem subtotals
* @return total of the sale
*/
public double calcTotal() {
total = 0.0;
Iterator i = lineItems.iterator();
while (i.hasNext()) total += ((LineItem)i.next()).calcTotal();
return total;
}
/**
* From the LineItem class:
* calculates the cost of this amount of this kind of item
* @return the cost of this amount of the item
*/
public double calcTotal() {
total = product.calcTotal(this);
return total;
}
/**
* From the Product class:
* calculates the current cost of a quantity of the product
* @return cost of the line item supplied
*/
public double calcTotal(LineItem li) {
return amount * li.getQuantity();
}
Java and UML Interaction Diagrams
To get a better overall feel for the sequence, only the method names are shown. More detailed
sequence diagrams show method arguments and return types.
Objects that participate in the sequence and exist at the start of the sequence are spread across
the top of the diagram. They are displayed using the usual UML notation for an object; the
same shape or symbol used for the class of the object (a rectangle by default) with the name of
the object followed by a colon, and the name of the class that defines that object. The whole
name is underlined (for example, aProduct:Product in Figure 2). Either the object name (for
example, :Sale in Figure 2) or class name (for example, Sender in Figure 2) may be omitted,
but obviously not both. If the object name is omitted, the colon must be retained.
Time is imagined as running vertically from top to bottom of the diagram. Each object has a
lifeline running vertically down the page, immediately below its rectangle. Method invocations
are drawn as solid lines with open arrowheads from the lifeline of the calling object to the
lifeline of the receiving object. An object's lifeline is widened whenever one of its methods is
being executed. These activation bars may be nested to show that another method of the object
has been invoked as part of the execution of the previous method; the getQuantity() method in
Figure 2 is an example of this.
Optionally, returns from methods may be shown as a dotted line with an open arrowhead (for
example, the return arrow from :Sale to Sender in Figure 2) where it makes things clearer to do
so.
Java and UML Interaction Diagrams
Where iteration over a collection of objects is needed, an asterisk precedes the method
name with an optional condition inside square brackets. An example can be seen in the
call from the Sale class to the LineItem class objects in Figure 2.
As with UML class diagrams, what would normally require the inspection of multiple
source code files is summarized in one UML diagram. Reverse engineering sequence
diagrams from existing source code can help developers new to the code understand
how it works, and can help developers communicate to client representatives the way
the software works in a form less threatening than source code.
Objects that I Know
When sketching a sequence diagram to explore the problem domain, explicitly prove a
class diagram can support a sequence of method invocations that achieve a required
goal, or as part of the low-level design of a functional requirement, a common mistake
that must be avoided is to invoke a method on an object that the caller has no direct
knowledge of. In Java, there are three fundamental ways an object may know of the
existence of another object. First, an object's static or instance variables may contain
either a reference to the target object, or a name or identifier through which the object
might be located via a lookup service of some description (for example, JNDI, Factory
class, database query). In this case, this knowledge is usually shown as an association in
a class diagram. Second, the calling object may be passed a reference
to the target object as a parameter of the invoked method.
Third, the calling object may create a new instance of the target object.
Java and UML Interaction Diagrams
More Notation
Figure 3 shows a sequence diagram for the complete() method of the Sale class. This
time, we have numbered the messages. The complete() method calls two other methods
of the Sale class: calcTotal() and calcPayments(). Figure 3 shows the loopback notation
used to indicate that an object is calling method on itself.
Larger sequence diagrams can become too wide to display comfortably on a screen. In
Figure 3, a switch in Together ControlCenter's2 options panel displays the class name
underneath the object name instead of alongside, reducing the amount of horizontal
space required for each object. This can reduce the amount of horizontal space required
by the diagram if class names are longer than a few characters and arguably make the
diagram a little easier to read. However, to be strictly compliant with the latest UML
specification3, the class name should appear to the left of the object name and be
preceded by a colon, as shown in Figure 2.
Notation Note
When working collaboratively, I enjoy the convenience of using three-inch square PostIt[tm]notes and marker pens on flipchart pads or whiteboards. I also use a common
shorthand convention for the object and class name; form the object name by prefixing
the class name with an"a" or "an" as appropriate, and omit the class name.
Java and UML Interaction Diagrams
Figure 3 A sequence diagram for completing a sale.
Java and UML Interaction Diagrams
As a result of the call to calcTotal() by the complete() method, our calcTotal()
sequence in Figure 2 is a consequence of the complete() sequence in Figure 3. We
could have simplified Figure 3 by omitting the Product object and its interactions
with the LineItem object and by referring the reader to Figure 2 at that point. Again,
the general principle of only showing what is necessary to accurately communicate
with the reader applies. For example, few readers of a sequence diagram will
benefit from the inclusion of standard Java classes such as iterators, wrappers, and
collection classes. Also, although a sequence diagram can show the use of looping
and branching constructs, this level of detail is perhaps best examined by reading
the actual source code in conjunction with a higher-level sequence diagram. Figure
4 shows the result of using Together ControlCenter to reverse engineer the
complete() method of the Sale class and asking it to include as much detail as
possible. This level of detail is probably too much for most people. However,
Figure 4 does serve one useful purpose: The exception object shows that objects
created during a sequence diagram are drawn at the point they are created instead of
at the top of the diagram.
Java and UML Interaction Diagrams
Figure 4 A highly detailed sequence diagram generated by a tool.
Java and UML Interaction Diagrams
Collaboration Diagrams
The second flavor of UML interaction diagram is the collaboration diagram.
Semantically equivalent to a sequence diagram, this type of diagram arranges
objects spatially—according to the whim of the user creating the diagram. The
sequence of interactions is determined from the message numbering. Some
people prefer this approach, and better UML tools allow the user to flip a
diagram between sequence and collaboration notations and back again as often
as they wish. Some organizations have a convention of using collaboration
diagrams to show interactions between components and using sequence
diagrams to show interactions between classes within a component. Figure 5
shows the collaboration diagram equivalent of the sequence diagram in Figure
2. Figure 6 does the same for Figure 4.
Java and UML Interaction Diagrams
Figure 5 UML collaboration diagram.
Java and UML Interaction Diagrams
Figure 6 Collaboration diagram equivalent of Figure 4.
Java and UML Interaction Diagrams
Conclusion
With practice, many required sequences of interactions can be implied from the class
diagram, especially when class archetypes and stereotypes are used to indicate particular
patterns of behavior and interaction. UML interaction diagrams make those implications
explicit and describe clearly scenarios in which the sequence of interactions is not clear
from class diagrams. In other words, UML interaction diagrams complement the more
static nature of UML class diagrams by making explicit the dynamic interactions
involved.
Neither type of diagram is particularly good at showing recursion and the handling of
exceptions. For significant interactions of this type, it is best to attach a note to the
relevant points in the diagram.
In the last article in the series, we will look briefly at the other five UML diagram types.
Until then, I leave you with a quote from Peter Coad (et al):
"Why use scenarios [interaction diagrams]? Here's why:
 To find additional objects.
 To better distribute and refine responsibilities.
 To gain a better understanding of system dynamics.
 To assess model completeness.
 To test an object model (ultimately, to test the system itself).
Java and UML Interaction Diagrams
Source Code
Sale Class
/**
* One user transaction consisting of purchases of potentially many kinds of
product
* @stereotype moment-interval
*/
public class Sale {
/**
* calculates the total of the sale from the lineItem subtotals
* @return total of the sale
*/
public double calcPayments() {
paymentsTotal = 0.0;
Iterator i = payments.iterator();
while (i.hasNext()) paymentsTotal += ((Payment)i.next()).getAmount();
return total;
}
/**
* calculates the total of the sale from the lineItem subtotals
* @return total of the sale
*/
public double calcTotal() {
total = 0.0;
Iterator i = lineItems.iterator();
while (i.hasNext()) total += ((LineItem)i.next()).calcTotal();
return total;
}
Java and UML Interaction Diagrams
/**
* retrieves the cached total of the sale
* @return total of sale, 0.0 if calcTotal() has not yet been called
*/
public double getTotal() {
return total;
}
/**
* sets the status of the sale to compltete if payments equal price
* @exception Exce[tion thrown if payments do not equal price
*/
public void complete() throws Exception {
if (calcTotal() != calcPayments()) {
throw new Exception("Payments do not equal total price");
}
status = "Complete";
}
/**
* adds a line item for a quantity of a type of item
* @param product the type of item being sold
* @param qty the quantity of the item being sold
*/
public void addLineItem(Product product, int qty) throws Exception {
if (status.equals("Incomplete")) {
lineItems.add(new LineItem(product, qty));
}
else {
throw new Exception("Cannot add items to a completed sale");
}
}
Java and UML Interaction Diagrams
/** status of sale */
private String status = "Incomplete";
/**
* the cost of this quantity of the item the Product is responsible for
* handling quantity discounting or time based special deals, etc
*/
private double total;
/** the total of all the payments made for this sale */
private double paymentsTotal;
/**
* @link aggregation
* @associates <{LineItem}>
* @supplierCardinality 1..*
* @clientCardinality 1
*/
private Vector lineItems = new Vector();
/**
* payments of various kinds
* @associates <{Payment}>
* @supplierCardinality 0..*
* @clientCardinality 1
*/
private Vector payments = new Vector();
}
Java and UML Interaction Diagrams
SaleItem Class
/**
* one line in a sale of potentially many kinds of product
* @stereotype mi-detail
*/
public class LineItem {
/**
* mandatory value constructor requires values for all attributes needed to
* put the object in a valid state
* @param product the kind of item being purchased
* @param qty the quanity of the item being purchased
*/
public LineItem(Product product, int qty) {
this.product = product;
this.quantity = qty;
}
/**
* calculates the cost of this amount of this kind of item
* @return the cost of this amount of the item
*/
public double calcTotal() {
total = product.calcTotal(this);
return total;
}
Java and UML Interaction Diagrams
/**
* accessor method for cached cost of this lineItem
* @return price for this quantity of the item at the time of the sale
*/
public double getTotal() {
return total;
}
/**
* accessor method for quantity
* @return the quanity of the item being purchased
*/
public int getQuantity() {
return quantity;
}
/**
* accessor method for quantity
* @param quantity the quanity of the item being purchased
*/
public void setQuantity(int quantity) {
this.quantity = quantity;
}
Java and UML Interaction Diagrams
/**
* the kind of ite being purchased
* @supplierCardinality 1
* @clientCardinality 0..*
*/
private Product product;
/** cached value of this line item */
private double total;
/** number of units being purchased */
private int quantity;
}
Java and UML Interaction Diagrams
Product Class
/**
* Instances represent entries in the inventory catalog
* @stereotype description
*/
public class Product {
/**
* mandatory values constructor requires values for all attributes needed to
* leave the object in a valid state
* @param code the UPC
* @param name short human friendly name for the product
* @param amount the price of one unit of the product
*/
public Product(String code, String name, double amount) {
this.code = code;
this.name = name;
this.amount = amount;
}
/**
* calculates the current cost of a quantity of the product
* @return cost of the line item supplied
*/
public double calcTotal(LineItem li) {
return amount * li.getQuantity();
}
/** UPC */
private String code;
Java and UML Interaction Diagrams
Product Class
/**
* Instances represent entries in the inventory catalog
* @stereotype description
*/
public class Product {
/**
* mandatory values constructor requires values for all attributes needed to
* leave the object in a valid state
* @param code the UPC
* @param name short human friendly name for the product
* @param amount the price of one unit of the product
*/
public Product(String code, String name, double amount) {
this.code = code;
this.name = name;
this.amount = amount;
}
/**
* calculates the current cost of a quantity of the product
* @return cost of the line item supplied
*/
public double calcTotal(LineItem li) {
return amount * li.getQuantity();
}
/** UPC
private
/** Human
private
*/
String code;
friendly product name */
String name;
/** current price of a unit of the product */
private double amount;
}
Java and UML Interaction Diagrams
Payment Class
/**
* represents a payment of some kind
* @stereotype moment-interval
*/
abstract public class Payment {
/**
* mandatory value constructor requires values for all attributes needed to
* put the new object into a valid state
*
* @param amount the amount of this payment
*/
public Payment(double amount) {
this.amount = amount;
}
/**
* accessor for amount property
* @return the amount property
*/
public double getAmount() {
return amount;
}
Java and UML Interaction Diagrams
/**
* accessor for the amount property
* @param amount the value of the property
*/
public void setAmount(double amount) {
this.amount = amount;
}
/** amount of the payment */
private double amount;
}
Java and UML Interaction Diagrams
CashPayment Class
/**
* a subclass that extends the Payment class to represent cash payments
* @stereotype moment-interval
*/
public class CashPayment extends Payment {
/**
* mandatory value constructor requires values for all attributes needed to
* put the new object into a valid state
*
* @param amount the amount tendered for this payment
*/
public CashPayment(double amount) {
super(amount);
}
/**
* amount tendered is what was passed into the constructor
* @return amount tendered
*/
public double getAmountTendered() {
return super.getAmount();
}
Java and UML Interaction Diagrams
/**
* calculate the change to be given for a required amount
* @return value of change
*/
public double calcChange( double requiredAmount ) {
change = super.getAmount() - requiredAmount;
return change;
}
/**
* override superclass to calc actual amount based on change given
* @return amountTendered - change given
*/
public double getAmount() {
return super.getAmount() - change;
}
/** amopunt of change given */
private double change = 0.0;
}
Java and UML Interaction Diagrams
CreditCardPayment Class
/**
* a subclass that extends the Payment class to represent credit card payments
* @stereotype moment-interval
*/
public class CreditCardPayment extends Payment {
/**
mandatory value constructor requires values for all attributes
needed to put the new object into a valid state
* @param amount the amount of this payment
* @param cardNumber the number of the credit card
*/
public CreditCardPayment(double amount, String cardNumber) {
super(amount);
this.cardNumber = cardNumber;
}
/**
* accessor for cardNumber property
* @return value of property
*/
public String getCardNumber() {
return cardNumber;
}
Java and UML Interaction Diagrams
/**
* accessor for cardNumber property
* @param cardNumber value of property
*/
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
/**
* authorize the payment using an external system somewhere
* @return true if payment valid
*/
public boolean authorize() {
return false; //not yet implemented
}
/** credit card number property */
private String cardNumber;
}