In the previous chapters we discussed how to model classes and objects. The purpose of this chapter is to describe how to model relationships between classes and between objects in the object model. The object model is the structure and description of classes, objects, and relationships in a system. The basic topics are:
What is a relationship?
What is an association relationship?
How to identify association relationships.
How to show association relationships on a class diagram.
How to specify an association relationship including constraints.
How to generate C++ with a CASE tool script for relationships.
A relationship is a link or connection between classes
or between objects. There are four major O-O relationships: association
("has a), aggregation ("part of"), generalization
specialization ("is a"), and interaction ("calls").
Association, aggregation, and association relationships are identified
in the object model. Interaction relationships are identified
in the dynamic model. In the physical world we see these relationships
all the time. Several physical real world examples of relationships
are shown in the table below.
Table of physical, real world examples of relationships.
------------------------------------------------------------------------------
Example Relationship
------------------------------------------------------------------------------
A driver "has a" car. Association
The car "has a part" a motor. Aggregation
The car "is a" vehicle. Generalization specialization
A driver "interacts" with a car. Interaction
A house "has" furniture. Association
A house "has a part" roof. Aggregation
A house "is a" physical structure. Generalization specialization
An owner "interacts" with a house Interaction
------------------------------------------------------------------------------
In software information systems we also see these
relationships. Several software, information systems examples
of relationships in the table below.
Table of software, information systems examples of relationships.
--------------------------------------------------------------------------------------------------
Example Relationship
--------------------------------------------------------------------------------------------------
An invoice "has a" salesman Association
An invoice "has a part" item list Aggregation
An invoice "is a" retail document Generalization specialization
A user "interacts with" (updates) an invoice Interaction
A library book loan "has a" borrower Association
A library book loan "has a" due date Aggregation
A library book loan "is a" library document Generalization specialization
A librarian "interacts with" (updates) a library book loan Interaction
----------------------------------------------------------------------------------------------------
A major relationship is association. In this section we will look at association, the "has a" connection. We will look at "what is association?", how to identify association, how to show association on a class diagram, how to specify association, and how to code association.
The class diagram is a graphic representation
of classes and relationships. Using the Rumbaugh OMT notation
we show association (has a), aggregation (part of) relationship,
and generalization specialization (is a) relationships on the
class diagram. We show the interaction (calls) relationship, e.g.
events and messages on the object interaction diagram to be presented
in a later section. The class diagram below shows the major relationships
and cardinalities of generalization specialization (Vehicle to
Car), 1 to 1 aggregation (Car to Motor), 1 to many aggregation
(Car to Tire), 1 to zero or one association (Car to CellularPhone),
and 1 to many association (Car to Passenger). The diagram shows
traversal path names for the association and aggregation relationships.
A traversal path name is a reference to one or more associated
or part objects. In the class diagram below, the traversal path
names are currentMotor, currentTires, currentCellularPhone, and
currentPassengers. The traversal path names are important to be
able to access an associated or part object. For example, the
car1 object accesses the cellularPhone1 object through the traversal
path "currentCellularPhone".
Each relationship should be described in a relationship
specification form. Its purpose is to state adequate information
to document and to program each relationship. We will first present
a sample specification input form, define the specification information,
and present a sample report. The steps to create the class specification
using a CASE tool or text editor are listed below.
>> Run the CASE tool or text editor from Windows
>> Select "File - Open" e.g. c:\car.omt
>> Double click on a relationship, e.g. currentMotor
>> Enter the relationship information
>> Select "Generate - Generate Class Report" and select a report script, e.g. RPTRELAT.SCT
>> Select "File - Edit File" and enter the report name to view the report.
The following is a sample relationship specification form created in a text editor. Some CASE tools collect relationship specification information directly from the class diagram and do not offer the relationship specification. If you desire to collect specification information, you may use a text editor to collect the following information.
Source Class - Car
Destination Class - Motor
Traversal Path Class/Type and Name - Car currentMotor
Order by Attribute Class/Type and Name - N/A
Type of Relationship (Association/Aggregation) - Aggregation
Relationship Constraints (rules) - A car must have a motor
Relationship Cardinality - 1 to 1
Inverse Traversal Path Class/Type and Name - Car currentCar
Implementation - C++ data member
Remarks -
N/A.
Figure Sample Relationship Specification Input Form for a Use with a Text Editor
The relationship specification holds information on each relationship. Relationship information includes the following:
- Source Class states initial class to define the relationship.
- Destination Class states the second class to define the relationship.
- Traversal Path Class/Type and Name states the class/type and name of the attribute which implements the relationship and which is used to traverse (get) an object of the associated or part class.
- Order by Attribute Class/Type and Name states the class/type and name of the attribute which orders the objects of the associated or part class.
- Type of Relationship (Association/Aggregation) states the type of relationship, e.g. association or aggregation.
- Relationship Constraints states rules or expressions that restrict or constrain a relationship such as a motor weight must be less car frame weight.
- Relationship Cardinality states the relationship cardinality, e.g. 1 to 1, 1 to 0 or 1, 1 to many, or many to many
- Inverse Traversal Attribute Class/Type and Name states class/type and name of the inverse traversal path.
- Implementation states the language dependent implementation of the relationship, e.g. a Motor data member implements the aggregation relationship between Car class and Motor class.
- Remarks states any additional information about the relationship.
To access the relationship specification information,
you need to know the script variable for each piece of information
in the class diagram or relationship specification input form.
The following are sample script variables from the relationship
specification in a CASE tool. This CASE tool only collects only
the relationship path and class names. A brief description states
what occurs when the script variable appears in a script. Relationship
variables access relationship information. Since there are multiple
relationship for each class, these variables are used within the
[] (Repeat Operator). For example, the following script statement
prints out the association relationship names for each class [ASSOCIATION_ONE_NAME,
].
BASE_CLASS or SUPERCLASS_NAME
Prints superclass name for a class
ASSOCIATION_ONE_CLASS Prints class name for a 1 to 1 association
ASSOCIATION_ONE_NAME Prints traversal path name for a 1 to 1 association
ASSOCIATION_MANY_CLASS Prints class name for a 1 to M association
ASSOCIATION_MANY_NAME Prints
traversal path name for a 1 to M association
AGGREGATION_NAME Prints traversal path name for aggregation (part) class
AGGREGATION_ONE_CLASS Prints class name for a 1 to 1 aggregation
AGGREGATION_ONE_NAME Prints traversal path name for a 1 to 1 aggregation
AGGREGATION_MANY_CLASS Prints class name for a 1 to M aggregation
AGGREGATION_MANY_NAME Prints traversal path name for a 1 to M aggregation
CASE tools collect minimum relationship information, such as the source class, destination class, and traversal path name. A relationship report detailing each relationship may be created. The following is a sample report and table showing relationship information for the Car Simulation System was created with a CASE tool.
Class : Car
Superclasses: Vehicle
Library Superclass:
Associated Class - Traversal Name: CellularPhone - currentCellularPhone
Associated Class - Traversal Name: Passenger - currentPassengers
Part Class - Traversal Name: Motor - currentMotor
Part Class - Traversal
Name: Tire - currentTires
The script (RPTRELAT.SCT) to create this report is
shown below.
Class : CLASS_NAME
Superclasses: [NO_RETURN BASE_CLASS ,DELETE_LAST_SYMBOL]
Library Superclass: CLASS_LIBRARY_BASE_CLASS
[Associated Class - Traversal Name: ASSOCIATION_ONE_CLASS - ASSOCIATION_ONE_NAME]
[Associated Class - Traversal Name: ASSOCIATION_MANY_CLASS - ASSOCIATION_MANY_NAME]
[Part Class - Traversal Name: AGGREGATION_ONE_CLASS - AGGREGATION_ONE_NAME]
[Part Class - Traversal Name: AGGREGATION_MANY_CLASS - AGGREGATION_MANY_NAME]
The relationship table for the Car Simulation System is shown below.
| Source Class | Destination Class | Traversal Path Name | Relationship Description |
| Vehicle | Car | N/A | Generalization Specialization Vehicle to Car |
| Car | CellularPhone | currentCellularPhone | 1:1 Association from Car to CellularPhone |
| Car | Passenger | currentPassengers | 1:M Association Car to Passenger |
| Car | Motor | currentMotor | 1:1 Aggregation Car to Motor |
| Car | Tire | currentTires | 1:M Aggregation Car to Tire |
The script (TABRELAT.SCT) to create this table from a class diagram
is shown below.
SCRIPT_NOREPEAT_HEADER_BEGIN
Source Class, Destination Class, Traversal Path Name, Relationship Description
SCRIPT_NOREPEAT_HEADER_END
[SUPERCLASS_NAME, CLASS_NAME, N/A, Generalization Specialization SUPERCLASS_NAME to CLASS_NAME]
[CLASS_NAME, ASSOCIATION_ONE_CLASS, ASSOCIATION_ONE_NAME, 1:1 Association from CLASS_NAME to ASSOCIATION_ONE_CLASS]
[CLASS_NAME, ASSOCIATION_MANY_CLASS,ASSOCIATION_MANY_NAME, 1:M Association CLASS_NAME to ASSOCIATION_MANY_CLASS]
[CLASS_NAME, AGGREGATION_ONE_CLASS,AGGREGATION_ONE_NAME, 1:1 Aggregation CLASS_NAME to AGGREGATION_ONE_CLASS]
[CLASS_NAME, AGGREGATION_MANY_CLASS,AGGREGATION_MANY_NAME, 1:M Aggregation CLASS_NAME to AGGREGATION_MANY_CLASS]
Association is a link between objects of two or more
classes. An association exists between objects of different classes
whenever there is a logical linking, interaction, or dependency
between the objects. An association relationship may be described
as "has a", "associated with" or "knows
about". For example a car "has a" cellular phone
as shown in the figure below.
An association has a cardinality or multiplicity.
This is the number of objects on one side of an association that
may be associated with an object on the other side of the association.
For example, one car has one cellular phone. This is an example
of a 1 to 1 association. If a car object may have many passenger
objects, then there is a 1 to many association between the car
class and the passenger class.
An association and aggregation relationship have
constraints. A constraint is a limitation, restriction, or rule.
Multiplicity (cardinality) is a constraint. For example a constraint
on the car to passenger association is that a car can only have
6 passengers. Constraints on each relationship may be specified
in a relationship specification form with a CASE tool.
The association relationship is important for many reasons. Association is the most fundamental relationship of real world physical things and software entities. Association helps develop highly cohesive objects where all associated objects are linked to accomplish a purpose. It implements dependency among objects. For example, an object may be dependent upon its associated objects to accomplish its function. It implements visibility among objects. For example, an object has visibility for (sees) its associated objects so that it can send messages to its associated objects.
Identify association by looking for logical links
or connections between two or more objects. One object may "know
about" another object. For example, a retail store "knows
about" its employees. One object may "depend upon"
another object. For example, a bank "depends upon" its
tellers. Often it is useful to make a drawing of the key objects
in a system and show the association between the objects on the
drawing. For each object on the drawing, identify associations
between them.
The following are suggested steps to identify association
relationships:
Step 1. Make a drawing or physical representation,
e.g., a car, a motor, a passenger.
Step 2. Identify the objects and their classes, e.g.,
a car, a motor, a passenger.
Step 3. For each object, identify association relationships
with the question "For each object what are the associated
objects?"
Step 4. Identify the multiplicity of each association
relationship with the questions "This object is associated
with zero, one or many of the other object?" and "This
association is either optional or required?"
Step 5. Identify if the relationship is an association
("has a") or an aggregation ("part of"). When
in doubt assume association ("has a").
Step 6. For each relationship, identify the constraints
(limitations or rules).
Step 7. If available, check messages between objects in the dynamic model because messages indicate association and aggregation relationships.
Step 8. Create a class diagram, data dictionary, class specification, and prototype.
Show association on a class diagram by connecting
two or more classes together with the association symbol (a solid
line). Some CASE tools show the name of the association relationship,
such as "has a", or the name of the traversal path,
such as currentCellularPhone. A traversal path name is
a reference to one or more associated objects. In the class diagram
below, the traversal path names are currentCellularPhone and currentPassengers.
The open circle on the association line indicates an optional
(zero or one) association. The black circle on the association
line indicates many (zero or more) in the association. The figure
below is an example of a class diagram showing association using
a CASE tool.
As shown in the figure below, the car class has an
association with the cellular phone class. An object of the car
class may have an association with zero or one objects of the
cellular phone class. The label on the association line is the
name of the traversal path name or the name of the association
such as "has a". A traversal path name is a reference
to one or more associated objects. In this case currentCellularPhone
is the traversal path name that holds the object name of the associated
cellular phone object. The car class has an operation "make
phone call" and the cellular phone class has an operation
"makePhoneCall".
As shown on this diagram, the car class has an association
with the passenger class. An object of the car class may have
an association with zero, one, or many objects of the passenger
class. The car class has a traversal path name currentPassengers
which holds the object names of associated passenger objects.
The car class has the operations "add passenger" and
"remove passenger".
The steps to create the class diagram showing association 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 first class.
>> Select and place a class symbol for the second class.
>> Select the association relationship symbol and the cardinality (1:1, 1:M, etc)
>> Connect the two classes.
>> For C++ code generation, enter a name for the traversal path name to represent the association.
Each association relationship should be described in a relationship specification form. Constraints (limitations and rules) should be stated. For association multiplicity constraints should be stated. For example, the following is a sample relationship specification report created in a text editor.
- Source Class - Car
- Destination Class - CellularPhone
- Traversal Path Class/Type and Name - Car currentCellularPhone
- Order by Attribute Class/Type and Name - N/A
- Type of Relationship (Association/Aggregation) - Association
- Relationship Constraints (rules) -
- Relationship Cardinality - 1 to 0/1
- Inverse Traversal Path Class/Type and Name - Car currentCar
- Implementation -
- Remarks.
There are several ways to code the 1 to 1 and 1 to Many associations in C++. These will be described in the following sections.
Once the class diagram has been completed, then C++
code can be automatically generated with a CASE tool. The CASE
tool implements each association shown on the class diagram. We
are presenting how to code associations in C++ to be able to understand
and modify the generated C++.
There are many ways to code association and aggregation
in C++, such as coding data members to hold associated objects,
pointers to associated objects, or objects of special association
classes. The most straight forward way to code association is
to identify a traversal path name. As previously stated, a traversal
path name is a reference to one or more associated objects.
A traversal path name is a data member that may hold one of the
following: an associated object, a collection of associated objects,
a pointer to an associated object, or a pointer to a collection
of associated objects.
To code the association relationship, identify a
traversal path name to represent the association. The traversal
path name becomes a data member in one of the associated classes.
In the simplest case, a car has 1 cellular phone. In the Car class
identify a traversal path name for the 1 to 1 association with
the cellular phone class. The traversal path name in the Car class
might be CellularPhone *currentCellularPhone.
In C++ code, create a data member for the traversal path name,
such as currentCellularPhone as a pointer in the following form
and example:
<Class Name of Associated Object> * <Traversal path name>;
CellularPhone *currentCellularPhone;
In this case the data member holds a pointer to the
associated object. A pointer is used because an associated object
can exist independently of the other associated object. When one
associated object is deleted, then the other associated object
is not deleted. For example, a cellular phone is not deleted
when the car is deleted.
When we use a C++ pointer for 1 to 1 association,
we must connect the declared pointer with an actual object. For
example we must connect the cellularPhone1 object to the pointer
*currentCellularPhone. We can do that with a constructor with
arguments or an attach operation. The sample constructors and
destructors below initialize the pointer to 0. Alternatively you
may initialize the pointer to a null association object as shown
in the code listing.:
Car () { currentCellularPhone = 0; } // default constructor
//to initialize currentCellularPhone pointer
Car (CellularPhone *aCellularPhone) //constructor with
{currentCellularPhone = aCellularPhone; }//argument
to connect the associated object
~Car () {currentCellularPhone = 0;}//destructor to detach
associated object
The following are sample invocations of these two constructors:
Car car1; //invokes the default constructor
Car car2 (&cellularPhone1) //invokes
the constructor with arguments
An set or attach operation to attach an associated object in a 1:1 sets the traversal path name to be the associated object.
void setCellularPhone (CellularPhone &aPhone)
{ currentCellularPhone = &aPhone;
}
The set or attach operation may receive the associated
object, a pointer to the associated object, or a reference to
the associated object. The benefit of a reference is that any
changes to the associated object in the set operation will be
reflected in the object itself.
The remove or detach operation to detach an associated objects sets the traversal path name to 0 or NULL in a 1 to 1 association. If required, the caller may have to delete the actual associated object if the object is a dynamic object created with the new keyword, e.g. delete cellularPhone1;
void removeCellularPhone (CellularPhone &aCellularPhone)
{ currentCellularPhone = 0; }
The getAssociatedObject operation returns the identity of the current associated object.
CellularPhone& getCellularPhone ()
{ return * currentCellularPhone; }
An example of association is shown in the figure
below. There is a one to one association between an object of
the car class and an object of the cellular phone class. The listing
below is a the generated C++ code with added statements and an
added main function.
Listing C++ code listing
showing 1 to 1 association.
//////////////////////////Car.h file////////////////////////////////////
#ifndef __CAR_H
#define __CAR_H
#ifndef __CLLLRPHN_H
#include "ClllrPhn.h"
#endif
class Car
{ int speed; //Attribute data member
CellularPhone* currentCellularPhone; //1:1 association
object data member
public:
Car () : speed(0)
{ currentCellularPhone = 0; //Initialization to 0
}
//Get accessor function for 1:1 association object data member
const CellularPhone* getCellularPhone() const
{ return currentCellularPhone;
}
//Set accessor function for 1:1 association object data member
void setCellularPhone (CellularPhone* const acurrentCellularPhone)
{ currentCellularPhone = acurrentCellularPhone;
}
//Remove function for 1:1 association object data member
//Warning delete currentCellularPhone object if dynamic object
void removeCellularPhone()
{ currentCellularPhone = 0;
}
void makePhoneCall (long aNumber) ;
~ Car ( ) { } //Destructor
};
#endif
//////////////////////////ClllrPhn.h file////////////////////////////////////
#ifndef __CLLLRPHN_H
#define __CLLLRPHN_H
class CellularPhone
{ long phoneNumber; //Attribute data member
public:
CellularPhone () : phoneNumber(0)
{
}
void makePhoneCall (long aNumber) ;
~ CellularPhone ( ) { } //Destructor
};
#endif
//////////////////////////Car.cpp file////////////////////////////////////
#include "Car.h"
void Car::makePhoneCall(long aNumber) //Body added after code generation
{ if (currentCellularPhone == 0) return; //no cellular phone to make call
currentCellularPhone -> makePhoneCall (aNumber);
}
//////////////////////////ClllrPhn.cpp file////////////////////////////////////
#include "ClllrPhn.h"
void CellularPhone::makePhoneCall(long aNumber) {
}
//////////////////////////main.cpp file////////////////////////////////////
#include "Car.h"
int main ()
{
Car car1; //invokes the default constructor
CellularPhone cellularPhone1;
car1.setCellularPhone (&cellularPhone1);
car1.makePhoneCall (55555);
return 0;
}
In this example, the CASE tool generated the class declarations (.h files) for the car class and the cellular phone class. In the car class, it generated the include directives to include the files of the associated cellular phone class. In the car class, the CASE tool generated a private data member to implement the 1:1 association between car and cellular phone. The private data member is a pointer to an associated cellular phone object. It is a pointer because the associated cellular phone object has independent existence. A car object can be deleted without deleting the associated cellular phone object. The generated C++ code was updated to fill-in the function definitions and to provide a main function.
To generate the C++ class declaration and function
definitions, we used two scripts. A class declaration script generated
the Car.h file and 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$ASSOCIATION_ONE_CLASS$_H
#include "TRUNCATE_EIGHT$ASSOCIATION_ONE_CLASS.h"
#endif
]
class CLASS_NAME[NO_RETURN NO_REPEAT: NO_REPEAT public BASE_CLASS ,DELETE_LAST_SYMBOL]
{ [CPP_ATTRIBUTE_STATIC CPP_ATTRIBUTE_CONSTANT
ATTRIBUTE_TYPE ATTRIBUTE_NAME$; //Attribute data member]
[ASSOCIATION_ONE_CLASS$* ASSOCIATION_ONE_NAME$;
//1:1 association object data member]
public:
CLASS_NAME ()
SELECT_WHEN LOGICAL_NOT ATTRIBUTE_IS_STATIC [NO_RETURN NO_REPEAT: ATTRIBUTE_NAME(ATTRIBUTE_INITIAL_VALUE),DELETE_LAST_SYMBOL]
{ [ASSOCIATION_ONE_NAME = 0; //Initialization to 0 ]
}
[ //Get accessor function for 1:1 association object data member
const ASSOCIATION_ONE_CLASS$* get$ASSOCIATION_ONE_CLASS$() const
{ return ASSOCIATION_ONE_NAME$;
} ]
[ //Set accessor function for 1:1 association object data member
void set$ASSOCIATION_ONE_CLASS ($ASSOCIATION_ONE_CLASS$* const a$ASSOCIATION_ONE_NAME$)
{ ASSOCIATION_ONE_NAME = a$ASSOCIATION_ONE_NAME$;
} ]
[ //Remove function for 1:1 association object data member
//Warning delete ASSOCIATION_ONE_NAME object if dynamic object
void remove$ASSOCIATION_ONE_CLASS$()
{ ASSOCIATION_ONE_NAME = 0;
} ]
[ OPERATION_RETURN_TYPE OPERATION_NAME (CPP_OPERATION_PARAMETERS) CPP_OPERATION_CONSTANT CPP_OPERATION_PURE_VIRTUAL;
]
~ 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)
{
OPERATION_CODE
}
]
To code C++ classes with 1 to 1 association, create
a class declaration 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 class declaration
of the first class (class with association) should have an include
statement for the association class file, a class name, data members,
traversal data members holding a pointer to an object of the association
class, a constructor, traversal function members to access an
object of the association class, traversal data member accessor
functions, function members, and a destructor. This information
class diagram shows that the first class (class with association)
is connected to the second class (association class) with the
traversal data member. The traversal data member is used to access
the object of the second class (association class). The class
diagram on the right shows sample C++ identifiers that are also
in the code listings.
The following are key points about coding a class with an association. The class may or may not create and delete association objects.
- Declare a #include compiler directives to include the association class, e.g. #include "Cllrphn.h".
- Declare a pointer as an association object data member, e.g. CellularPhone *currentCellularPhone;
- Define a traversal function to access the association object data member, e.g. void makePhoneCall ()
- Define a default constructor to initialize an object with default data member values, e.g. Car (){currentCellularPhone = 0;} Set association data member pointers to 0 or to the address of a null association object. Actual association object must be assigned with a set statement, e.g. car1.setCellularPhone(&phone1);
- Define a constructor with arguments to initialize an object with passed values. Optionally, pass a pointer to an association object and assign the association object data member. Car (int aSpeed, CellularPhone* currentCellularPhone) : speed (aSpeed), currentCellularPhone (aCellularPhone) { }
- Define a copy constructor to initialize a object with a copy of another object. Optionally, assign the association object data member to the association object in the copy or create a new association object. Car (const Car& aCar) { currentCellularPhone = aCar.currentCellularPhone; }
- Define an assignment operator (operator=) to assign one object to another. Optionally, assign the association object data member to the association object in the copy or create a new association object. Car& operator= (const Car& aCar) {currentCellularPhone = aCar.currentCellularPhone ; }.
- Define an equality operator (operator==) to compare two objects. Optionally, compare the association object data members, e.g. int operator== (const Car& aCar) { return (currentCellularPhone == aCar.currentCellularPhone ) ; }. Note - the two objects equal only if they both point to the same association object. The alternative is not to compare the associated object data members.
- Define input function - C++ insertion operator for use with cin. Optionally, invoke the association object operator>>, e.g. friend istream& operator>> (istream& is, Car& aCar) {is >> *aCar.currentCellularPhone; }
- Define the output function - C++ extraction operator for use with cout. Optionally, invoke the association object operator<<, e.g. friend ostream& operator<< (ostream& os, Car& aCar) { os << * aCar.currentCellularPhone; }
- Define get, set, remove, exists, and exists (a specific object) assessor functions, e.g. CellularPhone* getCellularPhone () { return currentCellularPhone; }. Warning returning a pointer provides direct access to a private data member.
- Define a default destructor to deinitialize an object, ~Car () {}
- Define a main (test) function to create an object
with an association object and invoke a traversal function
In the association class, optionally declare an inverse
traversal data member, e.g. Car * aCar; Prior
to compiling the class, insert a forward declaration before the
class declaration, e.g. class Car;
The complete C++ code listing is below.
Listing C++ Code Listing
for 1 to 1 Association
// 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
//Required for 1:1 association classes
#ifndef __CLLLRPHN_H //CellularPhone
#include "ClllrPhn.h"
#endif
class Car
{ int speed; //Attribute data member
CellularPhone* currentCellularPhone; //1:1 association
object data member
CellularPhone nullCellularPhone;//Null association
object for association object
public:
//Default constructor alternative to compiler provided default constructor
//Association object data member pointers initialized to null association object
Car ();
Car ( int aspeed ) ; //Constructor with arguments
Car (const Car& aCar ); //Copy Constructor
Car& operator= (const Car& aCar); //Operator= Assignment Operator
int operator== (const Car& aCar) const; //Operator==
Equality Operator
friend ostream& operator<< (ostream&
os, const Car& aCar); //Operator<< for cout
friend istream& operator>> (istream&
is, Car& aCar); //Operator>> for cin
int getspeed() const//Get accessor function for non-static attribute data member
{ return speed;
}
//Set accessor function for non-static attribute data member
void setspeed (const int aspeed)
{ speed = aspeed;
}
//Get accessor function for 1:1 association object data member
const CellularPhone* getCellularPhone() const
{ return currentCellularPhone;
}
//Set accessor function for 1:1 association object data member
void setCellularPhone (CellularPhone* const acurrentCellularPhone)
{ currentCellularPhone = acurrentCellularPhone;
}
//Remove function for 1:1 association object data member
//Warning delete currentCellularPhone object if dynamic object
void removeCellularPhone(); //throw (string)
//Exists function for 1:1 association object data member
int existsCellularPhone() const;
//Exists function with argument for 1:1 association object data member
int existsCellularPhone (CellularPhone* const
acurrentCellularPhone) const;
void makePhoneCall (long aNumber) ;
~ 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
//////////////////////Car.cpp file//////////////////////////////////////////////
#include "Car.h"
// Functions for class Car
void Car::makePhoneCall(long aNumber)
{
}
//Default constructor alternative to compiler provided default constructor
//Association object data member pointers initialized to null association object
Car::Car () : speed(0)
{ currentCellularPhone = &nullCellularPhone;
//Initialization to null association object
}
//Constructor with arguments
Car::Car ( int aspeed )
{ speed = aspeed; //Initialization of attributes
currentCellularPhone = &nullCellularPhone;
//Initialization to null association object
}
//Copy constructor alternative to compiler provided default copy constructor
Car::Car (const Car& aCar )
{speed = aCar.speed;
currentCellularPhone = &nullCellularPhone;
//Initialization to null association object
//currentCellularPhone = aCar.currentCellularPhone; //Commented out shallow copy
}
//Operator= Assignment Operator alternative to compiler provided operator=
Car& Car::operator= (const Car& aCar)
{ if (this == &aCar) return *this;
speed = aCar.speed;
currentCellularPhone = &nullCellularPhone;
//Initialization to null association object
// currentCellularPhone = aCar.currentCellularPhone;//Commented out shallow copy
return *this;
}
//Operator== Equality Operator
int Car::operator== (const Car& aCar) const
{ return (
//Equality check for 1:1 association data members for shallow comparison
//( currentCellularPhone == aCar.currentCellularPhone) &&
//Equality check for attribute data members
(speed == aCar.speed)
);
}
//Operator<< extraction for cout
ostream& operator<< (ostream& os, const Car& 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)
{ cout << "\nEnter Object Attribute Values or 0 - Class Car";
cout << "\nEnter speed : " << endl;
is >> aCar.speed;
if (aCar.currentCellularPhone != &aCar.nullCellularPhone) is >> *aCar.currentCellularPhone ;
return is;
}
//Remove function for 1:1 association object data member
//Warning delete currentCellularPhone object if dynamic object
void Car::removeCellularPhone() //throw (string)
{ string noCellularPhone;
if ( currentCellularPhone == &nullCellularPhone ) throw noCellularPhone;
else currentCellularPhone = &nullCellularPhone;
}
//Exists function for 1:1 association object data member
int Car::existsCellularPhone() const
{ if ( currentCellularPhone == &nullCellularPhone ) return 0; else return 1;
}
//Exists function with argument for 1:1 association object data member
int Car::existsCellularPhone (CellularPhone* const aCellularPhone) const
{ if ( currentCellularPhone == &nullCellularPhone ) return 0;
else if ( *currentCellularPhone == *aCellularPhone ) return 1;
else return 0;
}
In the 1 to many association identify a traversal
path name that holds a collection of associated objects. For example,
a car has many passengers. In the car class identify a traversal
path name, such as passengerArray or aPassengerList. In C++ code,
create a data member for the traversal path name, such as passengerArray
or aPassengerList. The following are several examples:
Passenger *passengerArray[maxPassengers]; //C++ array
List <Passenger> aPassengerList; //C++ template class
A C++ template is a special generic structure that
can be defined to hold objects of different classes. For example
a generic list can be defined to hold passengers or to hold items
of fruit.
It is the programmer's responsibility to add and
remove associated objects from the collection of associated objects.
For example, the programmer must insert passenger1 and passenger2
to the collection. He must remove passenger1 and passenger2 from
the collection. This is done in the car class with the operations
"add passenger" and "remove passenger".
An add operation to attach an associated object in a 1:M adds an associated object to the collection of associated objects.
void addPassenger (Passenger &aPassenger)
{ aPassengerList.add (&aPassenger);
}
The add operation may receive a pointer to the associated
object or a reference to the associated object. The benefit of
a reference is that any changes to the associated object in the
attach operation will be reflected in the object itself.
The remove operation to detach an associated object removes an associated object from the collection of associated objects.
void removePassenger (Passenger &aPassenger)
{ aPassengerList.detach (&aPassenger);}
Listing C++ code listing
showing 1 to many association.
//////////////////////////Car.h file////////////////////////////////////
#ifndef __CAR_H
#define __CAR_H
#ifndef __PASSENGR_H
#include "Passengr.h"
#endif
const int maxNumberOfPassengers
= 4;
class Car
{ int speed; //Attribute data member
int currentPassengersIndex; //Index
for array of 1:M association objects
//1:M association object data member
//Change C array to C++ collection class with iterator
Passenger* currentPassengers [maxNumberOfPassengers
];
// Null association object for initialization of association object data member
Passenger nullPassenger;
public:
Car () : speed(0)
{ currentPassengersIndex = 0; //Index for array of 1:M association objects
//Initialization of array of 1:M association objects to null association objects
for ( int i = 0; i < maxNumberOfPassengers; i++) currentPassengers [i ] = &nullPassenger;
//Alternative initialization currentPassengers[i] =0
}
void addPassenger (Passenger&
aPassenger) ;
void removePassenger (Passenger&
aPassenger) ;
~ Car ( ) { } //Destructor
};
#endif
//////////////////////////Passengr.h file////////////////////////////////////
#ifndef __PASSENGR_H
#define __PASSENGR_H
class Passenger
{ int seatBeltStatus; //Attribute data member
public:
Passenger () : seatBeltStatus(0)
{ }
void checkPassengerSeatBelt ()
;
~ Passenger ( ) { } //Destructor
};
#endif
//////////////////////////Car.cpp file////////////////////////////////////
#include "Car.h"
void Car::addPassenger(Passenger& aPassenger) {
}
void Car::removePassenger(Passenger& aPassenger) {
}
//////////////////////////Passengr.cpp file////////////////////////////////////
#include "Passengr.h"
void Passenger::checkPassengerSeatBelt() {
}
//////////////////////////main.cpp file////////////////////////////////////
#include "Car.h"
int main ()
{
Car car1; //invokes the default constructor
Passenger passenger1;
car1.addPassenger (passenger1);
car1.removePassenger (passenger1);
return 0;
}
To generate the C++ class declaration and function
definitions, 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$ASSOCIATION_MANY_CLASS$_H
#include "TRUNCATE_EIGHT$ASSOCIATION_MANY_CLASS$.h"
#endif
]
NO_OUTPUT_BEGIN Use the the CLASS_USER fields for typedef, enum, const declarations, e.g.
const int maxNumberOfTires = 4; NO_OUTPUT_END
CLASS_USER1
CLASS_USER2
class CLASS_NAME[NO_RETURN NO_REPEAT: NO_REPEAT public BASE_CLASS ,DELETE_LAST_SYMBOL]
{ [CPP_ATTRIBUTE_STATIC CPP_ATTRIBUTE_CONSTANT
ATTRIBUTE_TYPE ATTRIBUTE_NAME$; //Attribute data member]
[int ASSOCIATION_MANY_NAME$Index; //Index for
array of 1:M association objects]
[ //1:M association object data member
//Change C array to C++ collection class with iterator
ASSOCIATION_MANY_CLASS$* ASSOCIATION_MANY_NAME
LITERAL_SYMBOL[maxNumberOf$ASSOCIATION_MANY_CLASS$s LITERAL_SYMBOL];]
[ // Null association object for initialization of association object data member
ASSOCIATION_MANY_CLASS$ null$ASSOCIATION_MANY_CLASS;]
public:
CLASS_NAME ()
SELECT_WHEN LOGICAL_NOT ATTRIBUTE_IS_STATIC [NO_RETURN NO_REPEAT: ATTRIBUTE_NAME(ATTRIBUTE_INITIAL_VALUE),DELETE_LAST_SYMBOL]
{ [ASSOCIATION_MANY_NAME$Index = 0; //Index for array of 1:M association objects ]
[ //Initialization of array of 1:M association objects to null association objects
for ( int i = 0; i < maxNumberOf$ASSOCIATION_MANY_CLASS$s; i++) ASSOCIATION_MANY_NAME LITERAL_SYMBOL[i LITERAL_SYMBOL] = &null$ASSOCIATION_MANY_CLASS; ]
}
[ 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++ classes with 1 to many association, create
a class declaration 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 class declaration
of the first class (class with association) should have an include
statement for the association class file, a class name, data members,
a traversal data member collection holding pointers to objects
of the association class, a constructor, traversal function members
to access an object of the association class, traversal data member
accessor functions, function members, and a destructor. This information
class diagram shows that the first class (class with association)
is connected to the second class (association class) with the
traversal data member collection. The traversal data member collection
is used to access the object of the second class (association
class). The class diagram on the right shows sample C++ identifiers
that are also in the code listings.
Key aspects of coding a class with association are listed below. The class may or may not create and delete association objects:
- Declare a #include compiler directive to include the association class, e.g. #include "Passengr.h". If required include the collection class that holds association objects.
- Declare an association object data member - array or collection object to hold pointers to association objects, e.g. Passenger *currentPassengers [maxNumberOfPassengers];
- Define a default constructor to initialize an object with default data member values. Since the array holds pointers, initialize each pointer to 0. Car (){for (int i = 0; i < maxNumberOfPassengers; i++) currentPassengers[i] = 0;
- Define a constructor with arguments to initialize an object with passed values. Optionally pass a pointer to an array or collection object and assign the array or collection object. Car (int aSpeed, Passenger* pPassengers) { currentPassengers [0] = *pPassengers; }
- Define a copy constructor to initialize an object with a copy of another object. Optionally assign the array or collection object to the array or collection object of the copy. Optional iterator. Following code only if you desire to copy 1:M association objects Car (const Car& aCar) {for (i = 0; i < maxNumberOfPassengers; i++) currentPassengers [i] = aCar.currentPassengers [i]; }
- Define an assignment operator (operator=) to assign one object to another. Optionally assign the array or collection object. Optional iterator. Following code only if you desire to assign 1:M association objects Car& operator= (const Car& aCar) ) {for (i = 0; i < maxNumberOfPassengers; i++) currentPassengers [i] = aCar.currentPassengers [i]; }
- Define an equality operator (operator==) to compare two objects. Optionally compare the array or collection object. Optional iterator for a member by member comparison. int operator== (const Car& aCar);
- Define input function - C++ insertion operator for use with cin. Must invoke the array or collection object operator>>. Iterator required. friend istream& operator>> (istream& is, Car& aCar) {for (i = 0, i < maxNumberOfPassengers; i++) is >> *aCar.currentPassengers [0]; }
- Define the output function - C++ extraction operator for use with cout. Must invoke the association object operator<<. Iterator required. friend ostream& operator<< (ostream& os, Car& aCar) { for (i = 0, i < maxNumberOfPassengers; i++) os << *aCar.currentPassengers [0]; }
- Define a get and set function for the collection of association objects, e.g. Passenger* getCurrentPassenger () {return currentPassengers[0]; }
- Define get, set, remove, removeAll, exists, and exists (a specific object) assessor functions for individual association objects, e.g. Passenger* getPassenger () {return currentPassengers[0];
- Define a default destructor to deinitialize an object, ~Car () {}
- Define a main (test) function to create an association
object with several association objects and invoke a traversal
function
To code the second class, optionally, declare an
inverse traversal data member, e.g. Car * aCar;
Prior to compiling the association class, insert a forward declaration
before the association class declaration, e.g. class Car; The
complete C++ code listing is below.
Listing C++ Code Listing
for 1 to Many Association
// 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
//Required for 1:M association classes
#ifndef __PASSENGR_H //Passenger
#include "Passengr.h"
#endif
const int maxNumberOfPassengers = 4;
class Car
{
int speed; //Attribute data member
int currentPassengersIndex; //Index for array
of 1:M association objects
//1:M association object data member
//Change C array to C++ collection class with iterator
Passenger* currentPassengers [maxNumberOfPassengers
];
// Null association object for initialization of association object data member
Passenger nullPassenger;
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;
}
//Get accessor function for 1:M association collection
const Passenger* getPassengerCollection() const
;
//Set accessor function for 1:M association collection
void setPassengerCollection (Passenger* const
aPassengerCollection);
//Add function for 1:M association object data member
void addPassenger (Passenger* const aPassenger);
//throw (string)
//Remove function for 1:M association object data member
//Warning delete currentPassengers object if dynamic object
void removeLastPassenger ( ) ;//throw (string)
//Remove all function for 1:M association object data member
//Warning delete currentPassengers object if dynamic object
void removeAllPassengers ( ) ;//throw (string)
//Get accessor function for 1:M association object data member
const Passenger* getFirstPassenger() const ;//throw
(string)
//Exists function for 1:M association object data member
int existsPassenger() const ;
//Exists function for 1:M association object data member
int existsPassenger (Passenger* const aPassenger)
const ;
// void addPassenger (Passenger& aPassenger) ;
void removePassenger (Passenger& aPassenger)
;
~ 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::removePassenger(Passenger& aPassenger)
{
}
//Default constructor alternative to compiler provided default constructor
//Association object data member pointers initialized to null association object
Car::Car ()
: speed(0)
{ currentPassengersIndex = 0; //Index for array of 1:M association objects
//Initialization of array of 1:M association objects to null association objects
for ( int i = 0; i < maxNumberOfPassengers; i++) currentPassengers [i ] = &nullPassenger;
}
//Constructor with arguments
Car::Car ( int aspeed )
{ speed = aspeed; //Initialization of attributes
currentPassengersIndex = 0; //Index for array of 1:M association objects
//Initialization of array of 1:M association objects to null association objects
for ( int i = 0; i < maxNumberOfPassengers; i++) currentPassengers [i ] = &nullPassenger;
}
//Copy constructor alternative to compiler provided default copy constructor
Car::Car (const Car& aCar )
{int i = 0;
currentPassengersIndex = 0; //Index for array of 1:M association objects
speed = aCar.speed;
for ( i = 0; i < maxNumberOfPassengers; i++) currentPassengers [i ] = &nullPassenger;
//Initialization of pointers to null association object
//Commented out shallow copy
//for ( i = 0; i < maxNumberOfPassengers; i++) currentPassengers [i ] = aCar.currentPassengers [i ] ;
}
//Operator= Assignment Operator alternative to compiler provided operator=
Car& Car::operator= (const Car& aCar)
{ if (this == &aCar) return *this;
int i = 0;
speed = aCar.speed;
for (i = 0; i < maxNumberOfPassengers; i++) currentPassengers [i ] = &nullPassenger ; //Initialization of pointers to null association object
//for (i = 0; i < maxNumberOfPassengers; i++) currentPassengers [i ] = aCar.currentPassengers [i ] ;
return *this;
}
//Operator== Equality Operator
int Car::operator== (const Car& aCar) const
{ return (
//Equality check for 1:M association objects for shallow compare
//Update for the correct number of associated objects
( currentPassengers [0 ] == aCar.currentPassengers [0 ])&&
( currentPassengers [1 ] == aCar.currentPassengers [1 ])&&
( currentPassengers [2 ] == aCar.currentPassengers [2 ])&&
( currentPassengers [3 ] == aCar.currentPassengers [3 ])&&
//Equality check for attribute data members
(speed == aCar.speed)
);
}
//Operator<< extraction for cout
ostream& operator<< (ostream& os, const Car& aCar)
{ int i = 0;
os << "Object Attribute Values - Class Car" << endl;
os << "speed: " << aCar.speed << endl;
for (i = 0; i < maxNumberOfPassengers; i++)
{ if ( aCar.currentPassengers [i ] != &aCar.nullPassenger) os << " currentPassengers: " << *aCar.currentPassengers [i ] << endl;
}
return os;
}
//Operator>> insertion for cin
istream& operator>> (istream& is, Car& aCar)
{ int i = 0;
cout << "\nEnter Object Attribute Values or 0 - Class Car";
cout << "\nEnter speed : " << endl;
is >> aCar.speed;
for (i = 0; i < maxNumberOfPassengers; i++)
{ if ( aCar.currentPassengers [i ]!= &aCar.nullPassenger) is >> *aCar.currentPassengers [i ] ;
}
return is;
}
//Get accessor function for 1:M association object data member
const Passenger* Car::getFirstPassenger() const //throw (string)
{ string PassengerCollectionEmpty;
if ( currentPassengersIndex == 0 ) throw PassengerCollectionEmpty;
else return currentPassengers [currentPassengersIndex ];
}
//Get accessor function for 1:M association collection
const Passenger* Car::getPassengerCollection() const
{ return *currentPassengers;
}
//Set accessor function for 1:M association collection
void Car::setPassengerCollection (Passenger* const aPassengerCollection)
{ int i = 0;
for ( i = 0; i < maxNumberOfPassengers; i++) currentPassengers [ i ] = &nullPassenger ;
for ( i = 0; i < maxNumberOfPassengers; i++) currentPassengers [ i ] = &aPassengerCollection [ i ];
}
//Add function for 1:M association object data member
void Car::addPassenger (Passenger* const aPassenger) //throw (string)
{ string PassengerCollectionFull;
if ( currentPassengersIndex == maxNumberOfPassengers) throw PassengerCollectionFull;
else
{ currentPassengers [ currentPassengersIndex ] = aPassenger ;
currentPassengersIndex++;
}
}
//Remove function for 1:M association object data member
//Warning delete currentPassengers object if dynamic object
void Car::removeLastPassenger ( ) //throw (string)
{ string PassengerCollectionEmpty;
if ( currentPassengersIndex == 0 ) throw PassengerCollectionEmpty;
else
{ currentPassengersIndex--;
currentPassengers [currentPassengersIndex ] = &nullPassenger ;
}
}
//Remove all function for 1:M association object data member
//Warning delete currentPassengers object if dynamic object
void Car::removeAllPassengers ( ) //throw (string)
{ string PassengerCollectionEmpty;
if ( currentPassengersIndex == 0 ) throw PassengerCollectionEmpty;
for (int i = 0; i < maxNumberOfPassengers; i++) currentPassengers [i ] = &nullPassenger;
}
//Exists function for 1:M association object data member
int Car::existsPassenger() const
{ if ( currentPassengers [0 ] == &nullPassenger ) return 0; else return 1;
}
//Exists function for 1:M association object data member
int Car::existsPassenger (Passenger* const aPassenger) const
{ if ( currentPassengers [ 0 ] == &nullPassenger ) return 0;
for (int i = 0; i < maxNumberOfPassengers; i++)
{ if ( currentPassengers [i ] == aPassenger ) return 1;
}
return 0;
}
How we model association can affect the quality of
the software in terms of correctness, reliability, extendibility,
and reusability. For example, if we have excessive association
connections, then extendibility may be limited. It may be difficult
to make changes to the software without causing excessive adverse
side-effects.
In modeling association we can utilize for the software
quality techniques of weak coupling and strong cohesion. Coupling
is the degree of interconnectivity between entities. Association
connects classes and connects objects. Association is required
to accomplish the purpose of the system. However, we should strive
for a reasonable amount of association to logically connect classes
to accomplish the purpose of the system. We should avoid an excessive
amount of association in which all classes seem to be associated
with each other. A class diagram with excessive association appears
as a "spaghetti bowl" of association connections everywhere.
We should strive for a moderate degree of association for weak
coupling. With weak coupling we can make a change in one class
without unexpected adverse side-effects in another class. The
general guideline is to identify sufficient associations in a
system for an understandable class structure to accomplish the
purpose of the system.
Strong cohesion is the degree of internal relatedness of elements within a larger, more complex entity. We can get strong cohesion by ensuring attributes and operations related to the association support the purpose of the class. We can check to ensure that there are no out-of-place attributes, operations, and associations. Having sufficient (not excessive) associations can favorably affect cohesion. We should remove any associations that seem illogical or out-of-place.
In O-O modeling it is important to identify and describe relationships between classes and between objects. A relationship is a link or connection between classes or between objects. The association relationship is the "has a" relationship. The aggregation relationship is the "part of" relationship. Aggregation is a special form of association with special semantics (rules) for deletion and propagation of operations. The generalization specialization relationship is the "is a" relationship between superclasses and subclasses. Relationships are shown on the class diagram. Relationships may be specified in a relationship specification. Using a CASE tool, a class diagram showing relationships between classes may be created. The CASE tool may generate source code to implement the classes and the relationships.