Previous Section  < Free Open Study >  Next Section

Selected C++ Example #5


//Example #5

// This example with its five parts will illustrate how a

// uses relationship may be implemented. The most common

// method of implementing the uses relationship is via

// containment. Containment relationships always imply

// uses, but uses do not always imply containment. The second

// example illustrates uses via containment. How does a Meal

// know the name of its Melon object when it wants to use

// it? Answer: It knows its name because it contains it, i.e.,

// Meals contain Melons.

// This brings up an interesting point. In a design

// discussion, if a member of the design team states that

// some class X uses class Y, always ask, ''How does the X

// object know the name of the Y object?''. While the answer is

// often, ''Because X contains the Y object,'' there are five

// other possibilities. We will discuss these in the context

// of a car using a gas station.



// How does the Car object know the name of the GasStation

// object? It clearly doesn't contain it; Cars do not contain

// GasStations.



// The first, and most popular, method is that the Car object

// is given the GasStation object as an explicit parameter to

// its method. This is illustrated by the Car1 class.



// The second method is for all Car objects to go to the

// same global GasStation. They know its name by hardcoding

// it into the Car's method(s). Some consider this to be

// a special case of the first method since global variables

// are really implicit parameters. Because it has a different

// form, we implement it in the class Car2.



// The third method is for the wealthy. When their Car runs

// out of gasoline, they build a GasStation, pump gas, and

// tear down the gas station. While this is ridiculous in

// the domain of cars and gas stations, it is appropriate in

// many domains. The Car3 class captures this method of using

// local objects.



// The fourth method is that when a car is born, god gives

// it its gas station for later use. Each car has a one-to-one

// association with its gas station. We will talk more about

// associations in Chapter 7, but this serves as a good example

// of the implementation of associations. Do not confuse

// an association with containment by reference. The

// relationships are very different at design time, however,

// C++ does not distinguish them. The class Car4 implements

// this feature.



// The fifth method is for the Car class to ask a third-party

// object for a gas station. Of course, this only postpones

// the problem. How did we know the name of the third-party

// object? It must be one of the other five methods: i.e.,

// containment, passed as an argument, a global object,

// a local object, an association, or ... ask a fourth-

// party object ... (ad nauseum).

// The Car5 class illustrates this implementation of uses by

// asking a contained map object for a gas station.



#include <iostream.h>



// The GasStation is the same class in all five methods of

// implementing uses relationships. The only interest is how

// we gain knowledge of the name of a particular gas station

// we want to use. Our gas station has a constructor; it

// can take gas deliveries, it can change its price (no grades

// of gasoline here, for simplification), and most

// importantly, it can dispense gasoline to anyone willing

// to pay. In our case, Car objects.

class GasStation{

private:

     double quantityOfGas;

     double price;

public:

     GasStation(double, double quantity=0.0);

     void take_gas_delivery(double);

     void change_price(double);

     double give_gasoline(double);

};



GasStation::GasStation(double start_price, double quantity)

{

     quantityOfGas = quantity;

     price = start_price;

}



void

GasStation::take_gas_delivery(double quantity)

{

     quantityOfGas += quantity;

}



void

GasStation::change_price(double new_price)

{

     price = new_price;

}



double

GasStation::give_gasoline(double amount)

{

     double gas_purchased;



     gas_purchased = amount / price;

     quantityOfGas -= gas_purchased;

     if (quantityOfGas < 0) {

          gas_purchased += quantityOfGas;

          quantityOfGas = 0.0;

}

     return(gas_purchased);

}







// The Car1 class implements the first method of implementing

// a uses relationship which is not containment. The Car's

// get_gasoline method takes not only money, but also a

// GasStation object, as an argument.

class Car1 {

private:

     double milesPerGallon;

     double fuelCapacity;

     double gasInTank;

     double mileage;

public:

     Car1(double, double, double=0.0, double=0.0);

     void get_gasoline(GasStation&, double);

     void drive(double);

     void print();

};





Car1::Car1(double mpg, double capacity, double starting_gas,

                       double miles)

{

     milesPerGallon = mpg;

     fuelCapacity = capacity;

     gasInTank = starting_gas;

     mileage = miles;

}





// The drive method computes the gasoline needed to travel the

// desired distance. It then checks to see if there is enough

// gasoline in the car. If not, a message is printed to the user and

// the car moves as far as it can. If the car has less than 10% of its

// remaining capacity, a warning is printed to the user.

void

Car1::drive(double miles)

{

     double gas_needed;



     gas_needed = miles / milesPerGallon;

     if (gas_needed > gasInTank) {

          mileage += gasInTank * milesPerGallon;

          cerr << ''You ran out of gasoline after travelling '';

          cerr << gasInTank * milesPerGallon << '' miles.\n'';

          gasInTank = 0.0;

     }

     else {

          mileage += miles;

          gasInTank -= gas_needed;

          if (gasInTank < 0.1 * fuelCapacity) {

                                cerr << ''Warning: You have only enough gas

to go '';

                                cerr << GasInTank * miles_per_gallon << ''

miles.\n'';

         }

     }

}







// The get_gasoline method is the most interesting. In this

// implementation the Car class knows the name of its gas station

// because it is passed into the method. The gas station is used to

// get the gasoline, and a number of checks are made to be sure the

// gas station wasn't out of gas or that the user didn't spill some

// gas. Of course, this implementation could be made more

// elaborate but this will suffice to demonstrate the

// implementation of ''Car uses GasStation. ''

void

Car1::get_gasoline(GasStation& myGasStation, double money)

{

      double gas_received;



      gas_received = myGasStation.give_gasoline(money);

      if (gas_received == 0) {

           cerr << ''Sorry the gas station was out of gas.\n'';

      }

      else {

           gasInTank += gas_received;

           if (gasInTank > fuelCapacity) {

                      cerr << ''You spilled '' << (gasInTank -

fuelCapacity);

                      cerr << '' gallons of gas on the ground.\n'';

                      gasInTank = fuelCapacity;

         }

     }

}





void

Car1::print()

{

     cout << ''The car gets '' << miles_per_gallon;

     cout << '' miles per gallon.\nIt currently has '';

     cout << gasInTank << '' gallons of gasoline on board.\n'';

     cout << ''Its maximum fuel capacity is '' << fuelCapacity;

     cout << '' gallons\nand it has '' << mileage << '' miles.\n''

}





// The Car2 class demonstrates the implementation of a uses

// relationship through a global GasStation object. In this case,

// all Car2 objects go to the same GasStation, in this case

// ''Global_GasStation.'' Some designers consider this a special

// case of implementing the uses relationship through a parameter

// (e.g., Car1), but since it looks different in implementation, I

// felt it should be included.



GasStation Global_GasStation(1.25, 10000);

class Car2 {

private:

     double milesPerGallon;

     double fuelCapacity;

     double gasInTank;

     double mileage;

public:

     Car2(double, double, double=0.0, double=0.0);

     void get_gasoline(double);

     void drive(double);

     void print();

};





Car2::Car2(double mpg, double capacity, double starting_gas,

                        double miles)

{

     milesPerGallon = mpg;

     fuelCapacity = capacity;

     gasInTank = starting_gas;

     mileage = miles;

}





void

Car2::drive(double miles)

{

     double gas_needed;



     gas_needed = miles / milesPerGallon;

     if (gas_needed > gasInTank) {

          mileage += gasInTank * milesPerGallon;

          cerr << ''You ran out of gasoline after travelling '';

          cerr << gasInTank * milesPerGallon << '' miles.\n'';

          gasInTank = 0.0;

     }

     else {

          mileage += miles;

          gasInTank -= gas_needed;

          if (gasInTank < 0.1 * fuelCapacity) {

                     cerr << ''Warning: You have only enough gas

to go '';

                     cerr << GasInTank * milesPerGallon << ''

miles.\n'';

         }

     }

}

// Note the use of the Global_GasStation object to get_gasoline.

void

Car2::get_gasoline(double money)

{

     double gas_received;



     gas_received = Global_GasStation.give_gasoline(money);

     if (gas_received == 0) {

          cerr << ''Sorry the gas station was out of gas.\n'';

     }

     else {

          gasInTank += gas_received;

          if (gasInTank > fuelCapacity) {

                      cerr << ''You spilled '' << (gasInTank -

fuelCapacity);

                     cerr << '' gallons of gas on the ground.\n'';

                     gasInTank = fuelCapacity;

          }

     }

}





void

Car2::print()

{

     cout << ''The car gets '' << milesPerGallon;

     cout << ''miles per gallon.\nIt currently has '';

     cout << gasInTank << '' gallons of gasoline on board.\n'';

     cout << ''Its maximum fuel capacity is '' << fuelCapacity;

     cout << ''gallons\nand it has'' << mileage << '' miles.\n'';

}







// The Car3 class implements its uses relationship through a local

// object that is built ''on-the-fly'' at runtime. Whenever the

// get_gasoline() method is called on Car3 objects, the method

// builds itself a gas station, uses the gas station, and then

// destroys it.This is inefficient but is used in some

// implementations,

class Car3 {

private:

     double milesPerGallon;

     double fuelCapacity;

     double gasInTank;

     double mileage;

public:

     Car3(double, double, double=0.0, double=0.0);

     void get_gasoline(double);

     void drive(double);

     void print();

};

Car3::Car3(doublempg, double capacity, double starting_gas,

                      double miles)

{

     milesPerGallon = mpg;

     fuelCapacity = capacity;

     gasInTank = starting_gas;

     mileage = miles;

}





void

Car3::drive(double miles)

{

     double gas_needed;



     gas_needed = miles / milesPerGallon;

     if (gas_needed > gasInTank) {

          mileage += gasInTank * milesPerGallon;

          cerr << ''You ran out of gasoline after travelling '';

          cerr << gasInTank * milesPerGallon << '' miles.\n'';

          gasInTank = 0.0;

     }

     else {

          mileage += miles;

          gasInTank -= gas_needed;

          if (gasInTank < 0.1 * fuelCapacity) {

                     cerr << ''Warning: You have only enough gas

to go '';

                     cerr << gasInTank * milesPerGallon <<

'' miles.\n'';

          }

      }

}



// Note the creation and use of local_station in order to get

// gasoline for the car. While a bit silly in the domain of cars and

// gas stations, there are domains where the use of a local object

// as part of the implementation of a method is perfectly

// appropriate.

void

Car3::get_gasoline(double money)

{

      double gas_received;

      GasStationlocal_station(1.25, 1000);



      gas_received = local_station.give_gasoline(money);

      if (gas_received == 0) {

           cerr << ''Sorry the gas station was out of gas.\n'';

      }

       else {

            gasInTank += gas_received;

            if (gasInTank > fuelCapacity) {

                 cerr << ''You spilled '' << (gasInTank -

fuelCapacity);

                 cerr << '' gallons of gas on the ground.\n'';

                 gasInTank = fuelCapacity;

            }

     }

}



void

Car3::print()

{

      cout << ''The car gets '' << milesPerGallon;

      cout << '' miles per gallon.\nIt currently has '';

      cout << gasInTank << '' gallons of gasoline on board.\n'';

      cout << ''Its maximum fuel capacity is '' << fuelCapacity;

      cout << ''gallons\nand it has'' << mileage << ''miles.\n'';

}





// The Car4 class uses a different twist in implementing its uses

// relationship. When each Car4 object is built, it is told who its

// gas station is. This gas station is stored in the Car4 object for

// later use. In this case, the class will make a copy of the gas

// station, which is safer because it avoids problems of the gas

// station given to the Car4 object being destroyed before the Car4

// object. If data sharing is desired, then only the pointer,

// and not the stuff to which it points, should be copied. For an

// example of this form of shallow copying which also provides a

// safety mechanism, see the Air Traffic Controller example in

// Chapter 9 (Example #3). Do not confuse association with

// containment by reference. While the Car4 class does have a

// pointer to a GasStation as a data member, this is not containment

// by reference, it is association through a referential attribute.

// While C++ does not let us distinguish these two relationships,

// the distinction is available AND important to designers. If this

// were containment, we could ignore GasStations at some high

// level of design. The fact that it is assocation means we cannot.

class Car4 {

private:

     double milesPerGallon;

     double fuelCapacity;

     double gasInTank;

     double mileage;

     GasStation* myStation;

public:

     Car4(GasStation*, double, double, double=0.0, double=0.0);

     ~Car4();

     void get_gasoline(double);

     void drive(double);

     void print();

};

// Note the constructor copying the GasStation passed to it using

// the default copy constructor for GasStation. (Note: GasStation

// is a fixed-sized class so the default copy constructor does not

// cause any memory leakage/heap corruption problems. See Appendix B

// for a more thorough explanation of memory leakage/heap corruption

// problems of copy constructors/assignment operators.

Car4::Car4(GasStation* station, double mpg, double capacity,

                        double starting_gas, double miles)

{

     myStation = new GasStation(*station);

     milesPerGallon = mpg;

     fuelCapacity = capacity;

     gasInTank = starting_gas;

     mileage = miles;

}





Car4::~Car4()

{

     delete myStation;

}





void

Car4::drive(double miles)

{

     double gas_needed;



     gas_needed = miles / milesPerGallon;

     if (gas_needed > gasInTank) {

          mileage += gasInTank * milesPerGallon;

          cerr << ''You ran out of gasoline after travelling '';

          cerr << gasInTank * milesPerGallon << '' miles.\n'';

          gasInTank = 0.0;

     }

     else {

          mileage += miles;

          gasInTank -= gas_needed;

          if (gasInTank< 0.1* fuelCapacity) {

                     cerr << ''Warning: You have only enough gas

to go '';

                     cerr << gasInTank * milesPerGallon <<

 '' miles.\n'';

           }

       }

}





// Note the use of the referential attribute in the get_gasoline method.

void

Car4::get_gasoline(double money)

{

     double gas_received;

   gas_received = myStation->give_gasoline(money);

   if (gas_received == 0) {

        cerr << ''Sorry the gas station was out of gas.\n'';

   }

   else {

        gasInTank += gas_received;

        if (gasInTank > fuelCapacity) {

                   cerr << ''You spilled '' << (gasInTank -

fuelCapacity);

                   cerr << '' gallons of gas on the ground.\n'';

                   gasInTank = fuelCapacity;

        }

   }

}





void

Car4::print()

{

     cout << ''The car gets '' << milesPerGallon;

     cout << '' miles per gallon.\nIt currently has '';

     cout << gasInTank << '' gallons of gasoline on board.\n'';

     cout << ''Its maximum fuel capacity is '' << fuelCapacity;

     cout << '' gallons\nand it has '' << mileage << '' miles.\n'';

}





// The Car5 class implements its uses relationship by asking a

// third-party class, in this case a Map object. Of course, asking

// a third-party only postpones the answer to the question, ''How

// does a class know the name of the object it wishes to use? ''We

// will have to use one of the other five methods of implementing

// uses. In this case, I chose containment, i.e., Car 5 contains a

// Map object.



// We first implement our Map class. I have chosen a naive algorithm

// for finding a GasStation on a Map. Each Map object has four

// quadrants and a car has an x, y location. The map takes the x-

// and y-coordinates of a car and returns one of four stations.

// While it is true that this is naive (the attentive will notice

// that a Map has no way of getting gasoline deliveries to its

// GasStations), it is sufficient to demonstrate uses.

class Map {

private:

     GasStation* quadrant[4];

public:

     Map();

     ~Map();

     GasStation* get_station(int, int);

};



// The constructor for Map simply builds the four GasStations , one

// for each quadrant.

Map::Map()

{

      quadrant[0] = newGasStation(1.45, 1000);

      quadrant[1] = newGasStation(1.30, 200);

      quadrant[2] = newGasStation(2.10, 10000);

      quadrant[3] = newGasStation(1.10, 678);

}



Map::~Map()

{

     int i;



     for (i=0; i < 4; i++) {

          delete quadrant[i];

     }

}





// When a Car5 object asks the Map for a station, it gives the Map its

// x- and y-coordinates. The Map returns the appropriate

// GasStation.

GasStation*

Map::get_station(int x, inty)

{

     if (x > 0) {

          if (y > 0)

                     return(quadrant[0]);

          else

                     return(quadrant[3]);

     }

     else {

          if (y > 0)

                     return(quadrant[1]);

          else

                     return(quadrant[2]);

     }

}



// The Car5 class contains the Map by value. Even if it contained it

// by reference, this would still be a containment relationship.

// It would not be association through a referential attribute

// like the Car4 class. The difference is significant at design time.

// I can state that Maps are not as important a class in this domain

// as Car5 and GasStation objects because the former is contained in

// a top-level class and does not have to be discussed at high-level

// design time.

class Car5 {

private:

     int loc_x, loc_y;

     double milesPerGallon;

     double fuelCapacity;

     double gasInTank;

     double mileage;

     Map myMap;

public:

      Car5(int, int, double, double, double=0.0, double=0.0);

      void get_gasoline(double);

      void drive(double);

      void print();

};



// The constructor for Map is called automatically before the

// constructor for each Car5 object. The destructor for Map will

// likewise be called whenever a Car5 object is destroyed.

Car5::Car5(int x, int y, double mpg, double capacity,

                            double starting_gas, double miles)

{

       loc_x = x;

       loc_y = y;

       milesPerGallon = mpg;

       fuelCapacity = capacity;

       gasInTank = starting_gas;

       mileage = miles;

}



void

Car5::drive(double miles)

{

      double gas_needed;



      gas_needed = miles / milesPerGallon;

      if (gas_needed > gasInTank) {

           mileage += gasInTank * milesPerGallon;

           cerr << ''You ran out of gasoline after travelling '';

           cerr << gasInTank * milesPerGallon << '' miles.\n'';

           gasInTank = 0.0;

      }

      else {

           mileage += miles;

           gasInTank -= gas_needed;

           if (gasInTank < 0.1 * fuelCapacity) {

                      cerr << ''Warning: You have only enough gas

to go '';

                      cerr << gasInTank * milesPerGallon <<

''miles.\n'';

         }

     }

}





// Note the use of the contained, third-party object called MyMap to

// get a GasStation for use by the get_gasoline method.

void

Car5::get_gasoline(double money)

{

     double gas_received;

    GasStation *myStation = myMap.get_station(loc_x, loc_y);



    gas_received = myStation->give_gasoline(money);

    if (gas_received == 0) {

         cerr << ''Sorry the gas station was out of gas.\n'';

    }

    else {

         gasInTank += gas_received;

         if (gasInTank>fuelCapacity) {

                     cerr << ''You spilled '' << (gasInTank -

fuelCapacity);

                     cerr << '' gallons of gas on the ground.\n'';

                     gasInTank = fuelCapacity;

         }

     }

}



void

Car5::print()

{

     cout << ''The car at location ('' << loc_x << '', '' << loc_y <<

'') gets '';

     cout << milesPerGallon << '' miles per gallon.\nIt currently

has '';

     cout << gasInTank << '' gallons of gasoline on board.\n'';

     cout << ''Its maximum fuel capacity is '' << fuelCapacity;

     cout << '' gallons\nand it has '' << mileage << '' miles.\n''

}



void

main()

{

// The following code tests the Car1 class :

      GasStation g1(1.18, 300), g2(1.45, 2300);

      Car1 mycar1(22, 18, 10);



      mycar1.print();

      mycar1.drive(200);

      mycar1.print();

      mycar1.drive(100);

      mycar1.print();

      mycar1.get_gasoline(g1, 15.00);

      mycar1.print();



// The following code tests the Car2 class :



      Car2 mycar2(30, 20, 3);



      mycar2.print();

      mycar2.drive(200);

      mycar2.print();

      mycar2.get_gasoline(20.00);

      mycar2.print));

      mycar2.get_gasoline(10.00);

      mycar2.print();





// The following code tests the Car3 class:



      Car3 mycar3(9, 13, 5);



      mycar3.print();

      mycar3.drive(38);

      mycar3.print();

      mycar3.get_gasoline(10.00);

      mycar3.print();

      mycar3.drive(150);

      mycar3.print();



// The following code tests the Car4 class:



      Car4 mycar4(&g2, 18, 25, 12);



      mycar4.print();

      mycar4.get_gasoline(15.00);

      mycar4.print();

      mycar4.drive(150);

      mycar4.print();

      mycar4.get_gasoline(3.69);

      mycar4.print();



// The following code tests the Car5 class:



      Car5mycar5(10, -34, 35, 18, 15);



      myCar5.print();

      myCar5.drive(250);

      mycar5.print();

      myCar5.drive(250);

      myCar5.print();

      myCar5.get_gasoline(10.00);

      myCar5.print();

      myCar5.get_gasoline(20.00);

      mycar5.print();

}

    Previous Section  < Free Open Study >  Next Section