Moving Function Content into Member Functions

In the implementation of anAccumulator, you moved the content of the Accumulator() function into the Apply() function with minimal changes. Next, take a look at the header and implementation files for aController, which embodies much of what used to be in the Calculator() function (see Listings 19.4 and 19.5).

Listing 19.4. aController Header
  1: #ifndef ControllerModuleH
  2: #define ControllerModuleH
  3:
  4: #include "ExternalInterfaceModule.h"
  5: #include "AccumulatorModule.h"
  6: #include "TapeModule.h"
  7:
  8: namespace SAMSCalculator
  9: {
 10:    class aController
 11:    {
 12:       public:
 13:
 14:          aController
 15:          (
 16:             anExternalInterface &theExternalInterface,
 17:             anAccumulator &theAccumulator,
 18:             aTape &theTape
 19:          );
 20:
 21:          int Operate(void);
 22:
 23:       private:
 24:
*25:          anExternalInterface &myExternalInterface;
*26:          anAccumulator &myAccumulator;
*27:          aTape &myTape;
 28:
 29:          bool TestOK
 30:          (
 31:             anAccumulator &theAccumulator,
 32:             const aRequest &theRequest,
 33:             const float theExpectedResult
 34:
 35:          ) const;
 36:
 37:          void SelfTest(void) const;
 38:          void DisplayAccumulator(void) const;
 39:          void DisplayTape(void) const;
 40:    };
 41: };
 42:
 43: #endif

The header is straightforward, so let's begin an examination of the implementation with a look at the constructor in Listing 19.5.

Listing 19.5. aController Constructor
 1:    aController::aController
 2:    (
 3:       anExternalInterface &theExternalInterface,
 4:       anAccumulator &theAccumulator,
 5:       aTape &theTape
 6:    ):
 7:       myExternalInterface(theExternalInterface),
 8:       myAccumulator(theAccumulator),
 9:       myTape(theTape)
10:    {
11:    };

There is no default constructor, so this is the constructor that must be used to make aController. It requires theExternalInterface, theAccumulator, and theTape as arguments. The arguments are references to instances of the indicated classes. In lines 25–27 of the aController header, you can see the member variables that are initialized are also references.


Normally, you may recall, the compiler will not allow you to assign anything to a reference once the reference has been defined. However, the references in lines 25–27 of the declaration have only been declared at that point. As far as the compiler is concerned, they are not defined until the flow of control has reached the constructor body. Therefore, these references can be initialized by the constructor initializer section in lines 16–18 of the implementation.

Next, let's look at a change to the SelfTest() function in Listing 19.6.

Listing 19.6. aController SelfTest() Function
  1:    void aController::SelfTest(void) const
  2:    {
 *3:       anAccumulator TestAccumulator;
  4:
  5:       try
  6:       {
  7:          if
  8:             (
 *9:                TestOK
*10:                (
*11:                   TestAccumulator,
*12:                   aRequest(aRequest::add,3),
*13:                   3
*14:                )
 15:                &&
 16:                TestOK
 17:                (
 18:                   TestAccumulator,
 19:                   aRequest(aRequest::subtract,2),
 20:                   1
 21:                )
 22:                &&
 23:                TestOK
 24:                (
 25:                   TestAccumulator,
 26:                   aRequest(aRequest::multiply,4),
 27:                   4
 28:                )
 29:                &&
 30:                TestOK
 31:                (
 32:                   TestAccumulator,
 33:                   aRequest(aRequest::divide,2),
 34:                   2
 35:                )
 36:             )
 37:          {
 38:             cout << "Test OK." << endl;
 39:          }
 40:          else
 41:          {
 42:             cout << "Test failed." << endl;
 43:          };
 44:       }
 45:       catch (...)
 46:       {
 47:          cout << "Test failed because of an exception.";
 48:       };
 49:    };

Line 3 shows one of the most important differences from the procedural version: This SelfTest() function instantiates anAccumulator and performs all tests on that object, rather than on myAccumulator. This avoids disruption of the current calculator internal state and simplifies testing. The TestOK() function has been modified to use this object, which is provided as an argument. Lines 9–14 show such a call.


The SelfTest() TestAccumulator will be destroyed when SelfTest() is done. This will have no effect on aController's myAccumulator.

Line 12 shows the aRequest constructor being used in the actual arguments to TestOK() to create a temporary request for use by the test.

Next, let's look at the use of one of the references provided by main() to the constructor of aController (see Listing 19.7).

Listing 19.7. aController DisplayTape() Function
 1:    void aController::DisplayTape(void) const
 2:    {
 3:       int NumberOfElements = myTape.NumberOfElements();
 4:
 5:       for ( int Index = 0; Index < NumberOfElements; Index++ )
 6:       {
 7:          myExternalInterface. DisplayRequest(myTape.Element(Index));
 8:          myExternalInterface.DisplayEndOfLine();
 9:       };
10:
11:       DisplayAccumulator();
12:    };

In line 7, myExternalInterface function DisplayRequest() is used to take care of displaying the request Operator() and Operand().


Finally, the Operate() function implements the real control flow of the calculator, as shown in Listing 19.8.

Listing 19.8. aController Operate() Function
  1:    int aController::Operate(void)
  2:    {
 *3:       aRequest Request = myExternalInterface.NextRequest();
  4:
 *5:       while (Request.Operator() != aRequest::stop)
  6:       {
  7:          try
  8:          {
 *9:             switch (Request.Operator())
 10:             {
*11:                case aRequest::selftest:
 12:
 13:                   SelfTest();
 14:                   break;
 15:
*16:                case aRequest::querytape:
 17:
 18:                   DisplayTape();
 19:                   break;
 20:
*21:                case aRequest::query:
 22:
 23:                   DisplayAccumulator();
 24:                   break;
 25:
 26:                default:
 27:
*28:                   myTape.Add(Request);
*29:                   myAccumulator.Apply(Request);
 30:
 31:             };
 32:
 33:             Request = myExternalInterface.NextRequest();
 34:          }
*35:          catch (runtime_error RuntimeError)
 36:          {
 37:             cerr << "Runtime error: " << RuntimeError.what() << endl;
 38:          }
*39:          catch (...)
 40:          {
 41:             cerr <<
 42:                "Non runtime_error exception " << "caught in Controller.Operate." <<
 43:                endl;
 44:          };
 45:       };
 46:
 47:       return 0;
 48:    };

Line 3 gets aRequest from myExternalInterface and line 5 loops as long as the Operator() is not aRequest::stop.


Line 9 shows that the switch statement can use the enum nested within the aRequest class.

Lines 11, 16, and 21 are cases receiving the flow of control based on the Request.Operator() tested in line 9.

Lines 28 and 29 use myTape and myAccumulator to perform the central functions of the calculator.

And, of course, lines 35 and 39 catch any exceptions without allowing the loop to be interrupted.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.34.85