Object-Oriented Design


Object-Oriented Design

oop



Learning Objectives

  • Explain the basic steps involved in an object-oriented design process
  • Explain the difference between is-a, has-a and uses-a relationships and explain how to implement each type
  • Perform an object-oriented design given a set of test requirements and produce as a result and appropriate UML class diagram
  • Implement a UML class diagram

Object-Oriented Design Process

The typical object-oriented design process can be divided into four main stages:

  1. Identify the objects
  2. Determine the relationships between the objects
  3. Determine the attributes/behaviours of the objects
  4. Design the driver

Each of these stages is described in the following sections.

1. Identify the Objects

A key stage in object-oriented design is to identify objects given a program specification, or a description of the problem domain. The programmer should work out what things the program will need to deal with and note down potential classes. As well as identifying whether certain objects will allow the program to accomplish some of its objectives.

2. Determine the Relationships

Once a candidate list of potential objects has been identified, the relationships that link these objects can be thought about. There are three main types of relationship:

  • is a: if B is a subtype of A (e.g. a UK student is a student)
  • has a: if A has B and B cannot exist without A. This can also be thought of as A owns B, (e.g. the two real numbers in a complex number do not have any meaning/use outside of the complex number, so this is a has a relationship)
  • uses a: If A has B and B can exist without A (i.e B’s existence does not depend on A’s existence)

To illustrate a use a relationship, consider an a theoretical program which represents information about simultaneous linear equations of the form:

In this problem domain, all the equations have an order (an integer). It should be possible to display all equations. The linear equations should have three floating point coefficients (e.g. 2, 1 and 7 for the first equation shown above). Simultaneous equations should consist of 2 linear equations. It should be possible to display such a set of simultaneous linear equations in the form shown above, and also to solve the equations, i.e. display the values of x and y which satisfy the equations. A UML diagram which satisfies the conditions of this problem would look like so:

UML DIAGRAM

In this UML diagram a simultaneous equation has a linear equation (in fact it has two). To implement this has a relationship, composition is used. However simultaneous equations can also use a linear equation, if we want allow the program to possibly calculate more than just the scenario given.

Aggregation is used to implement a uses a relationship. Aggregation is similar to composition, but rather than making one class a data member of the other, we make a pointer to an instance of the contained class a data member of the contained class. The pointer typically points to an instance of the contained class that already exists outside of the the scope of the containing class. Therefore, if the containing class is destroyed, the contained class can continue to exist.

To show the distinction between composition and aggregation consider the following two alternative implementations of the simultaneous equations problem domain.

equation.h using composition
class SimultaneousEquations
{
  public:
    SimultaneousEquations(){}
    void SetEquations(LinearEquation e1,
                      LinearEquation e2)
    { _e1 = e1; _e2 = e2; }
    void Display()
    { _e1.Display(); _e2.Display(); }
    void Solve();
  protected:
    LinearEquation _e1, _e2;
}
equation.h using aggregation
class SimultaneousEquations
{
  public:
    SimultaneousEquations(){}
    void SetEquations(LinearEquation *e1,
                      LinearEquation *e2)
    { _e1 = e1; _e2 = e2; }
    void Display()
    { _e1->Display(); _e2->Display(); }
    void Solve();
  protected:
    LinearEquation *_e1, *_e2;
}

The main difference between these two implementations is when using composition the type of data members is LinearEquation whereas when using aggregation it is LinearEquation*. This difference means that a SimultaneousEquations instance does not have a LinearEquation, it uses one that must already exist outside of the SimultaneousEquations class. In other words, the existence of the LinearEquation instances does not depend upon any SimultaneousEquations instance of which they are part.

For example, consider the following excerpt from the main() function that makes use of these classes when implemented using aggregation:

SimultaneousEquations s;
LinearEquation 11, 12;
l1.SetCoeffs(2.0, 1.0, 7.0);
l2.SetCoeffs(3.0, -1.0, 8.0);
s.setEquations(&l1, &l2);
s.Display();
s.Solve();

SimultaneousEquations s2;
LinearEquation 13;
l3.SetCoeffs(2.0, 4.0, -5.0);
s2.setEquations(&l1, &l3);
s2.Display();
s2.Solve();

Note that the & operator is used when passing the LinearEquation instances into the SimultaneousEquations instances. This is because the SetEquations member function expects pointer arguments

The l1 instance of LinearEquation is used in both the s and s2 instances of SimultaneousEquations. This means that any change to l1 will result in a change to both s and s2. If composition was used instead, then copies of l1 would have been made and passed into s and s2 instances, so any subsequent changes to l1 would not affect s and s2.

UML CLASS DIAGRAM

Notice in the above UML class diagram, aggregation is represented with an unfilled diamond, compared to the earlier composition which is represented by a filled diamond

3. Identify the Attributes and Behaviours

The interactions between objects are defined by their public interfaces, which is their corresponding set of public attributes and behaviours. The public interface of an object should be carefully considered, and whether it should be available to the entire program, restricted to the class hierarchy or restricted to the individual class itself. To illustrates the process of identifying attributes and behaviours, let’s return to the simultaneous equations example again.

The attributes are fairly straightforward, and were mentioned when the problem was set up. By logical thinking about the problem, the following behaviours can be identified:

  • Solve() a public member function of SimultaneousEquations
  • GetA() GetB() GetC() a public inspector function in LinearEquation that provide access to the coefficients of the equation.
  • Display() a public type dependent member function in the Equation inheritance hierarchy
  • Display() a public member function in the SimultaneousEquations class

Note that candidate objects can be eliminate whilst identifying attributes/behaviours

4. Design the Driver

The driver can be thought of as the glue that binds the objects together, or the main algorithm of the program that makes use of the objects. In C++ the driver algorithm of the program corresponds to the main() function.

UML Notation

BASIC UML DIAGRAM

The basic format of class boxes in UML is illustrated below. Remember <<abstract>> above the class names indicates that the class is an abstract base class. All other classes will be concrete classes. The symbol to the left of each data member or member function indicates its visibility, i.e.

  • + public
  • # protected
  • - private

UML DIAGRAM

The scope of attributes and behaviours can also be indicated using UML. static data members are common to all instances of the class (i.e. the have class scope), whereas non-static members are specific to an instance of a class (i.e. they have instance scope). In UML terminology, C++ static members are known as classifier members, and non-static members are known as instance members. The UML notation for classier members is to underline as illustrated below.

UML DIAGRAM

UML notation for generalisation relationships is an arrow on the line from the derived class to the base class. The notation for has a (i.e. composition) relationships is a filled diamond on the lien from the containing class to the contained class, with the diamond being at the containing class end. The notation for uses a relationship (i.e. aggregation) is an unfilled diamond instead of a filled diamond. Finally for a virtual function (a type dependent operation in an inheritance hierarchy) the UML notation is italics.


return  link
Written by Tobias Whetton