.Open 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 .See http://www/dick/samples/uml.html .Open 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. .Image http://www.csci.csusb.edu/dick/cs320/handouts/genus.gif genus.gif .See http://www.csci.csusb.edu/dick/cs320/handouts/genus.mdl See .See http://www/dick/samples/uml1b.html#Polymorphism 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: "the score on the final is the sum of the scores on the Questions" or in the official language for such constraints - the $OCL: .As_is 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) .See http://www/dick/samples/ocl.html It is quite a simple language and expresses some complex ideas simply yet precisely. For example, the following constraint: .As_is result = self.questions.scores->sum 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: .Image http://www.csci.csusb.edu/dick/cs320/handouts/uml20.gif Example of constraint [Download .See http://www/dick/samples/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 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: .Image http://www.csci.csusb.edu/dick/cs320/handouts/umlutility.gif umluitility.gif .See http://www.csci.csusb.edu/dick/cs320/handouts/umlutility.mdl .Close Advanceed UML .Open 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 .See http://www/dick/cs320/handouts/cs320wuml.html and .See http://www/dick/samples/uml1.html Here is a list of things to be checked about any model .Set 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! .Close.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 .Box Class: the name of the class Responsibilities: What the objects know and what they can do Collaborators: What other objects help this class of objects meet its responsibilities. .Close.Box The above are called CRC cards: 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 .See [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. .Image coll.exist.gif 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: .Image colored.uml.gif A Colored UML diagram .Table Stereotype color Notes .Row <> green places, things and people .Row <> blue describes sets of similar things .Row <> pink a time when an entity has a role .Row <> pink A range of times when an entity plays a role .Row <> yellow A role that a place, thing, or person may play for a time. .Close.Table . 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. .Close Hints .Open 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 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. .Image http://www.csci.csusb.edu/dick/cs320/handouts/composite.gif composite.gif .See http://www.csci.csusb.edu/dick/cs320/handouts/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 .See http://www/dick/samples/uml1b.html#Linked Data Structures . 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: .As_is 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: .See http://www/dick/samples/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. 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 .List Add up all the items. Multiply the items. Count the items. Divide all the items by 10. ... .Close.List We often want do several different things to several different data structures. .List vector list tree heap ... .Close.List 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: .As_is class Visitor{ public: virtual void visit(int &i)=0; .As_is }; .Image simplevisit.gif Visitors and Containers Then the operations are derived from this base: .As_is class Adder:public Visitor { .As_is private: int s; .As_is public: Adder(): s(0){} .As_is void visit(int &i){ s = s + i; } .As_is int result(){return s;} .As_is };//Adder .As_is class Multiplier:public Visitor { .As_is private: int p; .As_is public: Multiplier(): p(1){} .As_is void visit(int &i){ p = p * i; } .As_is int result(){return p;} .As_is };//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? .As_is class Debugger:public Visitor { .As_is public: void visit(int &i){ cerr<< i << " "; } .As_is };//Debugger Then we can define classes that extend vectors and lists etc: .As_is class MyVector:public vector{ .As_is public: void traverse(Visitor &v) .As_is { for (int i=0; i{ .As_is public: void traverse(Visitor &v) .As_is { for (list::iterator i=begin(); i!=end(); i++) v.visit( *i ); .As_is } .As_is };//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: .As_is Adder s; MyVector v; .... .As_is v.traverse(s); .... cout << s.result()<<....; .As_is Adder s2; MyList v; .... .As_is v.traverse(s2); .... cout << s2.result()<<....; .As_is Multiplier p; MyVector v; .... .As_is v.traverse(p); .... cout << p.result()<<....; .As_is Multiplier p2; MyList v; .... .As_is v.traverse(p2); .... cout << p2.result()<<....; For a sample of running C++ code see .See ./MyVector.cpp You can down load the Rose model here: .See http://www/dick/samples/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: .See http://www.csci.csusb.edu/dick/samples/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 .See [Gammaetal94] for the "Gang of Four" book of Design patterns, and then Search my bibliography for patterns: .Find pattern and my software developers resource .See http://www/dick/samples/methods.html#patterns .Close Patterns .Close Advanced UML and OOAD