[Skip Navigation] [CSUSB] / [CNS] / [Comp Sci Dept] / [R J Botting] / [Samples] / uml4
[Index] [Contents] [Source Text] [About] [Notation] [Copyright] [Comment/Contact] [Search ]
Tue Sep 18 15:27:25 PDT 2007

Contents


    Advanced UML and OOAD

      These are some notes on the less well known and/or new ideas in the Unified Modeling Language -- -- --(UML)
      and some hints for starting object-oriented analysis and design-- -- --(OOAD)
      For less advance information see [ uml.html ]

      Advanced UML

        Polymorphism

        Polymorphism is useful whenever we have objects that share some operations, but also have their own specific versions. We put the common properties in a new class that generalizes both special cases. We also include operations in both the general and the specific cases. If we don't know how the general case behaves then we make it an abstract class and only refer to it via pointers to the specific cases. A simple example is that all Animals make a noise, but each species (Lion, Duck, Wolf, ...) has its own noise. genus.gif [ 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:
      1. "the score on the final is the sum of the scores on the Questions" or in the official language for such constraints - the OCL:
         	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
      2. is a specification for the Final::grade() operation of a Final as the sum of the scores of the questions on the final.

        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: Example of constraint [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:

        umluitility.gif [ umlutility.mdl ]

      . . . . . . . . . ( end of section Advanceed UML) <<Contents | End>>

      Hints

        There are several approaches to drawing a UML diagram. Here are some ideas you might try out in addition to the conceptual modeling technique mentioned in [ cs320wuml.html ] and [ uml1.html ] Here is a list of things to be checked about any model
        • Do the names make sense on the diagrams?
        • How does this set of objects and links do what the user wants?
        • There should be no dictators, omnipotent objects, or classes that are in control.
        • Replace an object with a complex operation by several objects that communicate to implement the operation simply.
        • An if-then-else may mean you need to use polymorphism.
        • It is OK for an object to talk to itself or to objects of the same type.
        • Do not confuse composition (a part of) with generalization (a kind of).
        • Can you split the user-interface objects away from the conceptual objects?
        • Can you split the database/persistent objects away form the logical objects?
        • Check the spelling!

      (End of Net)
      In a complex project it can be difficult to get a complete and correct UML diagram at the start of a project. One needs a very simple starting point that is easy to change and easy to translate into UML. One must be ready to test and change ones original ideas as the project progresses.

      Responsibility Driven Design

      Start by using post-it notes or 3-by-5 cards. One for each class of objects. Write down -- in pencil -- on each one
      1. Class: the name of the class
      2. Responsibilities: What the objects know and what they can do
      3. Collaborators: What other objects help this class of objects meet its responsibilities.

      The above are called CRC cards:
    1. CRC::={Class, Responsibilities, Collaborators}.

      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.

      Existence Dependency Diagram for a College

      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: A Colored UML diagram
      StereotypecolorNotes
      <<entity>>greenplaces, things and people
      <<description>>bluedescribes sets of similar things
      <<moment>>pinka time when an entity has a role
      <<interval>>pinkA range of times when an entity plays a role
      <<role>>yellowA role that a place, thing, or person may play for a time.

      Project Scope

      Big projects have a tendency to fail in an embarrassing way. It is wise therefore to take a big project and split it into a series of simple steps that add up to the big project when all are completed. The aim is to deliver and test a small but usable subset of the whole thing within a few weeks, and then start adding functionality to it. The trick is set the complexity of each increment (addition) to be something that can be done in a fixed time.

    . . . . . . . . . ( end of section Hints) <<Contents | End>>

    Patterns

      A pattern is a well known way of solving a design problem. Typically a pattern is a way of resolving conflicting needs: Maintenance vs Speed for example. Often it is an inventive way of using objects, classes, and polymorphism to get a special effect. The classic work on object oriented design patterns is "Design Patterns" By the Gang of Four (GoF). Here are some simple ones.

      The Composite Pattern

      In describing programming languages and other domains we often have a situation where an object can be made of other objects from the same class. For example, a statement may be a compound statement, and compound statements contain a number of statements. Similarly an expression often has an operator and a number of operands, and each operand can be any expression. Something as simple as a list of items might be defined by
    1. list::= element | element comma list.

      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.gif [ 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.
    2. Factory::=An object that has a method that creates objects of other types.

      Simple Visitor

      Sometimes we want something to happen to every item in a data structure. For example
      1. Add up all the items.
      2. Multiply the items.
      3. Count the items.
      4. Divide all the items by 10.
      5. ...

      We often want do several different things to several different data structures.
      1. vector
      2. list
      3. tree
      4. heap
      5. ...

      But, we don't want to code every possible pairing of data structure and operation: suppose we have four structures (list above) and four operations (above) then we don't want to code the 16 different combinations.

      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;
       	};

      Visitors and Containers

      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;}
       	};//Multiplier
      You 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 );
       		   }
       	};//MyList
      You 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