Class Design Lab Guidelines for ITSS
Class Design Lab Guidelines for ITSS
1. SUBMISSION GUIDELINE
To submit this lab, you and your teammate have to push all the work to the group
GitHub repository, following the naming convention MSTeamsName-TeamNumber
(e.g., [Link].20251-01).
For this lab, you are required to check out a branch named release/lab06,
contains all tasks related to class design.
The deadline for submission will be updated by the lecturer or the teaching assistant.
Therefore, you need to pay attention to avoid late submissions.
2. OVERVIEW
In the previous lab, with the completion of the combined analysis class diagram, you
concluded the architectural design process of the software development process.
From this point onward, you will transition to one of the most crucial stages — the
detailed design process.
Overall, this process can be divided into three smaller stages: class design, interface
design and data modeling. In this lab, you will begin with class design, guided step
by step in Section 3, using the analysis class diagram from the previous lab as input.
Class Design can be quite challenging, and even a bit vague, since you may not yet
have a complete picture of the entire system. But don’t worry. The purpose of this
course is to help you build a GOOD design. The class design you create today will go
through many refinements until the end of the course. So keep going and try your
best in this lab.
Please notice that: All the images/diagrams are merely examples, so they can be
inaccurate to the problem statement. You need to check carefully to ensure that your
class diagram(s) are consistent with your use case model.
3. CLASS DESIGN
3.1. INITIAL DESIGN CLASSES
In this step, we try to map the analysis classes and design elements (e.g., class, group
of classes, package, subsystem). A design class should have a single well-focused
purpose and should do one thing well. We would identify design classes by
considering each class in the unified class diagram from architectural design, along
with its class stereotype. Note that we have not applied any design patterns 1 in
this lab yet (which will be learned at the end of this course).
3.1.1. Design Boundary Classes
1 A software design pattern is a general, reusable solution to a commonly occurring problem within a given
context in software design. See more at this link.
In analysis, it was sufficient to specify the attribute name only, unless the
representation was a requirement that was to constrain the designer.
During Design, for each attribute, define the following:
• Its name, which should follow the naming conventions of both the
implementation language and the project.
• Its type, which will be an elementary data type supported by the
implementation language.
• Its default or initial value, to which it is initialized when new instances of
the class are created.
• Its visibility, which will take one of the following values:
o Public: The attribute is visible both inside and outside the package
containing the class.
o Protected: The attribute is visible only to the class itself, to its
subclasses, or to friends of the class (language dependent).
o Private: The attribute is visible only to the class itself and to friends of
the class.
For persistent classes, also include whether the attribute is persistent (the default)
or transient. Even though the class itself may be persistent, not all attributes of the
class need to be persistent.
Derived attributes and operations indicate a constraint between values. They can be
used to describe a dependency between attribute values that must be maintained by
the class. They do not necessarily mean that the attribute value is always calculated
from the other attributes.
3.2.2. Define operations
To identify operations on design classes:
• Study the responsibilities of each corresponding analysis class, creating an
operation for each responsibility. Use the description of the responsibility as
the initial description of the operation.
• Study the Use-Case Realizations in the class participations to see how the
operations are used by the Use-Case Realizations. Extend the operations, one
Use-Case Realization at a time, refining the operations, their descriptions,
return types and parameters. Each Use-Case Realization's requirements, as
regards classes, are textually described in the Flow of Events of the Use-Case
Realization.
• Study the use-case Special Requirements, to make sure that you do not miss
implicit requirements on the operation that might be stated there.
Use-Case Realizations cannot provide enough information to identify all operations.
To find the remaining operations, consider the following:
• Is there a way to initialize a new instance of the class, including connecting it
to instances of other classes to which it is associated?
• Is there a need to test to see if two instances of the class are equivalent?
• Is there a need to create a copy of a class instance?
• Are any operations required on the class by mechanisms which they use?
(Example: a “garbage collection” mechanism may require that an object be
able to drop all of its references to all other objects in order for unused
resources to be freed.)
Operations should be named to indicate their outcome. For example, getBalance()
versus calculateBalance(). One approach for naming operations that get and set
properties is to simply name the operation the same name as the property. If there
is a parameter, it sets the property; if not, it returns the current value.
You should name operations from the perspective of the client asking for a service
to be performed by the class. For example, getBalance() versus receiveBalance().
The same applies to the operation descriptions. Descriptions should always be
written from the operation USER’s perspective. What service does the operation
provide?
It is best to specify the operations and their parameters using implementation
language syntax and semantics. This way the interfaces will already be specified in
terms of the implementation language when coding starts.
In addition to the short description of the parameter, be sure to include things like:
• Whether the parameter should be passed by value or by reference, and if by
reference, is the parameter changed by the operation? By value means that
the actual object is passed. By reference means that a pointer or reference to
the object is passed.
• The signature of the operation defines the interface to objects of that class,
and the parameters should therefore be designed to promote and define what
that interface is. For example, if a parameter should never be changed by the
operation, then design it so that it is not possible to change it — if the
implementation environment supports that type of design.
• Whether parameters may be optional and/or have default values when no
value is provided.
• Whether the parameter has ranges of valid values.
The fewer parameters you have, the better. Fewer parameters help to promote
understandability, as well as maintainability. The more parameters clients need to
understand, the more tightly coupled the objects are likely to be conceptually.
One of the strengths of OO is that you can manipulate very rich data structures,
complete with associated behavior. Rather than pass around individual data fields
(for example, StudentID), strive to pass around the actual object (for example,
Student). Then the recipient has access to all the properties and behavior of that
object.
Operation visibility is the realization of the key object-orientation principle of
encapsulation.
● Public members are accessible directly by any client.
● Protected members are directly accessible only by instances of subclasses.
● Private members are directly accessible only by instances of the class to
which they are defined.
How do you decide what visibility to use? Look at the Interaction diagrams on which
the operation is referenced. If the message is from outside of the object, use public.
If it is from a subclass, use protected. If it’s from itself, use private. You should define
the most restrictive visibility possible that will still accomplish the objectives of the
class. Client access should be granted explicitly by the class and not taken forcibly.
Visibility applies to attributes as well as operations. In the UML, you can specify the
access clients have to attributes and operations. Export control is specified for
attributes and operations by preceding the name of the member with the following
symbols:
● + Public
● # Protected
● - Private
Association
Associations represent structural relationships between objects of different classes;
they connect instances of two or more classes together for some duration.
You can use associations to show that objects know about other objects. Sometimes,
objects must hold references to each other to be able to interact; for example, to send
messages to each other. Thus, in some cases, associations may follow from
interaction patterns in Sequence diagrams or Communication diagrams.
Most associations are simple (exist between exactly two classes), and are drawn as
solid paths connecting pairs of class symbols. Ternary relationships are also
possible. Sometimes a class has an association to itself. This does not necessarily
mean that an instance of that class has an association to itself; more often, it means
that one instance of the class has associations to other instances of the same class.
An association may have a name that is placed on, or adjacent to the association path.
The name of the association should reflect the purpose of the relationship and be a
verb phrase. The name of an association can be omitted, particularly if role names
are used.
Avoid names like "has" and "contains," as they add no information about what the
relationships are between the classes.
Each end of an association has a role in relationship to the class on the other end of
the association. The role specifies the face that a class presents on each side of the
association. A role must have a name, and the role names on opposite sides of the
association must be unique. The role name should be a noun indicating the
associated object's role in relation to the associating object.
The use of association names and role names is mutually exclusive: one would not
use both an association name and a role name. For each association, decide which
conveys more information.
The role name is placed next to the end of the association line of the class it describes.
In the case of self-associations, role names are essential to distinguish the purpose
for the association.
In the above example, the Professor participates in two separate association
relationships, playing a different role in each.
Multiplicity lets you know the lower and the upper bound number of relationships
that a given object can have with another object. Many times you do not know what
the maximum number of instances may be, and you will use the “*” to specify that
this number is unknown.
The most important question that multiplicity answers: Is the association
mandatory? A lower bound number that is greater than zero indicates that the
relationship is mandatory.
Multiplicity
Multiplicity can be defined as: The number of instances one class relates to one
instance of another class.
• For each role, you can specify the multiplicity of its class and how many
objects of the class can be associated with one object of the other class.
• Multiplicity is indicated by a text expression on the role. The expression is a
comma-separated list of integer ranges.
• It is important to remember that multiplicity is referring to instances of
classes (objects) and their relationships.
• Multiplicity must be defined on both ends of the association.
Multiplicity is indicated by a text expression on the role.
Aggregation
Aggregation is a stronger form of association which is used to model a whole-part
relationship between model elements. The whole/aggregate has an aggregation
association to the its constituent parts. A hollow diamond is attached to the end of
an association path on the side of the aggregate (the whole) to indicate aggregation.
Since aggregation is a special form of association, the use of multiplicity, roles,
navigation, and so forth is the same as for association.
Sometimes a class may be aggregated with itself. This does not mean that an instance
of that class is composed of itself (that would be silly). Instead, it means that one
instance if the class is an aggregate composed of other instances of the same class.
Some situations where aggregation may be appropriate include:
• An object is physically composed of other objects (for example, car being
physically composed of an engine and four wheels).
• An object is a logical collection of other objects (for example, a family is a
collection of parents and children).
• An object physically contains other objects (for example, an airplane
physically contains a pilot).
Aggregation should be used only where the "parts" are incomplete outside the
context of the whole. If the classes can have independent identity outside the context
provided by other classes, or if they are not parts of some greater whole, then the
association relationship should be used.
When in doubt, an association is more appropriate. Aggregations are generally
obvious. A good aggregate should be a natural, coherent part of the model. The
meaning of aggregates should be simple to understand from the context. Choosing
aggregation is only done to help clarify; it is not something that is crucial to the
success of the modeling effort.
The use of aggregation versus association is dependent on the application you are
developing. For example, if you are modeling a car dealership, the relationship
between a car and the doors might be modeled as aggregation, because the car
always comes with doors, and doors are never sold separately. However, if you are
modeling a car parts store, you might model the relationship as an association, as
the car (the body) might be independent of the doors.
Navigability
The navigability property on a role indicates that it is possible to navigate from an
associating class to the target class using the association. This may be implemented
in a number of ways: by direct object references, by associative arrays, hash-tables,
or any other implementation technique that allows one object to reference another.
• Navigability is indicated by an open arrow placed on the target end of the
association line next to the target class (the one being navigated to). The
default value of the navigability property is true (associations are bi-
directional by default).
• When no arrowheads are shown, the association is assumed to be navigable
in both directions.
During Use-Case Analysis, associations (and aggregations) may have been assumed
to be bi-directional (that is, communication may occur in both directions).
• During Class Design, the association’s navigability needs to be refined so that
only the required communication gets implemented.
• Navigation is readdressed in Class Design because we now understand the
responsibilities and collaborations of the classes better than we did in Use-
Case Analysis. We also want to refine the relationships between classes. Two-
way relationships are more difficult and expensive to implement than one-
way relationships. Thus, one of the goals in Class Design is the reduction of
two-way (bi-directional) relationships into one-way (unidirectional)
relationships.
• The need for navigation is revealed by the use cases and scenarios. The
navigability defined in the class model must support the message structure
designed in the interaction diagrams.
Dependency
During Analysis, we assumed that all relationships were structural (associations or
aggregations). In Design, we must decide what type of communication pathway is
required.
A dependency relationship denotes a semantic relationship between model
elements, where a change in the supplier may cause a change in the client.
There are four options for creating a communication pathway to a supplier object:
• Global: The supplier object is a global object. Is the receiver a “global?” If so,
establish a dependency between the sender and receiver classes in a class
diagram containing the two classes.
• Parameter: The supplier object is a parameter to, or the return class of, an
operation in the client object. Is the reference to the receiver passed as a
parameter to the operation? If so, establish a dependency between the sender
and receiver classes in a Class diagram containing the two classes.
• Local: The supplier object is declared locally (that is, created temporarily
during execution of an operation). Is the receiver a temporary object created
and destroyed during the operation itself? If so, establish a dependency
between the sender and receiver classes in a class diagram containing the two
classes.
• Field: The supplier object is a data member in the client object.
A dependency is a type of communication pathway that is a transient type of
relationship. These occur when the visibility is global, parameter, or local.
Look at each association relationship and determine whether it should remain an
association or become a dependency. Associations and aggregations are structural
relationships (field visibility). Association relationships are realized by variables
that exist in the data member section of the class definition. Any other relationships
(global, local, and parameter visibility) are dependency relationships.
Generalization
Generalization can be defined as: A specialization/generalization relationship, in
which objects of the specialized element (the child) are substitutable for objects of
the generalized element (the parent). (The Unified Modeling Language User Guide,
Booch, 1999.)
• The subclass may be used where the superclass is used, but not vice versa.
The child inherits from the parent.
• Generalization is transitive. You can always test your generalization by
applying the “is a kind of” rule. You should always be able to say that your
specialized class “is a kind of” the parent class.
• The terms “generalization” and “inheritance” are generally interchangeable,
but if you need to distinguish, generalization is the name of the relationship.
Later in this module, we will contrast “vanilla” packages, as defined above, with
subsystems.
When identifying classes, you should group them into packages, for organizational
and configuration management purposes.
The Design Model can be structured into smaller units to make it easier to
understand. By grouping Design Model elements into packages and subsystems,
then showing how those groupings relate to one another, it is easier to understand
the overall structure of the model.
You might want to partition the Design Model for a number of reasons:
• You can use packages and subsystems as order, configuration, or delivery
units when a system is finished.
• Allocation of resources and the competence of different development teams
might require that the project be divided among different groups at different
sites.
• Subsystems can be used to structure the Design Model in a way that reflects
the user types. Many change requirements originate from users; subsystems
ensure that changes from a particular user type will affect only the parts of
the system that correspond to that user type.
• Subsystems are used to represent the existing products and services that the
system uses.
When the boundary classes are distributed to packages, there are two different
strategies that can be applied. Which one to choose depends on whether or not the
system interfaces are likely to change greatly in the future.
• If it is likely that the system interface will be replaced, or undergo
considerable changes, the interface should be separated from the rest of the
Design Model. When the user interface is changed, only these packages are
affected. An example of such a major change is the switch from a line-oriented
interface to a window-oriented interface.
• If no major interface changes are planned, changes to the system services
should be the guiding principle, rather than changes to the interface. The
boundary classes should then be placed together with the entity and control
classes with which they are functionally related. This way, it will be easy to
see what boundary classes are affected if a certain entity or control class is
changed.
Mandatory boundary classes that are not functionally related to any entity or control
classes, should be placed in separate packages, together with boundary classes that
belong to the same interface.
If a boundary class is related to an optional service, group it in a separate subsystem
with the classes that collaborate to provide the service. The subsystem will map onto
an optional component that will be provided when the optional functionality is
ordered.
A package should be identified for each group of classes that are functionally related.
There are several practical criteria that can be applied when judging if two classes
are functionally related. These are, in order of diminishing importance:
• If changes in one class's behavior and/or structure necessitate changes in
another class, the two classes are functionally related.
• It is possible to find out if one class is functionally related to another by
beginning with a class — for example, an entity class — and examining the
impact of it being removed from the system. Any classes that become
superfluous as a result of a class removal are somehow connected to the
removed class. By superfluous, we mean that the class is only used by the
removed class, or is itself dependent upon the removed class.
• Two objects can be functionally related if they interact with a large number of
messages, or have an otherwise complicated intercommunication.
• A boundary class can be functionally related to a particular entity class if the
function of the boundary class is to present the entity class.
• Two classes can be functionally related if they interact with, or are affected by
changes in, the same actor. If two classes do not involve the same actor, they
should not lie in the same package. The last rule can, of course, be ignored for
more important reasons.
• Two classes can be functionally related if they have relationships between
each other (associations, aggregations, and so on). Of course, this criterion
cannot be followed mindlessly but can be used when no other criterion is
applicable.
• A class can be functionally related to the class that creates instances of it.
These two criteria determine when two classes should not be placed in the same
package:
• Two classes that are related to different actors should not be placed in the
same package.
• An optional and a mandatory class should not be placed in the same package.
3.3.3. Draw general class diagram
If you can put everything in a detailed class diagram (attribute, operation,
relationship), you may not need to draw a general class diagram.
If there are too many classes with detailed properties, you may need to draw a
general class diagram, which includes main design classes, packages and
subsystems. You don’t need to show the detailed members in packages or
subsystems that you draw in the general class diagram.
You are required to create a directory named DetailedDesign/ClassDesign/ to
submit all your work.
For all the class diagrams, you must submit both the Astah project files (.asta) and
the exported image files (in PNG or JPG format). Don’t forget to name the files in a
straightforward way.
For all the class design specification, you must submit the specification files (follow
the given template in Subsection 3.2) for each class. Don’t forget to name the files
according to the corresponding class.
DetailedDesign/
└── ClassDesign/
└── ClassDiagram/
└── asta/
└── [Link]
└── … (put your files here)
└── images/
└── [Link]
└── … (put your files here)
└── ClassDesignSpecification/
└── [Link]
└── … (put your files here)