1. Domain Modeling

Domain Modeling

An application domain or enterprise is the real world context of an application. For example, the application domain for a warehouse inventory system is the warehouse, including the palettes, boxes, aisles, bins, forklifts, loading docks, workers, customers, and shipping orders. During the requirements analysis phase the application developers must gain an adequate understanding of the application domain. This might involve working with domain experts (e.g. the warehouse manager) to build an application domain model:

Data flow models, entity-relation models, mathematical formulas, and object models are examples of common application domain models. We will see some examples shortly.

UML

The Unified Modeling Language (UML) is a system for building object oriented models using diagrams. During the analysis phase, the diagrams are used to model the application domain. Later, during the design phase, the diagrams are refined to describe the architecture of the application.

UML was developed at Rational Software [WWW 6] by Grady Booch, Jim Rumbaugh, and Ivar Jacobson (who like to call themselves The Three Amigos). It will probably become the standard modeling language.

Class Diagrams

Although there are many types of UML diagrams, we will only use class, object, and sequence diagrams. A class diagram represents classes by labeled boxes and relationships between classes by different types of connecting arrows. If we are building a model of an application domain, classes represent significant groups of people, places, things, and events. In the design and implementation phases, classes represent programmer defined data types. There are two types of relationships: specialization and association.

Specialization (Generalization)

Specialization (also called generalization) is the relationship between a subclass and a super class. For example, secretaries and executives are subclasses of employees because secretaries and executives are special kinds of employees. We express this in a class diagram as follows:

In C++ specialization is the relationship between a derived class and a base class. During the implementation phase, we might turn this diagram into the following declarations:

class Employee { ... }; // base class
class Secretary: public Employee { ... }; // derived class
class Executive: public Employee { ... }; // derived class

A subclass extends its super class by adding attributes and services, while retaining the attributes and services of the super class through inheritance. Java syntax makes this more explicit:

class Employee { ... }
class Secretary extends Employee { ... }
class Exceutive extends Employee { ... }

Association

Association is a relationship between objects of one class and objects of another class. While specialization is a relationship between classes, association is really a relationship between objects. For example, an executive might have one or two private secretaries. We can express this in our class diagram as follows:

(We combined the two specialization arrows into a single arrow to simplify the diagram.) We can distinguish association from specialization by the arrowheads. In an association, an arrowhead implies objects of one class know about associated objects of the other class. In our example, a secretary knows the executive he works for, and an executive knows the secretaries who work for him. If we weren't sure about this, we could leave the arrowheads off.

Multiplicity

The numbers in the last diagram indicate multiplicity. A private secretary works for one executive, but an executive might have one or two private secretaries. If an executive could have any number of secretaries, including none, we could represent this with a star:

In C++ an association might be represented by a member variable containing an object, an object reference, or an object pointer. For example:

class Executive: public Employee
{
   Secretary sec1, *sec2;
   // etc.
};

class Secretary: public Employee
{
   Executive& boss;
   // etc.
};

Creation

Creation is a special type of association. For example, a factory creates many products. UML doesn't have a special type of arrow to represent creation. We will use a one way arrow labeled by the word "creates". One factory produces many products, so the multiplicity is implicitly understood to be one to many:

In C++ creation might be represented by a member function in the factory class that returns a new product, a references to a new product, or a pointer to a new product. Such member functions are called factory methods:

class Product { ... };

class Factory
{
public:
   // a factory method:
   Product* makeProduct(...) { return new Product(...); }
   // etc.
};

Aggregation

Aggregation is another special type of association. It can be used to express the relationship between a whole and its parts (filled diamond aggregation) or, more commonly, the relationship between a container and the items it contains (hollow diamond aggregation). For example, a wheel is part of a car, and an organization contains people:

How are these different from the standard associations:

The distinction is so subtle that it is seldom worth making. Sometimes it causes more confusion than enlightenment. In this book we will not use filled diamond aggregation. We will use hollow diamond aggregation to indicate that the container class has or is a container such as an array, vector, map, stack, queue, or list that holds the contained items:

class Person { ... };

class Organization
{
public:
   bool qualified(Person* p);
   void addMember(Person* p)
   {
      if (qualified(p)) members.push_back(p);
   }
   void removeMember(Person* p) { members.remove(p); }
   // etc.
private:
   list<Person*> members; // list<> from STL
};

Alternatively, organization could privately inherit from list<>:

class Organization: list<Person*>
{
public:
   bool qualified(Person* p);
   void addMember(Person* p)
   {
      if (qualified(p)) push_back(p);
   }
   void removeMember(Person* p) { remove(p); }
   // etc.
};

We choose private inheritance to prevent clients from adding "unqualified" members to organizations:

Organization kgb;
Person jfk;
kgb.push_back(&jfk); // error!

Adding Attributes and Services

UML allows us to elaborate classes by adding attributes and services to class boxes. For example, two employee attributes are name and salary. Typing speed might be a secretary attribute, and an executive might have a flag that determines if he can borrow the company jet on weekends. Attributes are separated from the class name by a line:

Specialization implies that name and salary are also executive and secretary attributes by inheritance. In C++ attributes are private or protected member variables that hold primitive values or instances of classes not appearing in the class diagram:

class Employee
{
protected:
   string name;
   double salary;
   // etc.
};

class Executive: public Employee
{
   bool canUseJet;
   Secretary *sec1, *sec2;
   // etc.
};

class Secretary: public Employee
{
   int speed; // in words/minute
   Executive *boss;
   // etc.
};

Services performed by class instances are shown by their names followed by an empty parameter list. (UML allows us to show parameters and return values, but we will usually resort to code before this stage.) They are listed below attributes, or, if there are no attributes shown, below the class name. For example, an employees might evaluate themselves. (This may not be true in the real world, but it is a typical responsibility of a computer representation of an employee). A secretary types documents and an executive makes decisions:

In C++ services are represented by public member functions:

class Employee
{
public:
   Report* evaluate();
protected:
   string name;
   double salary;
   // etc.
};

class Executive: public Employee
{
public:
   bool decide(Issue* issue);
private:
   bool canUseJet;
   Secretary *sec1, *sec2;
   // etc.
};

class Secretary: public Employee
{
public:
   void type(Document* doc);
private:
   int speed; // in words/minute
   Executive *boss;
   // etc.
};

Example

Let's turn the following description of an application domain into a class diagram:

An airline has a fleet of planes. There are two types of planes: commuter jets with one engine per wing, and jumbo jets with two engines per wing. Each plane has three designated pilots that take turns flying it. A pilot is designated to fly only two planes in the fleet.

Assume we determine that the important groups are pilots, airplanes, commuter jets, jumbo jets, wings, engines, and fleets. (These are just the nouns in the description.) We begin by drawing labeled boxes for each group:

Next, we ask what are the relationships between these classes? Clearly jumbo and commuter jets are special types of airplanes. Also, a fleet contains many airplanes:

A plane has two wings, and a wing has one or two engines. We could show these associations as part-whole relationships, but we will favor associations over filled diamond aggregations:

Finally, the relationship between pilots and planes is a 3-to-2 association:

What attributes and services should we add to our classes? None, unless more information is provided. Suppose we learn:

It is important to know the altitude and air speed of an airplane. We must also know the angle of a wing's flaps and the rotational speed of an engine. An airplane must be able to take off, fly, and land. Of course these procedures are different for jumbo jets and commuter jets. An engine must be able to start, throttle, and stop. We must be able to raise and lower a wing's flaps.

Here is our updated diagram:

Our model is complete. But what about the application? There is none. This is only a model of an application domain. We can imagine several applications within this domain: an air traffic control program, a flight control program, or an information management program used by an airline to track its planes and pilots. Part or all of this model could be refined into a system architecture for one of these applications. (Of course we would need to add supporting classes such as server proxies, database proxies, and user interface classes.)

Even in the absence of a concrete application, it's still a good exercise to translate our UML model into a C++ model. Let's begin with the engine class. We put the declaration in a header file called engine.h:

// engine.h
#ifndef ENGINE_H
#define ENGINE_H
class Wing; // forward reference

class Engine { ... };

#endif

An engine maintains a pointer to the wing it is attached to. At this early stage the start(), throttle(), and stop() functions only change the engine speed. Getter and setter functions are provided for the engine speed and associated wing:

class Engine
{
public:
   Engine(Wing* w = 0) { engineSpeed = 0; wing = w; }
   void start() { engineSpeed = 2500; }
   void throttle(double amt) { engineSpeed += amt; }
   void stop() { engineSpeed = 0; }
   double getEngineSpeed() const { return engineSpeed; }
   Wing* getWing() const { return wing; }
   void setWing(Wing* w) { wing = w; }
private:
   double engineSpeed; // in rotations/minute
   Wing* wing;
};

The declaration of the wing class belongs in a file called wing.h:

// wing.h
#ifndef WING_H
#define WING_H
#include "engine.h"
#include <stdexcept>
using namespace std;
class Airplane; // forward reference

class Wing { ... };

#endif

Wings are more complicated because there are two types: wings used by commuter jets have one engine, while jumbo jet wings have two. The wing type is determined by the numEngines attribute, which is set by the constructor from the number of non null engine parameters. The getter function for engine2 throws and exception if there is only one engine. Note that the wing passes itself to the setWing() function of each engine.

class Wing
{
public:
   Wing(Engine* eng, Engine* eng2 = 0)
   {
      engine = eng;
      engine->setWing(this);
      numEngines = 1;
      flapAngle = 0;
      if (eng2)
      {
         engine2 = eng2;
         engine2->setWing(this);
         numEngines++;
      }
   }
   void flaps(double amt) { flapAngle += amt; }
   Engine* getEngine() const { return engine; }
   Engine* getEngine2() const
   {
      if (numEngines != 2)
         throw exception("Only one engine");
      return engine2;
   }
   double getFlapAngle() const { return flapAngle; }
   int getNumEngines() const { return numEngines; }
   Airplane* getAirplane() const { return airplane; }
   void setAirplane(Airplane* plane) { airplane = plane; }
private:
   int numEngines;
   Engine *engine, *engine2;
   double flapAngle;
   Airplane* airplane;
};

The airplane class declaration belongs in airplane.h:

// airplane.h
#ifndef AIRPLANE_H
#define AIRPLANE_H
#include "wing.h"
class Pilot; // forward reference

class Airplane { ... };

#endif

Airplanes maintain pointers to their designated pilots and to their wings. A protected default constructor is declared to prevent clients from constructing generic airplanes. Instead, clients will have to declare jumbo jets or commuter jets. Alternatively, we could have declared takeoff(), fly(), and land() to be pure virtual member functions, which would have made Airplane an abstract class. We declare the attributes protected so they will be available to the derived classes.

class Airplane
{
public:
   double getAirSpeed() const { return airSpeed; }
   double getAltitude() const { return altitude; }

   Wing* getLeftWing() const { return leftWing; }
   Wing* getRightWing() const { return rightWing; }

   Pilot* getPilot1() const { return pilot1; }
   Pilot* getPilot2() const { return pilot2; }
   Pilot* getPilot3() const { return pilot3; }

   void setPilot1(Pilot* p) { pilot1 = p; }
   void setPilot2(Pilot* p) { pilot2 = p; }
   void setPilot3(Pilot* p) { pilot3 = p; }

protected:
   Airplane()
   {
      airSpeed = altitude = 0;
      pilot1 = pilot2 = pilot3 = 0;
   }
   Wing *leftWing, *rightWing;
   double altitude;
   double airSpeed;
   Pilot *pilot1, *pilot2, *pilot3;
};

Jumbo and commuter jet constructors throw exceptions if they are given the wrong types of wings. Airspeed and altitude are initialized by calling the protected airplane default constructor in the initializer lists. The jumbo jet class is declared in jumbo.h:

// jumbo.h
#ifndef JUMBO_H
#define JUMBO_H
#include "airplane.h"

class JumboJet: public Airplane
{
public:
   JumboJet(Wing *left, Wing *right)
      :Airplane()
   {
      if (left->getNumEngines() == 2 &&
          right->getNumEngines() == 2)
      {
         leftWing = left;
         left->setAirplane(this);
         rightWing = right;
         right->setAirplane(this);
      }
      else
         throw exception("wrong types of wings");
   }
   void land();
   void fly();
   void takeoff();
};

#endif

The commuter jet class is declared in commuter.h:

// commuter.h
#ifndef COMMUTER_H
#define COMMUTER_H
#include "airplane.h"

class CommuterJet: public Airplane
{
public:
   CommuterJet(Wing *left, Wing *right)
      :Airplane()
   {
      if (left->getNumEngines() == 1 &&
          right->getNumEngines() == 1)
      {
         leftWing = left;
         left->setAirplane(this);
         rightWing = right;
         right->setAirplane(this);
      }
      else
         throw exception("wrong types of wings");
   }
   void land();
   void fly();
   void takeoff();
};

#endif

The takeoff(), land(), and fly() member functions for commuter jets will be implemented in commuter.cpp. The jumbo jet versions will be defined in jumbo.cpp. It's too early to attempt real implementations at this stage, but for testing purposes we can provide callable stubs. For example:

// jumbo.cpp
#include "jumbo.h"
#include <iostream>
using namespace std;

void JumboJet::takeoff() { cout << "a jumbo jet is taking off\n"; }
void JumboJet::fly() { cout << "a jumbo jet is flying\n"; }
void JumboJet::land() { cout << "a jumbo jet is landing\n"; }

For now, pilots only have associated planes:

// pilot.h
#ifndef PILOT_H
#define PILOT_H
#include "airplane.h"

class Pilot
{
public:
   Airplane* getPlane1() const { return plane1; }
   void setPlane1(Airplane* a);
   Airplane* getPlane2() const { return plane2; }
   void setPlane2(Airplane* a);
private:
   Airplane* plane1;
   Airplane* plane2;
};

#endif

The implementations of setPlane1() and setPlane2() are a little awkward because they attempt to add the pilot to the list of pilots designated to fly the given plane. Under the current implementation, this involves assigning the pilot to the first non null pilot member variable encapsulated by the plane. If the plane already has three designated pilots, an exception is thrown. For example, we place the following implementation in pilot.cpp:

void Pilot::setPlane1(Airplane* a)
{
   if (!a->getPilot1())
      a->setPilot1(this);
   else if (!a->getPilot2())
      a->setPilot2(this);
   else if (!a->getPilot3())
      a->setPilot3(this);
   else
      throw exception("too many pilots");
}

A fleet is simply a list of airplanes:

// fleet.h
#ifndef FLEET_H
#define FLEET_H
#include "airplane.h"
#include <list>
using namespace std;

class Fleet: list<Airplane*>
{
public:
   void addPlane(Airplane* p) { push_back(p); }
   void remPlane(Airplane* p) { remove(p); }
};

#endif

Object Diagrams

An object diagram represents some or all of the objects in a running program. Objects are represented by boxes labeled by the underlined name and type of the object. The name and type are separated by a colon. If the object is anonymous, the name is left out. The box also contains the names and current values of all its primitive member variables.

Member variables containing pointers to other objects are shown as arrows connecting the objects.

For example, assume the program developed earlier will be instantiated to model a small airline:

Stormdoor airline has a fleet of two planes: a commuter jet and a jumbo jet. The airline has three pilots. Each pilot is designated to fly both planes, and each plane can be flown by all three pilots.

We could translate this description into the following C++ declarations:

// stormdoor.cpp
#include "jumbo.h"
#include "commuter.h"
#include "pilot.h"
#include "fleet.h"

int main()
{
   CommuterJet
      jet1(new Wing(new Engine()), new Wing(new Engine()));
   JumboJet
      jet2(new Wing(new Engine(), new Engine()),
          new Wing(new Engine(), new Engine()));

   Fleet fleet;
   fleet.addPlane(&jet1);
   fleet.addPlane(&jet2);
   
   Pilot p1, p2;
   p1.setPlane1(&jet1);
   p1.setPlane2(&jet2);
   p2.setPlane1(&jet1);
   p2.setPlane2(&jet2);

   // etc.

   return 0;
}

Here's an object diagram showing the fleet, pilots, and jet1:

Sequence Diagrams

A sequence diagram also shows the objects of a running program, but this time each object is represented by a vertical time line, where above means before. If object x calls y.f(), then a horizontal arrow labeled by f() is drawn from the time line of x to the time line of y. The return value, if it's important, is shown as a horizontal dashed arrow from y back to x. The return arrow is below the call arrow because it happens at a later point in time:

If y invokes its own member function, f(), then an arrow is drawn from the time line of y back to itself:

For example, assume we are given the following take off instructions for a commuter jet:

Start the left engine; start the right engine. Throttle the left engine; throttle the right engine. Raise the left wing flaps; raise the right wing flaps.

We can represent these instructions with the following sequence diagram:

Sequence diagrams are useful guides for implementing functions. Given more information, we could use this diagram to implement CommuterJet::takeoff() in commuter.cpp:

void CommuterJet::takeoff()
{
   Engine
      *leftEngine = leftWing->getEngine(),
      *rightEngine = rightWing->getEngine();
   leftEngine->start();
   rightEngine->start();
   leftEngine->throttle(500);
   rightEngine->throttle(500);
   leftWing->flaps(30);
   rightWing->flaps(30);
}

 

Problems

1.1 Problem

A class is taught in a school by a teacher. There are two types of classes: seminars and lectures. Any number of students may take a class, but a student may take no more than five classes per term. Teachers teach from two to four classes per term.

Draw a class diagram showing the relationships between schools, classes, teachers, students, seminars, and lectures. (Diagrams may be drawn by hand or by using a diagram editor.)

Faithfully convert your class diagram into C++ class declarations contained in separate header files (i.e., .h files). Implement member functions as stubs in separate source files (i.e., .cpp files). Prove your declarations are consistent by writing, building, and running a simple test driver.

1.2 Problem

A warehouse has any number of aisles, an aisle has any number of bins, a bin has any number of boxes, and a box contains any number of items. There are three types of items: spoons, moose heads, and comic books.

Draw a class diagram showing the relationships between warehouses, aisles, bins, boxes, items, spoons, moose heads, and comic books.

Faithfully convert your class diagram into C++ class declarations contained in separate header files. Implement member functions as stubs in separate source files. Prove your declarations are consistent by writing, building, and running a simple test driver.

1.3 Problem

A play has many characters. A play occurs on a stage and has three acts. Each act has three scenes. A character may be played by many different actors, and an actor may play different characters.

Draw a class diagram showing the relationships between plays, stages, acts, scenes, characters, and actors.

Faithfully convert your class diagram into C++ class declarations contained in separate header files. Implement member functions as stubs in separate source files. Prove your declarations are consistent by writing, building, and running a simple test driver.

1.4 Problem

A tennis tournament has many matches. Each match is between two players and consists of six or seven games. Each game consists of six or seven sets, and each set consists of five or more points.

Draw a class diagram showing the relationship between tournaments, matches, games, sets, points, and players.

Faithfully convert your class diagram into C++ class declarations contained in separate header files. Implement member functions as stubs in separate source files. Prove your declarations are consistent by writing, building, and running a simple test driver.

1.5 Problem

A C++ program is a sequence of declarations:

   PROGRAM ::= DECLARATION ...

Besides declarations, there are two other types of C++ statements: expressions and control structures:

   STATEMENT ::= DECLARATION | EXPRESSION | CONTROL

There are four types of control structures: conditional (if-else, switch), iterative (for, do-while, and while), jump (break, continue, goto, return), and block.

   CONTROL ::= CONDITIONAL | ITERATIVE | JUMP | BLOCK

A block is a sequence of statements between curly braces:

   BLOCK ::= { STATEMENT ... }

An if-else statement consists of an expression (the condition), and one or two statements (the consequent and the alternative):

   IF ::= if (EXPRESSION) STATEMENT [else STATEMENT]

A while or do-while statement consists of a condition (the loop condition) and a statement (the iterate):

   WHILE ::= while (EXPRESSION) STATEMENT
   DO ::= do STATEMENT while (EXPRESSION);

Draw a class diagram showing the relationships between program, statement, expression, declaration, control structure, block, conditional, if-else, switch, iterative, for, while, do-while, jump, break, continue, goto, and return.

Faithfully convert your class diagram into C++ class declarations contained in separate header files. Implement member functions as stubs in separate source files. Prove your declarations are consistent by writing, building, and running a simple test driver. (Essentially, instances of the program class are parse trees.)

1.6 Problem

Complete the class diagram of the previous problem by including the classes derived from expression and declaration. See [STR] for guidance.

1.7 Problem

A hospital has many patients. Each patient has one doctor, although a doctor may have several patients. Tests are performed on each patient resulting in many measurements that must be recorded in a data base. In some cases the measurements can be complicated data structures. Examples of measurements include blood pressure, temperature, and pulse. It's important to know the time of a measurement.

Draw a class diagram showing the relationships between hospital, doctor, patient, measurement, blood pressure, temperature, pulse, and time.

Faithfully convert your class diagram into C++ class declarations contained in separate header files. Implement member functions as stubs in separate source files. Prove your declarations are consistent by writing, building, and running a simple test driver. (See [FOW2] for reusable domain models related to observations and measurements.)

1.8 Problem

Assume the following C++ class declarations have been made:

class A { public: virtual void f() = 0; ... };
class B: public A { A* a; ... };
class C: public A { A* a; ... };
class D { list<A*> as; ... }; // list<> is an STL container
class E: public B, public C { D* d; ... };

Draw a class diagram showing the relationship between A, B, C, D, and E.

1.9 Problem

A foundation class is a class that is normally not the base class of another class. Instances of foundation classes represent common objects that appear in a many application domains without much variation. Examples of foundation classes include date, place, person, phone number, and name.

With reuse in mind, draw a class diagram showing the relationship between person, place (current address, nationality, citizenship), time (date of birth), phone number, and name. Show properties in your classes. For example, the properties of time include second, minute, hour, day, month, and year.

Faithfully convert your class diagram into C++ class declarations contained in separate header files. No member functions are required at this time, just show private member variables. (Why not protected member variables?) Assume associations are represented as pointers and strings are represented by the standard string class.

1.10. Problem

Spend fifteen minutes familiarizing yourself with Java class declarations. They are syntactically and semantically similar to C++ class declarations (except Java doesn't permit global variables and functions), and all documentation on the subject can be found at [WWW 7]. UML diagrams are language-independent. Repeat the last problem using Java. Java doesn't have pointers, so assume associations are represented as references and strings are represented using Java's String class.

 

1.15. Problem: The Transaction Pattern [COAD]

A transaction has zero or one subsequent transactions. A transaction occurs in a place, may have one or more participants, and may involve one or more items transacted. For example, a sale consists of three transactions: select an item, followed by send an invoice, followed by ship the item. The participants are a customer and a salesman. The transactions take place in a store. Assume the items can be bananas or tangerines.

Draw a class diagram showing the relationships between transaction, select an item, send an invoice, ship the item, items, bananas, tangerines, participants, salesmen, customers, places, and stores.

1.16. Problem: The Actor-Participant Pattern [COAD]

Sometimes a person can participate in a transaction in different ways. (A man can wear many hats.) For example, a person might be an employee, a customer, and a supplier of the same business. For this reason it is a good idea to separate participants from actors, where an actor can be a person or organization. Actor properties include personal information like name and address. Usually an organization has an associated contact person. Participants have properties relevant for the way of participating. For example, an employee has a salary property, while a customer has amount owed and amount purchased properties. Draw a class diagram showing the relationships between transactions, participants, employees, suppliers, customers, actors, organizations, and persons.

1.17. Problem

A folder may contains files. These files may be documents, applications, or other folders.

Draw a class diagram showing the relationships between files, folders, and documents. (This is an instance of the Composite pattern, which is discussed in [Go4].)

1.18. Problem

A tree has two types of nodes: parents and leafs. A parent node has one or more nodes below it (called the child nodes). A child node may be a leaf or a parent. A leaf node has no children.

Draw a class diagram showing the relationships between parent, node, and leaf. (This is an instance of the Composite pattern, which is discussed in [Go4].)

1.19. Problem

A simple programming language has three types of expressions: literals, symbols, and operations:

EXPRESSION ::= LITERAL | SYMBOL | OPERATION

There are two types of operations: infix and prefix:

OPEARATION ::= INFIX | PREFIX

An infix operation consists of two expressions separated by an operator symbol:

INFIX ::= EXPRESSION OPERATOR EXPRESSION

For example: 42 + x. A prefix expression consists of an operator followed by an expression:

PREFIX ::= OPERATOR EXPRESSION

For example: -42.

Draw a class diagram showing the relationships between expression, literal, symbol, operation, infix operation, and prefix operation. (This is an instance of the Composite pattern, which is discussed in [Go4].)

1.20. Problem: The Proxy Pattern [Go4]

A proxy implements the same interface as a server. A proxy performs some extra service such as security check, caching recent results, or maintaining usage statistics, then delegates the clients request to another object that implements the server's interface. This might be the server or another server proxy.

Draw a class diagram showing the relationships between proxy, server, client, and interface. (This is an instance of the Proxy Pattern, which is discussed in [Go4] and [POSA].)

1.21. Problem

A foreign diplomat sends a message to an agent. Either the agent is a diplomat or a translator who translates the message from one language to another, then forwards the translated message to another agent.

Draw a class diagram showing the relationships between diplomats, agents, and translators.

1.22. Problem

An Indian diplomat sends a message in Hindi to an agent who translates the message to German and sends it to another agent who translates the message to Arabic. This agent sends the message to another agent who translates it to Spanish, then sends the message to a Mexican diplomat. The Mexican diplomat reads the message, then sends a reply back through the same chain of translators.

Draw a UML sequence diagram showing the sequence of events.

1.23. Problem

An application running on host A sends a message to an application running on Host B. The message is first sent from the application layer on Host A to the Transport layer, where the message is broken into packets. The transport layer sends the packets to the network layer, which determines the route the packets will take. The network layer sends the packets to the data link layer, which breaks the packets into frames to be sent to the first hop on the route selected by the network layer. The data link layer sends the frames to the physical layer, which actually sends the frames to the next hop. Assume the message from A to B will be routed through Host C.

Draw a UML sequence diagram showing the events that will occur.

1.24. Problem: A UML Meta Model [FOW1]

A class diagram consists of many classes and relationships. A relationship defines a connection between two classes. There are two types of relationships: generalization and association. A third type of relationship, aggregation, is a special type of association. There are two types od aggregation: hollow diamond and solid diamond.

Draw a class diagram showing the relationships between diagram, class, relationship, association, generalization, aggregation, hollow diamond, and solid diamond.