Selected C Example 5

 <  Free Open Study  >  

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(); } 
 <  Free Open Study  >  


Object-Oriented Design Heuristics
Object-Oriented Design Heuristics (paperback)
ISBN: 0321774965
EAN: 2147483647
Year: 1996
Pages: 180

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net