In this section we will look at generalization specialization, the "is a" connection. We will look at "what is generalization specialization?", how to identify generalization specialization, how to show generalization specialization on a class diagram, how to specify generalization specialization, and how to code generalization specialization.
A generalization specialization relationship indicates a commonality and similarity between classes. It indicates that a superclass (generalization class) and subclass (specialization class) have common attributes, operations, and relationships. A superclass has the most general attributes, operations, and relationships that may be shared with subclasses. A subclass is a specialization of a superclass. A subclass may have more specialized attributes and operations. Generalization specialization indicates an "is a" or "type of" relationship. For example, a Vehicle superclass defines common attributes, operations, and relationships for a Car subclass. A car "is a" vehicle as shown in the figure below.
The generalization specialization relationship is implemented in object-oriented programming languages with inheritance. Inheritance is the implementation mechanism for the generalization specialization relationship. In single inheritance a subclass has only one superclass. In multiple inheritance a subclass has two or more superclasses. Inheritance is the mechanism used in object-oriented programming (OOP) languages to permit a subclass to share attributes and operations defined in the superclasses. For example the subclass Car can share the attributes and operations from the superclass Vehicle. The subclass Car may add attributes and operations. The subclass Car may redefine attributes and operations. Only a few OOP languages, like Smalltalk and Eiffel permit redefining attributes. C++ does not permit redefining attributes. In C++ refine attributes in subclasses with constraints, e.g. value < = maximumValue. It is very common to redefine operations. As shown on the class diagram below, the operations start and stop are redefined in the subclasses. These redefined operations are called polymorphic operations. Polymorphism means "one name many forms". In O-O modeling this means that a superclass and subclass have the same named operation but with different implementations. For example, the polymorphic operation "start" is in the Vehicle superclass and the Car subclass. "Start" has a different implementation in the Vehicle superclass and the Car subclass. "Start" is redefined in the subclass Car with a different implementation. The generalization specialization symbol is a triangle on a solid line as shown in the figure below.
The diagram in the figure above shows the Vehicle superclass and the Car and Truck subclasses. The superclass attribute speed is inherited by the subclasses. There are two polymorphic operations in the superclass, start and stop. These two operations are initially defined in the superclass, then redefined in the subclasses. There should be a different implementation of each of these operations in the subclasses. There is the added operation "operate" in the subclasses.
Identify generalization specialization by looking
for superclasses and subclasses. Our goal is the effective use
of generalization specialization for reusability, code sharing
and extendibility. The following are suggested steps to identify
generalization specialization relationships:
Step 1. Make a drawing or physical representation,
e.g. a car, a motor, a passenger.
Step 2. Identify the classes, e.g. Car, Motor, Passenger
Step 3. For each class, identify generalization specialization
relationships with the questions "As a superclass what are
the subclasses?" and "As a subclass what are the superclasses?"
Step 4. Ask the following questions. "What are
the general attributes, operations, and exceptions that may be
shared (inherited) in subclasses?" " What are the specific
attributes and operations that ought to be refined, added, or
removed from the subclasses?" "Is there a single or
multiple superclasses?", "Is association more appropriate
than generalization specialization?" "Is there a single
"cosmic" superclass named Object?"
Step 5. Identify polymorphic (same name) operations.
For example, a superclass and subclass may have an operation named
"start ()". The implementation of the start operation
is refined and specialized in the subclass.
Step 6. For each generalization specialization relationship, identify constraints (limitations or rules) for the relationship.
classes, class specifications, and prototype.
Step 7. Create or update the class diagram, data dictionary listing classes, class specifications, and prototype.
Generalization specialization is shown on a class
diagram by connecting superclasses and subclasses with the generalization
specialization symbol. The symbol in the Rumbaugh OMT is a triangle.
An example of a class diagram showing generalization specialization
using a CASE tool is the previous class diagram.
The steps to create the class diagram showing generalization
specialization using a CASE tool are as follows:
>> Run the CASE tool and show the tool bar or pull down menu.
>> Select and place a class symbol for the superclass.
>> Select and place a class symbol for the subclass.
>> Select the generalization specialization
relationship symbol and connect the two classes.
The following relationship specification report holds information on each generalization relationship. This report was created in a text editor.
- Source Class states initial class to define the relationship, e.g. Vehicle.
- Destination Class states the second class to define the relationship, e.g. Car.
- Type of Relationship states the type of relationship, e.g. generalization.
- Implementation states the language dependent implementation of the relationship, e.g. C++ public inheritance.
- Remarks states any additional information about the generalization relationship.
Once the class diagram has been completed, then C++
code can be automatically generated with a CASE tool. The CASE
tool implements the generalization specialization shown on the
class diagram. We are briefly presenting how to code generalization
specialization in C++ to be able to understand and modify the
generated C++. Generalization specialization is coded in C++ by
declaring base classes (superclasses) and derived classes (subclasses).
The following is the comparison of the O-O modeling terms and
C++ terms for generalization specialization.
Table Comparison of O-O modeling and C++ terms for generalization specialization.
---------------------------------------------------------------------
O-O Modeling C++
---------------------------------------------------------------------
superclass base class
subclass derived class
polymorphic operation virtual function or pure virtual function
---------------------------------------------------------------------
The general form of the base class in C++ is as follows:
General Form Car Example
class <base class name> class Vehicle
{ {
<base class members> public:
virtual void start ();
}; };
The general form of the derived class in C++ is as
follows:
General Form Car Example
class <derived class name> class Car : public Vehicle
:public <base class name>
{ {
<derived class members> public:
void start ();//polymorphic
}; };
The access of base class data and function members
may be public, protected, or private. A protected data or function
member is visible (accessible) within its class and from any of
its subclasses. The declaration of a derived class includes indicates
public <base class name>. This means that objects of the
derived class may access public members of the superclasses. Based
upon the diagram in the figure below above, the listing below
is the C++ code generated from a CASE tool.
Listing Generated C++
showing generalization specialization.
------------------------Class Declaration - vehicle.h --------------
#ifndef __VEHICLE_H
#define __VEHICLE_H
class Vehicle {
int speed;
public:
virtual void stop () = 0;
virtual void start () = 0;
Vehicle(){}
~Vehicle(){}
};
#endif
--------------Class Declaration - truck.h -------------------------
#ifndef __TRUCK_H
#define __TRUCK_H
#ifndef __VEHICLE_H
#include "Vehicle.h"
#endif
class Truck : public Vehicle {
public:
Truck(){}
~Truck(){}
virtual void stop ();
virtual void start ();
void operate (int aSpeed);
};
#endif
-------------------Class Declaration - car.h -----------------------
#ifndef __CAR_H
#define __CAR_H
#ifndef __VEHICLE_H
#include "Vehicle.h"
#endif
class Car : public Vehicle {
public:
Car(){}
~Car(){}
virtual void stop ();
virtual void start ();
void operate (int aSpeed);
};
#endif
-------Vehicle Class Function Definitions - vehicle.cpp------------
#include "Vehicle.h"
void Vehicle::start (){
};
void Vehicle::stop (){
};
------------Truck Class Function Definitions - truck.cpp------------
#include "Truck.h"
#include <iostream.h>
void Truck::start () {
cout << "Truck is started.\n";
}
void Truck::operate (int aSpeed) {
cout << "Truck is operating at " << aSpeed << " mph\n";
}
void Truck::stop () {
cout << "Truck is stopped.\n";
}
--------------Car Class Function Definitions
- car.cpp--------------
#include "Car.h"
#include <iostream.h>
void Car::start () {
cout << "Car is started.\n";
}
void Car::operate (int aSpeed) {
cout << "Car is operating at " << aSpeed << " mph\n";
}
void Car::stop () {
cout << "Car is stopped.\n";
}
--------------Main Function Definition - main.cpp------------------
#include "car.h"
#include "truck.h"
int main ()
{
Car car1;
car1.start ();
car1.operate (55);
car1.stop ();
Truck truck1;
truck1.start ();
truck1.operate (40);
truck1.stop ();
return 0;
}
---------------------------Sample Output-----------------------------
Car is started.
Car is operating at 55 mph.
Car is stopped.
Truck is started.
Truck is operating at 40 mph.
Truck is stopped.
In this example the CASE tool generated the class
declarations for the Vehicle class, Car class, and Truck class.
The Vehicle class is the base class (superclass) and the Car and
Truck classes are the derived classes (subclasses). The Vehicle
class has two virtual function members for the polymorphic operations
start and stop. Virtual means that the function member may be
redefined in the derived classes. The Vehicle superclass (C++
base class) is an abstract class. This means that there can be
no objects of the Vehicle class. In C++ an abstract class has
at least one pure virtual function such as virtual
void start () = 0. Abstract classes are useful
for extendibility. When you have a superclass that is not an abstract
class you may have objects of this superclass. When you need to
change the superclass so as to make a change in the objects, you
may create undesirable side-effects in objects of subclasses.
In this example, the Car and Truck classes are concrete classes.
A concrete class has no pure virtual functions and may have objects.
The figure below provides another example of generalization
specialization. The Passenger superclass has two subclasses: AdultPassenger
and ChildPassenger. The Passenger superclass has two polymorphic
operations: fastenSeatBelt and unfastenSeatBelt. These operations
are redefined in the subclasses. For example, the fastenSeatBelt
operation is different for an adult passenger or a child passenger.
A child passenger requires greater assistance and instructions
to fasten the seat belt than an adult passenger. Notice that the
ChildPassenger class has an additional attribute "age"
that may be used to determine if additional assistance is required.
The generated C++ code is shown in the listing below.
Listing Generated C++
Base and Derived Classes
-----------------Passenger Class Declaration - passenge.h ----------
enum Boolean {OK, NotOK};
class Passenger { //Base class
public:
virtual Boolean unfastenSeatBelt (); //Virtual (polymorphic)
//function
virtual Boolean fastenSeatBelt (); //Virtual (polymorphic)
//function
Passenger(){} //Constructor
~Passenger(){} //Destructor
};
-----------AdultPassenger Class Declaration - adultpas.h -----------
#include "Passenge.h" //Include directive for base class
class AdultPassenger : public Passenger { //Derived class
public:
Boolean unfastenSeatBelt (); //polymorphic function
Boolean fastenSeatBelt (); //polymorphic function
AdultPassenger(){} //Constructor
~AdultPassenger(){} //Destructor
};
-----------ChildPassenger Class Declaration - childpas.h -----------
#include "Passenge.h" //Include directive for base class
class ChildPassenger : public Passenger { //Derived class
int age;
public:
Boolean unfastenSeatBelt (); //polymorphic function
//Refined function from the base class function
//to indicate requires adult supervision
Boolean fastenSeatBelt (); //polymorphic function
//Refined function from the base class function
//to indicate requires adult supervision
ChildPassenger(){} //Constructor
~ChildPassenger(){} //Destructor
};
In this example the CASE tool generated the class declarations for the passenger base class and for the adult passenger and child passenger derived classes. It provided virtual member functions for the two polymorphic operations, "fasten seat belt" and "unfasten seat belt". These functions are refined to indicate requires adult supervision.
In this example of base and derived classes, the Vehicle class has two virtual function, e.g. start and stop. These are redefined in the derived Car class. The Vehicle class has a single pure virtual function, operate. This makes the Vehicle class an abstract base class. You cannot create an object of an abstract base class like the Vehicle class.
Listing C++ listing showing
base and derived classes.
//////////////////////////Vehicle.h file////////////////////////////////////
#ifndef __VEHICLE_H
#define __VEHICLE_H
class Vehicle
{ float weight; //Attribute data member
public:
Vehicle () : weight(0) { }
virtual void start () ;
virtual void operate (int aSpeed) = 0;
virtual void stop () ;
virtual ~ Vehicle ( ) { } //Destructor
};
#endif
//////////////////////////Car.h file////////////////////////////////////
#ifndef __CAR_H
#define __CAR_H
#ifndef __VEHICLE_H
#include "Vehicle.h"
#endif
class Car : public Vehicle
{ int speed; //Attribute data member
public:
Car () : speed(0) { }
void start () ;
void operate (int aSpeed) ;
void stop () ;
~ Car ( ) { } //Destructor
};
#endif
//////////////////////////Vehicle.cpp file////////////////////////////////////
#include "Vehicle.h"
void Vehicle::start() {
}
void Vehicle::operate(int aSpeed) {
}
void Vehicle::stop() {
}
//////////////////////////Car.cpp file////////////////////////////////////
#include "Car.h"
void Car::start() {
}
void Car::operate(int aSpeed) {
}
void Car::stop() {
}
//////////////////////////main.cpp file////////////////////////////////////
#include "Car.h"
int main ()
{
Car car1; //invokes the default constructor
car1.start();
car1.operate (55);
car1.stop();
return 0;
}
To generate the C++ class declaration with data member
declarations, we used two scripts. A class declaration script
generated the Car.h file and a function definition script generated
the Car.cpp file. We created the main.cpp manually. The two scripts
are shown below.
//////////////////////////TRUNCATE_EIGHT$CLASS_NAME.h
file////////////////////////////////////
#ifndef __$CAPITALIZE_ALL$TRUNCATE_EIGHT$CLASS_NAME$_H
#define __$CAPITALIZE_ALL$TRUNCATE_EIGHT$CLASS_NAME$_H
[
#ifndef __$CAPITALIZE_ALL$TRUNCATE_EIGHT$BASE_CLASS$_H
#include "TRUNCATE_EIGHT$BASE_CLASS$.h"
#endif
]
class CLASS_NAME[NO_RETURN NO_REPEAT: NO_REPEAT public BASE_CLASS ,DELETE_LAST_SYMBOL] CLASS_LIBRARY_BASE_CLASS
{
[CPP_ATTRIBUTE_STATIC CPP_ATTRIBUTE_CONSTANT
ATTRIBUTE_TYPE ATTRIBUTE_NAME$; //Attribute data member]
public:
CLASS_NAME ()
SELECT_WHEN LOGICAL_NOT ATTRIBUTE_IS_STATIC [NO_RETURN NO_REPEAT: ATTRIBUTE_NAME(ATTRIBUTE_INITIAL_VALUE),DELETE_LAST_SYMBOL]
{
}
[ CPP_OPERATION_VIRTUAL CPP_OPERATION_STATIC OPERATION_RETURN_TYPE OPERATION_NAME (CPP_OPERATION_PARAMETERS) CPP_OPERATION_CONSTANT CPP_OPERATION_PURE_VIRTUAL;
]
OPERATION_CPP_VIRTUAL_BASE_CLASS ~ CLASS_NAME ( ) { } //Destructor
};
#endif
//////////////////////////TRUNCATE_EIGHT$CLASS_NAME.cpp file////////////////////////////////////
#include "TRUNCATE_EIGHT$CLASS_NAME.h"
[
OPERATION_RETURN_TYPE CLASS_NAME::OPERATION_NAME(CPP_OPERATION_PARAMETERS) CPP_OPERATION_CONSTANT
{ OPERATION_CODE
}]
To code C++ base and derived classes, create class
declarations and function definitions as shown in the diagrams
below. Key aspects of the class declaration are shown on the left.
This information class diagram shows that a base class declaration
should have a class name, data members, a constructor, function
members, virtual function members, pure virtual function members,
and a destructor. It shows that the derived class have an include
statement to include the base class header file and redefined
virtual function members. The class diagram on the right shows
sample C++ identifiers that are also in the code listings.
Key aspects of coding a base class are:
- Create #ifdefs, #define, and #endif compiler directives to avoid multiple class declarations, e.g. #define __VEHICLE_H.
- Declare the base class, e.g. class Vehicle
- Declare pure virtual function members, e.g. virtual operate (int aSpeed) = 0; Pure virtual function must be defined in derived classes and makes the base class an abstract base class. No objects of an abstract base class may be created.
- Declare virtual function members, e.g. virtual void start (); Virtual functions may be redefined in derived class.
- Define a virtual default destructor to deinitialize
an object, virtual ~Vehicle() {}.
Key aspects of coding a derived class are:
- Create #ifdefs, #define, and #endif compiler directives to avoid multiple class declarations, e.g. #define __CAR_H.
- Declare the derived class, e.g. class Car : public Vehicle
- Define the pure virtual and virtual function members, e.g. void start ();
- Define a default constructor to initialize an object with default data member values in the initializer list. Base class default constructor is invoked automatically. Car () { }
- Define a constructor with arguments to initialize an object with passed values, e.g. Car(int aSpeed) : speed(aSpeed), Vehicle() {}
- Define a copy constructor to initialize an object with a copy of another object. Must explicitly invoke the base class copy constructor. Car (const Car& aCar) : Vehicle (aCar)
- Define an assignment operator (operator=) to assign one object to another. Must explicitly invoke the base class assignment operator. Car& operator= (const Car& aCar) { Vehicle::operator= (aCar); }
- Define an equality operator (operator==) to compare two objects. Must explicitly invoke the base class equality operator. int operator== (const Car& aCar) { Vehicle::operator== (aCar); }
- Define input function - C++ insertion operator for use with cin. Must explicitly invoke the base class operator>>, e.g. friend istream& operator>> (istream& is, Car& aCar) { is >> (Vehicle&) aCar; }
- Define the output function - C++ extraction operator for use with cout. Must explicitly invoke the base class operator<<, e.g. friend ostream& operator<< (ostream& os, Car& aCar) { os << (Vehicle&) aCar; }
- Define a default destructor to deinitialize an object, ~Car () {}
- Define a main (test) function to create an object
of the derived class and invoke a virtual function
The complete C++ code listing is below.
Listing C++ Code Listing for Base and Derived Classes
// Class: Car //ANSI C++
#ifndef __CAR_H //Required for Car class
#define __CAR_H
#ifndef __IOSTREAM_H //Required for cin and cout
#include <iostream.h>
#endif
#ifndef __CSTRING_H //Required for CString class
#include <CString.h>
#endif
#ifndef __VEHICLE_H //Required for Vehicle class
#include "Vehicle.h"
#endif
class Car : public Vehicle
{ int speed; //Attribute data member
public:
//Default constructor alternative to compiler provided default constructor
//Ensure correct initial values
//Initialization list has members in the order declared
//Association object data member pointers initialized to null association object
Car ();
//Constructor with arguments
//Update to argument list to initialize base class data members,
//e.g. (int aNumber) : BaseClass (aNumber)
Car ( int aspeed ) ;
//Copy Constructor
Car (const Car& aCar );
//Operator= Assignment Operator
Car& operator= (const Car& aCar);
//Operator== Equality Operator
int operator== (const Car& aCar) const;
//Operator<< for cout
friend ostream& operator<< (ostream&
os, const Car& aCar);
//Operator>> for cin
friend istream& operator>> (istream&
is, Car& aCar);
//Get accessor function for non-static attribute data member
int getspeed() const
{ return speed;
}
//Set accessor function for non-static attribute data member
void setspeed (const int aspeed)
{ speed = aspeed;
}
void start () ;
void operate (int aSpeed) ;
void stop () ;
~ Car ( ) { } //Destructor - Delete any pointer data members that used new in constructors
//Destructor should be virtual if and only if class contains at least one virtual function
//Objects destroyed in the reverse order of the construction order
};
#endif
//////////////////////////.cpp file/////////////////////////////////////////////////////
#include "Car.h"
void Car::start() {
}
void Car::operate(int aSpeed) {
}
void Car::stop() {
}
//Default constructor alternative to compiler provided default constructor
Car::Car () : speed(0) {
}
//Constructor with arguments
//Update to argument list to initialize base class data members and const data members
//e.g. (int aNumber) : BaseClass (aNumber) , constMinimumValue (aMinimumValue)
Car::Car ( int aspeed )
: speed(aspeed), Vehicle()
{ speed = aspeed; //Initialization of attributes
}
//Copy constructor alternative to compiler provided default copy constructor
Car::Car (const Car& aCar ) : Vehicle (aCar)
{ speed = aCar.speed;
}
//Operator= Assignment Operator alternative to compiler provided operator=
Car& Car::operator= (const Car& aCar)
{ if (this == &aCar) return *this;
Vehicle::operator= (aCar);
speed = aCar.speed;
return *this;
}
//Operator== Equality Operator
int Car::operator== (const Car& aCar) const
{ return (
(Vehicle::operator== (aCar)) &&
(speed == aCar.speed)
);
}
//Operator<< extraction for cout
ostream& operator<< (ostream& os, const Car& aCar)
{ os << (Vehicle &) aCar;
os << "Object Attribute Values - Class Car" << endl;
os << "speed: " << aCar.speed << endl;
return os;
}
//Operator>> insertion for cin
istream& operator>> (istream& is, Car& aCar)
{ is >> (Vehicle &) aCar;
cout << "\nEnter Object Attribute Values or 0 - Class Car";
cout << "\nEnter speed : " << endl;
is >> aCar.speed;
return is;
}
With multiple inheritance a subclass can have two or more superclasses. In multiple inheritance attribute and operation name clashes (ambiguities) must be resolved. Repeated inheritance occurs when subclasses have a common superclass. A virtual base class is used in C++. Multiple inheritance should be used with caution. Investigate the use of association or aggregation in place of multiple inheritance.
Listing C++ code showing
multiple inheritance.
// LibraryTruck Class in C++
#include <iostream.h>
//----------------------------------------------------
class ServiceItem {//Virtual Base Class that may be
private: //common to several derived classes
int maxUsers;
public:
ServiceItem (int aMaxUsers = 0 )
{maxUsers = aMaxUsers;}
void setMaxUsers (int aMaxUsers = 1)
{maxUsers = aMaxUsers;};
int getMaxUsers ()const {return maxUsers;};
};
//----------------------------------------------------
class Library : virtual public ServiceItem {
private: //Derived Class of ServiceItem
int numberBooks;
public:
Library (int aNumberBooks = 0, int aMaxUsers = 1);
int getNumberBooks () const {return numberBooks;};
void setNumberBooks (int aNumberBooks = 0)
{numberBooks = aNumberBooks;} ;
};
Library::Library (int aNumberBooks, int aMaxUsers) : ServiceItem (aMaxUsers) {numberBooks = aNumberBooks;}
//----------------------------------------------------
class Truck : virtual public ServiceItem {
private: //Derived Class
float gasQuantity;
public:
Truck (float aGasQuantity, int aMaxUsers = 1);
int getGasQty () const { return gasQuantity; }
void setGasQty ( int aGasQuantity = 0.0 )
{ gasQuantity = aGasQuantity; }
};
Truck::Truck (float aGasQuantity, int aMaxUsers)
: ServiceItem (aMaxUsers) {gasQuantity = aGasQuantity;}
//----------------------------------------------------
class LibraryTruck : public Truck, public Library {
public: //Derived Class of Truck and Library
LibraryTruck (int aNumberBooks = 0,
float aGasQuantity = 0.0, int aMaxUsers = 1);
};
LibraryTruck::LibraryTruck (int aNumberBooks,
float aGasQuantity, int aMaxUsers) : Truck (aGasQuantity), Library
(aNumberBooks), ServiceItem (aMaxUsers) {}
//-----------------Test Module------------------------
main ()
{LibraryTruck theLibraryTruck (0,0.0,0);
theLibraryTruck.setMaxUsers (200);
cout <<"\nMaximum number of users is "
<< theLibraryTruck.getMaxUsers();
return 0; }
//Sample output
//Maximum number of users is 200
In this example the LibraryTruck class is a derived class to both the Library class and the Truck class. We can say that a library truck "is a" library and "is a" truck". LibraryTruck inherits the data and function members of Library class and Truck class. It also inherits the data and function members of the ServiceItem class. In C++ the ServiceItem class is a virtual base class because of repeated inheritance. Both the Library class and the Truck class indicate "virtual public ServiceItem". This avoids the potential multiple creation of data members in objects of the LibraryTruck class. All the classes have a constructor with arguments to initialize the data members. When an object of the LibraryTruck class is created it indicates the initial value of data members declared in all superclasses, such as LibraryTruck theLibraryTruck (0, 0.0, 0).
How we model generalization specialization can affect
the quality of the software in terms of correctness, reliability,
extendibility, and reusability. For example, the use of generalization
specialization leads to specialized classes that can be easily
modified or changed out without affecting other classes. This
improves the extendibility of the software.
The use of generalization specialization with polymorphic operations is a software quality technique. It supports the creation of understandable, extendible software. All the systems modeled in this book make extensive use of generalization specialization with polymorphic operations. We identify a superclass with general operations. Then we identify subclasses with the same named operations (polymorphic operations). We create a specific implementation of each polymorphic operation in the subclasses. Each subclass has the responsibility to provide the correct, specific actions for the polymorphic operation. Later, when we want to expand the system we add a new subclass with new implementations of the polymorphic operations. We can add a new subclass with new implementations of the polymorphic operations relatively easily without adversely affecting other classes in the system.
In O-O modeling it is important to identify and describe generalization specialization between classes. The generalization specialization relationship is the "is a" relationship between superclasses and subclasses. Relationships are shown on the class diagram. Using a CASE tool, a class diagram showing generalization specialization between classes may be created. The CASE tool may generate source code to implement the classes and the relationships.