Previously we presented how to model classes and objects. Classes define attributes and operations. The purpose of this chapter is to describe attributes in the object model. The major topics are:
What is an attribute?
How to identify attributes.
How to show attributes on a class diagram.
How to update a class specification with attribute information.
How to generate C++ with a CASE tool script and how to understand the C++ source code.
The purpose of this section is to define an attribute and present to how to identify attributes, show attributes on a class diagram, describe attributes in a class specification, and generate C++ code for attributes.
An attribute is a characteristic or property
of an object. An attribute is typically an atomic literal, such
as an integer, float, character, etc. An attribute may be set
of literals, such as a string of characters. For example, an attribute
of a car is gasQuantity which is a float. An attribute is specified
with the attribute name and attribute class. The table below shows
some sample attributes.
Table Sample attributes.
----------------------------------------------------------------------------------------------------------
Class Attribute Name Attribute Data Type/Class
-----------------------------------------------------------------------------------------------------------
Car gasQuantity float
Motor rpm integer
Passenger name string
Customer creditOK boolean
Product description string
Order quantity integer
----------------------------------------------------------------------------------------------------------
To identify attributes ask the following questions for each class. What are the characteristics or properties of objects of the class? Peter Coad and Ed Yourdon suggest that the developer consider himself as an object of the class [Coad-91]. Ask the questions: As an object of the class, how am I described? As an object of the class, what do I need to know? As an object of the class, what information do I remember?
Attributes are shown on class diagrams. In the Rumbaugh
OMT and the Coad-Yourdon class diagram attributes are shown in
the middle section of the class symbol. Additionally, you may
show the attribute class/type and initial value on the class diagram
[Rumbaugh-91].
The attribute specification lists important text
information for each attribute. Its purpose is to state adequate
information to document and to code each attribute. We will first
present a CASE tool specification input form, define the specification
information, and present a sample report. The steps to create
the attribute specification using a CASE tool are listed below.
>> Run the CASE tool from Windows
>> Select "File - Open" e.g. c:\car.omt
>> Double click on a class, e.g. Car
>> Double click on an attribute in the class, e.g. gasQuantity to bring up the attribute specification form
>> Enter attribute information, e.g. description, access, etc.
>> Select "Generate - Generate Class Report and enter the report script, e.g. RPTATTR.SCT or TABATTR.SCT
>> Select "File - Edit File" and enter the report file name to view the report
The figure below is a sample attribute form in a CASE tool for the gasQuantity attribute.
A complete specification of an attribute includes the following information for the car class attribute gasQuantity. These specification items are defined in the next paragraph. Some information may not be required of each attribute. Some information is provided that may be placed in a user field in the CASE specification form above.
- Attribute name provides the name of the attribute.
- Attribute type provides the attribute type or class. It is important for code generation to state the attribute type or class.
- Description provides other information about the attribute.
- Access (visibility) states the visibility of the attribute, e.g. public, protected, private, or implementation.
- Initial value provides the initial value of the attribute. This is important for code generation to set the initial value of the attribute. For code generation ensure the initial value is filled-in.
- Minimum value provides the minimum value of the attribute.
- Maximum value provides the maximum value of the attribute.
- Constraints (limits or restrictions) provides rules, limits, or restrictions of the attribute. A constraint is a rule or limitation on a attribute value, e.g. worker_salary must be less than the boss_salary.
- User1, User2,
and User3 provide user defined fields for comments or other
information.
There are Static, Constant, and Index
check boxes to designate a static, constant, or index attribute.
If neither check box is checked then the attribute is a normal
attribute (not static and not constant and not index).
The following is additional information that may be collected for each attribute.
- Class name provides the name of the class.
- Form (object/class) states whether the attribute is an object attribute or class attribute.
- Key (Yes/No) states if the attribute is a lookup key.
- Qualifier (Yes/No) states if the attribute is qualifier that distinguishes among the set of objects at the
"many" side of an association.
- Derived (Yes/No) states if the attribute is a derived attribute that is computed from other attributes.
- Implementation states language specific information, e.g. C++ friend, const, static.
- Size states the estimated amount of memory
consumed.
Attribute access is the degree that another
object can use or manipulate an attribute value. The forms of
access in this book are based upon C++ access definitions. They
are applicable to several object-oriented languages. Private access
is the most restrictive access. With private access an
object cannot directly manipulate an attribute value. For example,
a user object cannot directly access the gasQuantity attribute
a car object. A user object cannot directly set or get a car's
gasQuantity. Protected access is a less restrictive access than
private access. With protected access an object cannot
access a protected attribute. However, protected access has special
rules that permit access to a protected attribute from a subclass.
Public access is the least restrictive form of access. With public
access an object can use or manipulate a public data member
or function member. Attributes are rarely defined with public
access.
Attribute constraints are limits or restrictions
on an attribute value. It is a rule for correctness. A very simple
constraint is that an attribute value must be within a minimum
and maximum value. For example, a bank account balance must always
be greater than zero. A constraint can show a relationship between
two objects. For example, a football player's salary must be within
the football team's salary cap.
Most attributes are object attributes. As an object attribute, each object of a class may have a unique value for the attribute. For example, the object car1 has a unique value of 14.0 for the gasQuantity attribute. Some attributes are class attributes. As a class attribute, the attribute value is applicable to all objects of the class. The attribute value is shared by all objects of the class. For example, maxNumberPassengers could be a class attribute because all objects of the car class would have the same attribute value of six. Another example of a class attribute is the number of cars created in the car class. The guideline is to create an object attribute if you can answer yes to the following question: Can the attribute have a unique value for each object? Create a class attribute if you answer yes to the following question: Does the attribute value apply to all objects of the class and is the attribute value shared by all objects of the class?
To access the attribute specification information,
you need to know the script variable for each piece of information
in the attribute specification input form. The following are sample
script variables from the attribute specification in a CASE tool.
A brief description states what occurs when the script variable
appears in a script. Attribute variables access attribute information.
Since there are multiple attributes for each class, these variables
are used within the [] (Repeat Operator), e.g. [ATTRIBUTE_NAME,
] prints out all the attributes for each class.
ATTRIBUTE_NAME Prints attribute name
ATTRIBUTE_TYPE Prints attribute type
ATTRIBUTE_INITIAL_VALUE Prints attribute initial value
ATTRIBUTE_ACCESS Prints attribute visibility
ATTRIBUTE_MAX_VALUE Prints attribute maximum value
ATTRIBUTE_MIN_VALUE Prints attribute minimum value
ATTRIBUTE_CONSTRAINT Prints attribute constraint or rule
ATTRIBUTE_DESCRIPTION Prints attribute information (remarks)
ATTRIBUTE_USER1 Prints user supplied information
ATTRIBUTE_USER2 Prints user supplied information
ATTRIBUTE_USER3 Prints user supplied information
CPP_ATTRIBUTE_STATIC Prints static if the attribute is static
CPP_ATTRIBUTE_CONSTANT Prints const if the attribute is const
The following is a sample specification report for the attribute gasQuantity.
Attribute Name: gasQuantity
Attribute Type/Class: float
Attribute Initial Value: 0.0
Attribute Access: private
Attribute Maximum Value: 99.9
Attribute Minimum Value: 0.0
Attribute Constraint: must be within minimum and maximum value
Attribute Description: gasQuantity is the amount of available gasoline in the car
Attribute User1: N/A
Attribute User2: N/A
Attribute User3: N/A
Figure Attribute Specification
Report
The abbreviated script (RPTATTR.SCT) to generate
this report from the class diagram is shown below.
[Attribute Name: ATTRIBUTE_NAME
Attribute Type/Class: ATTRIBUTE_TYPE
Attribute Initial Value: ATTRIBUTE_INITIAL_VALUE
Attribute Access: ATTRIBUTE_ACCESS
Attribute Maximum Value: ATTRIBUTE_MAX_VALUE
Attribute Minimum Value: ATTRIBUTE_MIN_VALUE
Attribute Constraint: ATTRIBUTE_CONSTRAINT
Attribute Description: ATTRIBUTE_DESCRIPTION
Attribute User1: ATTRIBUTE_USER1
Attribute User2: ATTRIBUTE_USER2
Attribute User3: ATTRIBUTE_USER3 ]
The following is sample additional information on the attribute gasQuantity. This information may be placed in a user field.
- Form of attribute - object attribute
- Key - no,
- Qualifier - no,
- Derived - no,
- Implementation - C++ private data member,
- Size - small,
The following is a sample attribute table for all attributes in the Car Simulation System. It lists key information for all attributes from the class diagram in the Car Simulation System.
| Class Name | Attribute Name | Attribute Type | Initial Value | Description |
| Vehicle | weight | float | 0 | The weight in pounds for the vehicle. |
| Car | gasQuantity | float | 0 | The amount of gasoline in a car. |
| Car | speed | int | 0 | The velocity of a car. |
| Car | totalCarsBuilt | int | 0 | The total number of cars built. |
| Car | maxNumberOfTires | int | 4 | The maximum number of tires on a car. |
| Car | maxNumberOfPassengers | int | 4 | The maximum number of passengers in a car. |
| Passenger | seatBeltStatus | int | 0 | The status of the seat belt - fastened or unfastened. |
| CellularPhone | phoneNumber | long | 0 | The phone number of person being called. |
| Tire | pressure | int | 0 | The pressure of a tire in PSI (pounds per square inch). |
| Motor | rpm | int | 0 | The revolutions per minute of a motor. |
The abbreviated script (TABATTR.SCT) to generate this table is
shown below.
SCRIPT_NOREPEAT_HEADER_BEGIN
Class Name, Attribute Name, Attribute Type, Initial Value, Description SCRIPT_NOREPEAT_HEADER_END
[CLASS_NAME, ATTRIBUTE_NAME, ATTRIBUTE_TYPE, ATTRIBUTE_INITIAL_VALUE, ATTRIBUTE_DESCRIPTION]
Once the class diagram and the attribute forms have
been completed, then C++ code can be automatically generated with
a CASE tool. We are briefly presenting how to code attributes
in C++ to be able to understand and modify the generated C++.
Attributes are coded in C++ as data members. The
purpose of this section is to present the how to code of C++ data
members for object attributes and static data members for class
attributes. An example of C++ code generated from a CASE tool
is presented. The table below is a comparison of the O-O modeling
terms and C++ terms for attributes.
Table Comparison of O-O modeling and C++ terms for attributes.
-----------------------------------------------------------------------------
O-O Modeling C++
-----------------------------------------------------------------------------
object attribute data member
class attribute static data member
-----------------------------------------------------------------------------
There are object attributes and class attributes.
An object attribute holds a value that is applicable for a specific
object. To code an object attribute in C++ declare a data member
in the following form: <class or type name> <data
member name>. An example is as follows:
float gasQuantity.
A data member may be declared as an automatic object
or a dynamic object. A data member as an automatic object has
the form of <class or type name> <object name>
as shown in the example above. A data member as a dynamic object
has the form of <class or type name> *<object
name>. An example is as follows: char
* name.
A class attribute holds a value that is shared by
all objects of the class. To code a class attribute, create a
C++ static object. Memory is allocated as soon as the object is
created. The object remains for the duration of the program. The
following is the form for a C++ static data member declaration:
static <class or type name> <object name>.
An example follows: static int maxNumberPassengers.
A static data member must be initialized outside the class declaration.
Based upon the class diagram in the figure below,
the generated C++ code is presented with constructors and destructors
in the listing below.
Listing Generated C++ source code.
--------------------Car Class Declaration - car.h ------------------
class Car {
int speed; //private data member
float gasQuantity; //private data member
public:
Car(){speed = 0; gasQuantity = 0.0;} //constructor updated
//to initialize data members
~Car(){} //destructor
};
-----------------Motor Class Declaration - motor.h -----------------
class Motor {
int rpm; //private data member
int displacement; //private data member
public:
Motor(){rpm = 0; displacement = 0;}//constructor updated
//to initialize data members
~Motor(){} //destructor
};
-------------Passenger Class Declaration - passenge.h --------------
#include <strng.h> //include file for String
enum boolean {OK, NotOK}; //enumerated data type for
//for seatBeltStatus
class Passenger {
String name; //private data member
boolean seatBeltStatus; //private data member
public:
Passenger(){name = ""; seatBeltStatus = OK;} // constructor
// updated to initialize data members
~Passenger(){} // destructor
};
The CASE tool provided all the C++ source code in the listing above except the enum boolean {OK, NotOK} statement.
To code attributes in C++, you should declare the
class, declare the attributes as data members, and initialize
the data members in the default constructor. The sample C++ code
below was generated by a CASE tool. This example shows the normal
data member "speed", the constant data member "minimumSpeed",
the static data member "totalCarsBuilt", and the global
constant "INITIAL_NUMBER".
Listing C++ to Show Data Member Declarations
//////////////////////////////////Car.h ////////////////////////////
#ifndef __CAR_H
#define __CAR_H
const int INITIAL_NUMBER = 1; //Symbolic const declaration and initialization
class Car
{ int speed; //Normal data member
const int minimumSpeed; //Constant data member
static long totalCarsBuilt; //Static data member
public:
Car () : speed(0), minimumSpeed(0) { } //constructor
~ Car ( ) { } //Destructor
};
#endif
//////////////////////////////////Car.cpp/////////////////////////////////
#include "Car.h"
long Car::totalCarsBuilt = 0; //Static data member
initialization
/////////////////////////////////main.cpp//////////////////////////////////
#include "car.h"
int main ()
{
Car car1; //Default constructor
return 0;
}
To generate the C++ class declaration with data member
declarations, we used script to generate the Car.h file and to
generate 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
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$; //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;
]
~ CLASS_NAME ( ) { } //Destructor
};
#endif
//////////////////////////TRUNCATE_EIGHT$CLASS_NAME.cpp
file////////////////////////////////////
#include "TRUNCATE_EIGHT$CLASS_NAME.h"
SELECT_WHEN ATTRIBUTE_IS_STATIC
[ATTRIBUTE_TYPE CLASS_NAME$::$ATTRIBUTE_NAME
= ATTRIBUTE_INITIAL_VALUE$; //Static data member initialization]
[
OPERATION_RETURN_TYPE CLASS_NAME::OPERATION_NAME(CPP_OPERATION_PARAMETERS) CPP_OPERATION_CONSTANT
{
OPERATION_CODE
}
]
To code attributes and their accessor functions in
C++, create non-static and static data members and accessor functions
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 should have a class name, static data members,
static data member accessor functions, non-static data members,
non-static data member accessor functions, a constructor, function
members, and a destructor. The class diagram on the right shows
sample C++ identifiers that are also in the code listings.
const Symbolic Name const int INITIAL_NUMBER = 1;
Key aspects of coding a class with non-static and static data members and accessor functions are:
- define a default constructor to initialize an object with default data member values in the initializer list, e.g. Car ():gasQuantity (0),minGasQuantity (0) { } ,
- define a constant attribute as a C++ const data member, e.g. const int minGasQuantity; A const data member must be initialized in the initializer list. Set accessor functions are not permitted for const data members.
- define a symbolic constant as a C++ const outside the class declaration, e.g. const int INITIAL_NUMBER = 1;
- define get assessor functions to get the data member values, e.g. int getgasQuantity () const {return gasQuantity;},
- define set assessor functions to set data member values, e.g. void setgasQuantity (float agasQuantity) { gasQuantity = agasQuantity; },
- declare a static data member for a class attribute, e.g. static long totalCarsBuilt;
- define static get assessor functions for static data members, e.g. static long getTotalCarsBuilt () {return totalCarsBuilt; },
- define static set assessor functions for static data members, e.g. static void setTotalCarsBuilt (long atotalCarsBuilt) {totalCarsBuilt = atotalCarsBuilt; },
- define a default destructor to deinitialize the object, ~Car () { },
- initialize static data member outside the class declaration, e.g. long Car::totalCarsBuilt = 0; A static data member may not be initialized in the default constructor or constructor with arguments.
- define a main (test) function to create an object
and access non-static and static data members.
The complete C++ code listing is below.
Listing C++ Code Listing
for Non-Static and Static Data Members and Accessor Function
// 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
const int INITIAL_NUMBER = 1; //Symbolic constant
class Car
{ float gasQuantity; //Non-static data member
const float minGasQuantity; //Constant data member
static int totalCarsBuilt; //Static data member
public:
//Default constructor alternative to compiler provided default constructor
Car ();
//Constructor with arguments
Car ( float agasQuantity, float aminGasQuantity ) ;
//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
float getgasQuantity() const
{ return gasQuantity;
}
//Set accessor function for non-static attribute data member
void setgasQuantity (const float agasQuantity)
{ gasQuantity = agasQuantity;
}
//Get accessor function for static attribute data member
static int gettotalCarsBuilt()
{ return totalCarsBuilt;
}
//Set accessor function for static attribute data member
static void settotalCarsBuilt ( const int atotalCarsBuilt)
{ totalCarsBuilt = atotalCarsBuilt;
}
//Get accessor function for constant attribute data member
float getminGasQuantity()
{ return minGasQuantity;
}
~ Car ( ) { } //Destructor
};
#endif
//////////////////////.cpp file//////////////////////////////////////////////
#include "Car.h"
int Car::totalCarsBuilt = 0; //Static data member
initialization
// Functions for class Car
//Default constructor alternative to compiler provided default constructor
Car::Car () : gasQuantity(0), minGasQuantity(0)
{
}
//Constructor with arguments
Car::Car ( float agasQuantity, float aminGasQuantity )
: minGasQuantity (aminGasQuantity)
{ gasQuantity = agasQuantity; //Initialization of attributes
}
//Copy constructor alternative to compiler provided default copy constructor
Car::Car (const Car& aCar ) : minGasQuantity (aCar.minGasQuantity)
{int i = 0;
gasQuantity = aCar.gasQuantity;
}
//Operator= Assignment Operator alternative to compiler provided operator=
Car& Car::operator= (const Car& aCar)
{ if (this == &aCar) return *this;
gasQuantity = aCar.gasQuantity;
return *this;
}
//Operator== Equality Operator
int Car::operator== (const Car& aCar) const
{ return (
(gasQuantity == aCar.gasQuantity) //Equality check for attribute data members
);
}
//Operator<< extraction for cout
ostream& operator<< (ostream& os, const Car& aCar)
{ os << "Object Attribute Values - Class Car" << endl;
os << "gasQuantity: " << aCar.gasQuantity << endl;
os << "minGasQuantity: " << aCar.minGasQuantity << endl;
os << "totalCarsBuilt: " << aCar.totalCarsBuilt << endl;
return os;
}
//Operator>> insertion for cin
istream& operator>> (istream& is, Car& aCar)
{ cout << "\nEnter Object Attribute Values or 0 - Class Car";
cout << "\nEnter gasQuantity : " << endl;
is >> aCar.gasQuantity;
return is;
}
/////////////////////////////////main.cpp/////////////////////////////////
#include "car.h"
#include <iostream.h>
int main ()
{ Car car1; //invokes the default constructor
Car car2 (10.0, 0); //invokes the constructor with arguments
car1.setgasQuantity (20.0); //invokes the set assessor function
Car::settotalCarsBuilt (1000);//invokes the set assessor function for
//static member
cout << car1;
return 0;
}
///////////////////////////Sample Output///////////////////////////////
Object Attribute Values - Class Car
gaaQuantity: 20
minGasQuantity: 0
totalCarsBuilt: 1000
How we model attributes can affect the quality of
the resulting software. Our goal is to create high quality software.
Correctly identifying and specifying attributes can contribute
to creating high quality software that is correct, reliable, extendible,
and reusable. For example, specifying correct constraints such
as minimum and maximum attribute values supports correctness and
reliability.
The two software quality techniques most applicable to attributes are encapsulation and strong cohesion. We can support encapsulation (information hiding) by declaring attributes as private. This may prevent corrupting an attribute with an illegal value. We can support strong cohesion by identifying attributes that are related together in a class. There should be no unneeded or out-of-place attributes. This is also a guideline in relational data base management systems. There, a normalization rule to avoid data redundancy states that each field of a table should represent a fact about the key, the whole key, and nothing but the key. The object-oriented version of this rule for strong cohesion is that each attribute value should represent a fact about the object, the whole object, and nothing but the object. We can check for strong cohesion by examining each attribute in a class to ensure that it describes an object of the class. For example, the attribute "current national debt" is an out-of place attribute in a customer class.
To model classes and objects, attributes and operations must be identified and described. An attribute is a characteristic or property of an object. An operation is an action or set of steps to accomplish something. Attributes and operations are graphically shown on the class diagram. They are described in the attribute specification form and operation specification form. A CASE tool is useful to create class diagrams and specification forms. A CASE tool may generate source code for the declaration of attributes and operations in the class declaration. A CASE tool may generate fill-in code templates which must be updated with code statements to accomplish the purpose of each operation.