[ genus.mdl ]
See
[ Polymorphism in uml1b ]
for more examples and discussion.
Constraints
Suppose we wish to document the fact that we find the grade for
a final examination by adding up the scores
on the Questions on the Final. It would be a mistake to
show that "grade was composed of scores" because (1) grade and score
are operations and not objects, and (2) composition does not mean arithmetic
addition. If we more more precise we would say:
self.score = self.questions.score->sum
Such constraints can be put inside the specifications that are linked to every box and line in a diagram using the UML. In Rational Rose for example a tab-bed box can be displayed specifying a class. The documentation field in the first "General" tab can mention the constraint. The Operations Tab has a list of operations. These also have specifications that can be opened up and edited. Constraints can be recorded in the pre-condition and post_condition tabs of the operations. Constraints can also be specified for associations and roles. These are automatically displayed inside braces: {constraint}.
The UML also defines a formal language for writing constraints called the
Object Constraint Language -- -- --(OCL)
[ ocl.html ]
It is quite a simple language and expresses some complex ideas simply
yet precisely. For example, the following constraint:
result = self.questions.scores->sum
Constraints on classes can placed in the diagram by adding comment
boxes.
The diagram below shows how to document the constraint linking Finals
and Questions:
[Download
[ uml20.mdl ]
the Rose model]
Adding Compartments
It is legal to extend the UML by defining a new compartment in classes in
addition to names, attributes, and operations. However the meaning of
the items in the new compartment needs to be
carefully documented
before the new notation can make sense to other people. Further it
is rare to find a tool that allows you to do this. One possibility
is to put constraints on classes in such a compartment.
Class Utilities
Some times we want to model a function library (for example <math.h>
in C) or a package of constants (Ada has a standard package of constants
used in the sciences). The UML models collections of data and functions
that are not attached to objects (free functions and static data)
as ClassUtilities. This looks like a normal class with a shadow. You can
find an example below:
[ umlutility.mdl ]
. . . . . . . . . ( end of section Advanceed UML) <<Contents | End>>
Hints
Now try running a scenario or algorithm thru by hand -- which object knows enough to get started? Which objects help it? and so on. Note. This best done with a group of people acting out the different classes of objects. It is fun and effective.
Be ready to change the writing on the cards, to delete things, insert things, and be ready to throw cards away. Also be ready to write new cards.
As you do this watch out for classes that share some common responsibilities since these indicate generalization relationships.
Don't draw a UML diagram until you are happy that the classes on the cards
make sense and you've confirmed this with your clients. When you convert
CRC cards to the UML the knowledge that objects are responsible for becomes
attributes and associations in the diagram. "Know-how" becomes operations.
Workshops and Walkthroughs
If you have users or clients then get them involved in animating
your CRC cards! This acts as a cheap low-tech prototype. It finds
mistakes fast and so save time, money and embarrassment.
It also stream lines your design.
Existence Dependency
Monique Snoeck & Guido Dedene have recently proposed
[SnoeckDedene98]
a simple notation that does this. For each class of object
you ask if it depends on an object of another class - for example
a student can not exist unless the student is a person, an enrollment
can not exist unless it has a student to be enrolled, and a class
to be enrolled in. You write down the names of the
objects and show dependency as a line going up from the
dependent object to the name of the class of the one that must also exist.
To make this into a UML diagram: (1) draw boxes round the words, (2)
set the multiplicity at the top of each line be 1, and (3) write down any
role names and multiplicities at the other end.
Classes Found in Business
Peter Coad's work analyzing lots of commercial systems
has uncovered certain kinds of classes and relations that
appear again and again. These can be given UML stereotypes
and then given colors.
Further he has chosen to use colors of popular sticky notelets.
He has found that certain objects (green) often share
descriptions (blue). These objects are typically places, things, or
people. They play different roles in the business(yellow)
at different times(pink).
In the more advanced tools a box can be colored in. The
result is a model that is much easier to take in by eye:
| Stereotype | color | Notes |
|---|---|---|
| <<entity>> | green | places, things and people |
| <<description>> | blue | describes sets of similar things |
| <<moment>> | pink | a time when an entity has a role |
| <<interval>> | pink | A range of times when an entity plays a role |
| <<role>> | yellow | A role that a place, thing, or person may play for a time. |
. . . . . . . . . ( end of section Hints) <<Contents | End>>
Patterns
Similar thing happen in other domains as well. For example in modeling
a factory we might find that a product is made of parts and these
parts are themselves products of the factory.
The Composite pattern handles these situations well.
[ composite.mdl ]
Simple Linked List
When you have objects sending messages to others of the same type you'll
probably end up using a Linked List data structure. See
[ Linked Data Structures in uml1b ]
Simple Subscriber
A method in an object can pass its own name to another object. This
helps when one class of objects is involved in some operations and
events and there are other objects, in a different class, that have an
interest in these events and/or operations. Rather than tangle up
the two classes, it is simpler to let the first class be responsible for keeping a list of other objects that want to be told when things happen... we can
call these subscribers. The first class then has methods that add and delete
subscribers into this list. These methods pass the identity of the
subscriber to the active class:
publisher.addSubscriber(this)From then on this is on the subscription list and gets told about changes. Normally there is a fixed function or interface that the publisher calls and the subscriber implements. Down load the UML model: [ subscriber.mdl ]
Factories
Sometime you need a standard way to construct a complex data structure
or family of objects. A Factory object can be made responsible rather than
having a constructor.
Instead we separate the task of traversing the data structure from the operation that is to be done to each item. The data structure knows how to traverse all the items. The travers operation takes objects called Visitors to each item and lets them do what they want with it.
The key is to define an abstract interface that is common to all Vistors. To create a new Visitor takes two steps: defining a class of Visitors that defines the visit operation, and then declaring an object to do the actual visit. Here is an example interface for Visitors integer data:
class Visitor{ public: virtual void visit(int &i)=0;
};
Then the operations are derived from this base:
class Adder:public Visitor {
private: int s;
public: Adder(): s(0){}
void visit(int &i){ s = s + i; }
int result(){return s;}
};//Adder
class Multiplier:public Visitor {
private: int p;
public: Multiplier(): p(1){}
void visit(int &i){ p = p * i; }
int result(){return p;}
};//MultiplierYou can add more types of visitors as you need... For example: Reader, Printer, Doubler, Copier, Zeroize, etc.
What do objects of this Visitor do?
class Debugger:public Visitor {
public: void visit(int &i){ cerr<< i << " "; }
};//Debugger
Then we can define classes that extend vectors and lists etc:
class MyVector:public vector<int>{
public: void traverse(Visitor &v)
{ for (int i=0; i<size(); i++) v.visit( at(i) );
}
};//MyVector
class MyList:public list<int>{
public: void traverse(Visitor &v)
{ for (list<int>::iterator i=begin(); i!=end(); i++) v.visit( *i );
}
};//MyListYou can add more traversals as well. Defining the traverse(Visitor & v) function makes sure they will work with any Visitor.
Here is how we use them:
Adder s; MyVector v; ....
v.traverse(s); .... cout << s.result()<<....;
Adder s2; MyList v; ....
v.traverse(s2); .... cout << s2.result()<<....;
Multiplier p; MyVector v; ....
v.traverse(p); .... cout << p.result()<<....;
Multiplier p2; MyList v; ....
v.traverse(p2); .... cout << p2.result()<<....;
For a sample of running C++ code see [ MyVector.cpp ]
You can down load the Rose model here: [ SimpleVisitor.mdl ]
As in most patterns once the framework is created endless new possibilities
open up.
The State Pattern
The state pattern allows an objects behavior to change as it runs. It
simulates dynamic inheritance. For example, suppose we want to have
a class of Person who can be either Married or Unmarried and whose behavior
depends on whether they are married or unmarried. We can not have
Married and Unmarried as subclasses of Person because people change from
one to the other. So we introduce a new class MaritalStatus with two
specialized classes: Married and Unmarried. The Person class is given
a reference to a MaritalStatus.
This allows us to delegate all marital matters to the MaritalStatus class. Person does need to know much about being married as long is it can look it up by using a MaritalStatus pointer! MaritalStatus will be an abstract class and Married and UnMarried will implement the common functions. Thus objects in Married know how to be married (and that they can be divorced, but not married again...). And those in Unmarried know all about this state! When an operation in Person depends on the MaritalStatus then Person's operation sends the appropriate operation to the currently referred to MaritalStatus. These function can return useful information, do useful things, and even hand back a new marital status!
We can finally improve the design by having precisely two MaritalStatus objects called married and unmarried! As long as the constructors for Married and Unmarried are private the rest of our program won't be able to make any more. We also keep the two objects hidden so that they can not be changed, but return their addresses to act as the states.
The final model is subtle: [ state.mdl ]
In this pattern we also used a variation of the Singleton Pattern
described next.
The Singleton Pattern
Sometimes we want to have a class that has precisely one instance with
many references to it. We do this by declaring private constructors
so that clients can not accidently create new objects. We also apply
a function returning a pointer to the object, getting its status,
and setting its status.
See Also
See
[Gammaetal94]
for the "Gang of Four" book of Design patterns, and then
Search my bibliography for patterns:
pattern
and my software developers resource
[ patterns in methods ]
. . . . . . . . . ( end of section Patterns) <<Contents | End>>
. . . . . . . . . ( end of section Advanced UML and OOAD) <<Contents | End>>
End