UML Class Diagrams

Objects and Classes

In UML classes are represented by icons of the form:

A class diagram shows classes and dependencies between classes.

A class diagram has three different interpretations depending on the development phase. In the analysis phase classes are concepts. In the design phase classes are interfaces. In the implementation phase classes are implementation classes.

Here is an example of a class diagram adapted from [FOW-2]:

In general, a dependency from class A to class B indicates that changes to A may cause changes to B. Dependencies are indicated by dashed arrows. For example, a View window depends on the Document it displays:

There are three special types of dependencies: associations, generalizations (specializations), and realizations (implementations).

Associations

Assume a certain school employs four teachers: Teacher = {a, b, c, d}. Assume this school offers six courses: Course = {x1, x2, x3, x4, x5, x6}. There are 24 possible pairs of teachers and courses (i.e., |Teacher x Course| = 4 * 6 = 24). In a particular semester the teaches relationship contains some subset of these 24 pairs:

teaches = {(a, x1), (a, x2), (b, x3), (b, x4), (c, x1), (c, x6)} Notice that each teacher teaches exactly two courses. Assume this is a contractual obligation. Notice that each course has zero or more teachers (a and c both teach x1, and no one is teaching x5 this semester). We may assume teachers know which courses they teach, but assume we can't find out who teaches a given course from the course description alone.

If we think of Teacher and Course as classes, then in UML terminology the teaches relationship is called an association and the pairs in the teaches relationship are called links or association instances.

In a class diagram an association is represented by a line segment connecting two class icons. The line segment may be labeled by the name of the association. Each end of the line segment is called a role. A role may be decorated by a name, a multiplicity, and/or an arrowhead:

Unless otherwise specified, the name of the role is the name of the class it connects to. Multiplicity is a range of integers: m..n. It indicates the number of instances of the class that may be linked to one instance of the other class. A barbed arrowhead indicates navigability, the ability to find out what object another object is linked to.

In C++ a link might be represented as a pointer:

// from teacher.h
class Teacher
{
public:
    Teacher(Course* c1 = 0, Course* c2 = 0)
    {
        course1 = c1; course2 = c2;
    }
    // getters:
    Course* getCourse1() { return course1; }
    Course* getCourse2() { return course2; }
    // setters:
    void setCourse1(Course* c) { course1 = c; }
    void setCourse2(Course* c) { course2 = c; }
    // etc.
private:
    Course *course1, *course2;
    // etc.
};

// from course.h
class Course
{
public:
    void addTeacher(Teacher* t) { taughtBy.push_back(t); }
    void remTeacher(Teacher* t) { taughtBy.remove(t); }
    list<Teacher*> getTeachers() { return taughtBy; }
    // etc.
private:
    list<Teacher*> taughtBy;
    // etc.
};

Here are a few more examples:

Aggregation and Composition

Aggregation is a special type of association, and composition is a special type of aggregation.

Aggregation is the relationship between a whole and its parts. Composition adds the restriction that the parts cannot be parts of more than one whole at a time. Also, a composition is responsible for the creation, management, and destruction of its parts.

Aggregation is indicated by a hollow diamond on the aggregate end of the association. Composition is indicated by a solid diamond on the composite side of the association. Here are a few examples:

Generalization/Specialization

A certain company has 5 employees: Employee = {e1, e2, e3, e4, e5}. Every employee has a name, salary, and social security number. Three of the employees are secretaries: Secretary = {e1, e2, e3}, the other two are executives: Executive = {e4, e5}. In addition to name, salary, and social security number, secretaries are employees with secretarial skills such as typing. In addition to name, salary, and social security number, executives are employees with decision making skills.

If we think of Employee, Secretary, and Executive as classes, then the subset relationship between Secretary and Employee and the subset relationship between Executive and Employee are called generalizations. In UML notation a generalization is an arrow with a triangular head:

In C++ generalization can be expressed by base classes:

class Employee { ... };
class Secretary: public Employee { ... };
class Executive: public Employee { ... };
Realizations

A realization is the relationship between an interface and a class that implements it. This doesn't have too much meaning during the analysis phase, but during the design and implementation phase, interfaces and realizations play an important role.

An interface(or ADT) is a class without attributes or operation implementations. To put it another way, an interface is merely a collection of operation signatures (a.k.a. headers, prototypes). Although an object must instantiate a proper class, an object handle (i.e., a pointer, reference, OID, etc.) need only be associated with an interface. Thus, a client can write programs that perform operations on a handle without knowing exactly what class of object the handle is currently attached to. Indeed, the same handle may be attached to objects of different classes at different times during the execution of the program. Not only is this a wonderful abstraction mechanism (i.e., the interface is separated from the implementation), it is also allows programmers to write polymorphic programs (i.e., the behavior of the handle can only be known at runtime, when the object the handle is attached to is known.)

Java allows programmers to explicitly declare interfaces:

interface Airship
{
    void takeoff();
    void fly();
    void land();
}
A given interface may have several implementation classes: class Plane implements Airship
{
    void takeoff() { ... }
    void fly() { ... }
    void land() { ... }
}

class Blimp implements Airship
{
    void takeoff() { ... }
    void fly() { ... }
    void land() { ... }
}

Clients may fly airships without knowing the exact type of airship: Airship a;
a = new Blimp();
a.takeoff();
a.fly();
a.land();
a = new Plane();
a.takeoff();
a.fly();
a.land();
We can achieve the same effect in C++ using pure virtual functions and abstract classes: class Airship // an interface
{
public:
    virtual void takeoff() = 0;
    virtual void fly() = 0;
    virtual void land() = 0;
};
Implementations are given by derived classes: class Plane: public Airship
{
public:
    void takeoff() { ... }
    void fly() { ... }
    void land() { ... }
};

class Blimp: public Airship
{
public:
    void takeoff() { ... }
    void fly() { ... }
    void land() { ... }
};

Instead of Java references, we must use C++ pointers: Airship* a;
a = new Blimp();
a->takeoff();
a->fly();
a->land();
a = new Plane();
a->takeoff();
a->fly();
a->land();
UML doesn't have a special notation for interfaces, so we must use stereotyped classes. Realization is represented by a dashed generalization arrow:

Meta Classes, Power Classes, and Stereotypes

Recall that if A is a set:

A = {a, b, c} The P(A), the power set of A, is the set of all subsets of A: P(A) = {{}, {a}, {b}, {c}, {a, b}, {a, c}, {b, c}, {a, b, c}} Instances of a power class are also classes. Class, PresentationClass, AbstractClass, FoundationClass, ControlClass, BusinessClass are power classes. We can think of power classes as formalizations of stereotypes.

The UML Meta Model

Meta classes are a little more general than power classes. Anything in the solution domain may be an instance of a meta class. For example, we can think of all the UML elements we have seen as instances of various meta classes. A class diagram made up of meta classes is called a meta model: