Скачиваний:
64
Добавлен:
15.03.2015
Размер:
4.31 Mб
Скачать

FIGURE 5.1

Linking Figure 3.1 with actual C# program.

Chapter 5 • YOUR FIRST OBJECT-ORIENTED C# PROGRAM 123

private Instance variables public methods

Elevator class

LoadPassenger

currentFloor

requestedFloor

totalFloorsTraveled

passenger

Initiate

 

New

 

Floor

 

Request

ReportStatistics

 

Person class

Person

randomNumber Generator

NewFloorRequest

elevator class

01: // A simple elevator simulation 02:

03: using System; 04:

05: class Elevator 06: {

07:private int currentFloor = 1;

08:private int requestedFloor = 0;

09:private int totalFloorsTraveled = 0;

10:private Person passenger;

11:

12:public void LoadPassenger()

13:{

14:passenger = new Person();

15:}

16:

17:public void InitiateNewFloorRequest()

18:{

19:requestedFloor = passenger.NewFloorRequest();

20:Console.WriteLine("Departing floor: " + currentFloor

21:+ " Traveling to floor: " + requestedFloor);

22:totalFloorsTraveled = totalFloorsTraveled +

23:Math.Abs(currentFloor - requestedFloor);

24:currentFloor = requestedFloor;

25:}

26:

27:public void ReportStatistic()

28:{

29:Console.WriteLine("Total floors traveled: " + totalFloorsTraveled);

30:}

31:}

32:

33:class Person

34:{

35:private System.Random randomNumberGenerator;

37:public Person()

38:{

39:randomNumberGenerator = new System.Random();

40:}

41:

42:public int NewFloorRequest()

43:{

44:// Return randomly generated number

45:return randomNumberGenerator.Next(1,30);

46:}

47:}

48:

49:class Building

50:{

51:public static Elevator elevatorA;

53:

public static void Main()

Person class

54:{

55:elevatorA = new Elevator();

56:elevatorA.LoadPassenger();

57:elevatorA.InitiateNewFloorRequest();

58:elevatorA.InitiateNewFloorRequest();

59:elevatorA.InitiateNewFloorRequest();

60:elevatorA.InitiateNewFloorRequest();

61:elevatorA.InitiateNewFloorRequest();

62:

elevatorA.ReportStatistic();

Building class

63:}

64:}

A Deeper Analysis of SimpleElevatorSimulation.cs

The following sections discuss important subjects related to Listing 5.1.

Defining a Class to Be Instantiated in Our Own Program

You are already familiar with how a class is defined. I still highlight line 5 because this is the first time you define your own class that is being instantiated and therefore used to create an object in your program.

05: class Elevator

Initializing Variables

The new feature presented in line 7 is the combination of a declaration statement and an assignment statement. This is called an initialization. private int currentFloor; is a

124 C# PRIMER PLUS

straightforward declaration and, by adding = 1 at the end, you effectively assign 1 to currentFloor during or immediately after the creation of the object to which this instance variable belongs.

07:private int currentFloor = 1;

A variable often needs an initial value before it can be utilized in the computations in which it takes part. currentFloor in line 7 is a good example. We want the elevator to start at floor number 1 so we initialize it to this value.

When a variable is assigned a value before it takes part in any computations, we say it is being initialized. Two important methods exist to initialize an instance variable. You can

Assign the variable an initial value in its declaration (as in line 7).

Utilize a C# feature called a constructor. The source code contained in a constructor is executed when an object of a class is being created, which is the ideal time for any initializations. Constructors are informally presented shortly.

Instance variables that are not explicitly initialized in the source code are automatically assigned a default value by the C# compiler. For example, had we not assigned the value 1 to currentFloor in line 7, C# would have given it the default value 0.

Initialize All Your Variables Explicitly

Do not rely on the C# compiler to initialize your variables. Explicit initializations make the code clearer and avoid the source code relying on compiler initializations and their default values, which can change in the future and introduce errors into your source code.

Tip

It is possible to declare a class member without explicitly stating its accessibility by leaving out the public or private keyword. The accessibility is then, by default, private.

Nevertheless, use the private keyword to declare all private class members to enhance clarity.

Declaring a Variable Representing an Object of a Specific Class

Line 10 states that the instance variable passenger can hold an object created from the Person class.

10:private Person passenger;

We haven’t yet put a particular Person object here; we would need an assignment statement to do this, which you will see a bit further in this section. So far, we are merely expressing that an Elevator object is able to “transport” one passenger, which must be a Person. For example, no Dog, Airplane, or Submarine objects can be stored in the Elevator, had we ever defined such classes in our program.

Chapter 5 • YOUR FIRST OBJECT-ORIENTED C# PROGRAM 125

Now, jump down to lines 33–47 for a moment. By defining the Person class in these lines, you have effectively created a new custom-made type. So, instead of merely being able to declare a variable to be of type int or type string, you can also declare it to be of type Person, which is exactly what you do in line 10.

Note

int and string are built-in, pre-defined types. The classes you write and define in your source code are custom-made types.

Notice that line 10 is closely linked with lines 14, 19, and 33–47. Line 14 assigns a new Person object to passenger; line 19 utilizes some of the functionality of the passenger variable (and hence a Person object) by calling one of its methods, and lines 33–47 define the

Person class.

Tip

The sequence of class member declarations and definitions is arbitrary. However, try to divide the class members into sections containing members with similar access modifiers to improve clarity.

The following is one example of a commonly used style:

class ClassName

{

declarations of private instance variables private method definitions

public method definitions

}

Instance Variables Representing the State of an Elevator Object

Lines 7–10 contain the list of instance variables I found relevant to describe an Elevator object for this simulation.

07:private int currentFloor = 1;

08:private int requestedFloor = 0;

09:private int totalFloorsTraveled = 0;

10:private Person passenger;

The state of any Elevator object is described by the instance variables declared in the following lines:

Line 7—The currentFloor variable keeps track of the floor on which an Elevator object is situated.

Line 8—A new floor request will be stored in the requestedFloor variable. The Elevator object will attempt to fulfill this request as swiftly as possible (line 24), depending on the speed of your computer’s processor.

126C# PRIMER PLUS

Line 9—When the Elevator object is created, you can regard it to be “brand new.” It has never moved up or down, so totalFloorsTraveled initially must contain the value 0. This is achieved by initializing it to the value 0.

The amount of floors traveled is added to totalFloorsTraveled (lines 22-23), just before a trip is finished (line 24).

Line 10—The passenger(s) of the elevator are ultimately the decision makers of the floor numbers visited by the elevator. The Person object residing inside passenger chooses which floors our Elevator object must visit. A request is obtained from the passenger in line 19 and assigned to the requestedFloor variable.

Abstraction and the Choice of Instance Variables

Recall the discussion about abstraction in the beginning of Chapter 3. The goal associated with abstraction is to identify the essential characteristics of a class of objects that are relevant to our computer program.

During the process of deciding which instance variables to include in a class definition, and the declaration of them in the source code, the programmer is applying the conceptual idea of abstraction in a very practical, hands-on manner.

For example, I could have attempted to include an instance variable such as color of type string in the Elevator class, declared as follows:

private string color; probably useless for our purposes

But where can I make any use of it? I could perhaps assign the string red to it and write a method that would print the following:

My color is: red

on the command console whenever called; but the exercise would be irrelevant to what we are trying to achieve in our simple little simulation so it is wasteful and complicates our Elevator class unnecessarily.

Another programmer might have included an instance variable of the Elevator class that measures the number of trips performed by the elevator; the programmer might call it totalTrips and declare it as follows:

private int totalTrips; potentially useful

The Elevator class could then be designed so that a method would add 1 to totalTrips every time a request had been fulfilled. This instance variable would enable us to keep track of another, perhaps important, statistic and so it has the potential of being useful.

As you can see, there are many different ways to represent a real world object when deciding which instance variables to include. The choice of instance variables depends on the individual programmer and what he or she wants to do with each object.

Chapter 5 • YOUR FIRST OBJECT-ORIENTED C# PROGRAM 127

Enabling Elevator Object to Load a New Passenger

An Elevator object must be able to load a new Person object. This is accomplished by the LoadPassenger method residing inside an Elevator object (see line 12). LoadPassenger() is accessed from outside its object, and so it must be declared public. The call to LoadPassenger is made in line 56 of Listing 5.1.

12:public void LoadPassenger()

Talking About Classes and Objects

Consider the following declaration statement:

private Person passenger;

You will come across many different, more or less correct, ways to describe what this sentence is about and, in particular, how to identify an object. In my opinion, the following is a good description but perhaps a bit cumbersome:

passenger is a variable declared to hold an object of class Person.”

Due to this lengthy description, I often use the following expression:

passenger is a variable declared to hold a Person object.”

In Chapter 6, “Types Part I: The Simple Types,” you will see why the following description is probably the most correct but also the longest:

passenger is a variable declared to hold a reference to an object of class Person.”

Creating a New Object with the new Keyword

Line 14 goes hand-in-hand with line 10.

14:passenger = new Person();

new, as applied in this line, is a keyword in C# used to instantiate a new object. It will create a new instance (object) of the Person class. This new object of class Person is then assigned to the passenger variable. passenger now contains a Person object that can be called and used to perform actions.

Note

When an object is instantiated, an initialization of this object’s instance variables is often needed. A constructor is a special kind of method that performs this initialization task.

To specify that a method is a constructor, it must have the same name as its class. Thus, a constructor for the Person class is called Person() (see line 37). Whenever a new Person object is created with the keyword new, the Person() constructor is automatically called to perform the necessary initializations. This explains the parentheses in line 14, which are always required when any method, and any constructor, is called.

128 C# PRIMER PLUS

Person() is a constructor of the Person class and called due to the keyword new

14: passenger = new Person()

class name and constructor name must be identical

33: class Person

Person() constructor called.

34: {

35: private System.Random randomNumberGenerator;

36:

37:public Person()

38:{

39:randomNumberGenerator = new System.Random();

40:}

source code initializing the new object

Letting an Elevator Object Receive and Fulfill Passenger Requests

By calling the InitiateNewFloorRequest method, the Elevator object is briefly instructed to:

Get a new request from the passenger (line 19)

Print out its departure and destination floor (lines 20-21)

Update the totalFloorsTraveled statistic (lines 21-22)

Fulfill the request of the passenger (line 24).

17:public void InitiateNewFloorRequest()

18:{

19:requestedFloor = passenger.NewFloorRequest();

20:Console.WriteLine(“Departing floor: “ + currentFloor

21:+ “ Traveling to floor: “ + requestedFloor);

22:totalFloorsTraveled = totalFloorsTraveled +

23:Math.Abs(currentFloor - requestedFloor);

24:currentFloor = requestedFloor;

25:}

Chapter 5 • YOUR FIRST OBJECT-ORIENTED C# PROGRAM 129

Let’s begin with line 19:

In line 19 the NewFloorRequest method of passenger is called with:

object of class Person

method of Person object

Calling the NewFloorRequest

19:passenger.NewFloorRequest() method of Person object

dot operator

Here, we are using the following syntax introduced earlier:

ObjectName.MethodName(Optional_arguments)

Dot operator

where the dot operator (.) is used to refer to a method residing inside an object. You have already used the dot operator many times. For example you used the dot operator when you called the WriteLine method of the System.Console with System.Console.WriteLine(“Bye Bye!”). This time, however, instead of calling a prewritten method of the .NET Framework you are calling your own custom-made method from the Person class.

Note

You can see the fixed class definition in the source code. An object, on the other hand, is dynamic and comes to life during execution of the program.

Instead of calling a method residing inside the same object, you are calling a method residing in another object. Here, the Elevator object is said to send a message to the Person object. A call to a method residing in another object is similar to calling a local method. In Figure 5.2, I have linked Figure 3.1 with the general mechanism illustrated in Figure 4.1. The upper half of the figure illustrates the call to NewFloorRequest, following the same graphical style as in Figure 4.1. Step 1 symbolizes the call to NewFloorRequest. There are no formal parameters specified for NewFloorRequest, so no arguments are passed to the method. This step is equivalent to step 1 of the lower half of the figure. After the NewFloorRequest has terminated, it returns a value of type int, as symbolized by the arrow marked 2. The graphics equivalent of step 2 is shown in the lower half of this figure. As seen previously (line 18 of Listing 4.1 in Chapter 4), after step 2 is completed, you can substitute passenger.NewFloorRequest() with this value of type int. Finally, this value can be assigned to the variable requestedFloor with the assignment operator = .

130 C# PRIMER PLUS

FIGURE 5.2

 

 

 

 

 

 

 

 

 

 

Invoking a method of

 

The invoking method inside Elevator

The invoked method inside Person

another object.

 

 

 

 

 

 

 

 

 

 

 

17: public void InitiateNewFloorRequest()

method headerof NewFloorRequest()

 

18: {

invoking NewFloorRequest of passenger

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

19: .....passenger.NewFloorRequest()

42: public int NewFloorRequest()

 

 

:

 

 

Zoom

 

 

 

Zoom

 

 

 

 

 

 

 

 

 

 

 

:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

25: }

passenger.NewFloorRequest()

 

 

 

 

 

 

 

2

 

value of type int returned

no arguments passed

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

 

 

 

 

 

 

int NewFloorRequest()

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Equivalent to

no formal parameters

1

 

 

 

Elevator object

 

2

Person object

 

 

 

InitiateNewFloorRequest

 

NewFloorRequest

currentFloor

ReportStatistics

 

randomNumberGenerator

requestedFloor

 

 

 

 

 

totalFloorsTraveled

 

 

 

passenger

 

 

 

Loadpassenger

 

 

 

Sending a message to another object

 

 

Sending back requested information

 

 

private Instance variables

 

 

 

public methods

 

 

 

After the newly received request from its passenger in line 19, the Elevator object has a current position held by the currentFloor variable and a different floor held in requestedFloor to which it must travel. If the Elevator is “brand new” and has just been created, currentFloor will have a value of 1. If the Elevator has already been on one or more “rides,” currentFloor will be equal to the destination of the latest “ride.” Line 20 will print out the value of the currentFloor by using the now familiar WriteLine method. The newly received passenger request is then printed in line 21.

Note that lines 22 and 23 represent just one statement; you can verify this by locating the single semicolon found at the end of line 23. The statement has been spread over two lines due to lack of space. Line 23 has been indented relative to line 22; this indicates to the reader that we are dealing with only one statement.

Chapter 5 • YOUR FIRST OBJECT-ORIENTED C# PROGRAM 131

Absolute Value

The absolute value of a positive number is the number itself.

The absolute value of a negative number is the number with the negative sign removed.

For example,

The absolute value of (-12) is 12.

The absolute value of 12 is 12.

In Mathematics, absolute value is indicated with two vertical lines, surrounding the literal or variable, as shown in the following:

|-12| = 12

The use of Math.Abs()

The Abs method of the Math class residing inside the .NET Framework returns the absolute value of the argument sent to it. Consequently, the method call Math.Abs(99) returns 99, whereas

Math.Abs(-34) returns 34.

When calculating the distance traveled by the elevator, we are interested in the positive number of floors traveled, irrespective of whether the elevator is moving up or down. If we calculate the number of floors traveled using the following expression:

currentFloor – requestedFloor

we will end up with a negative number of floors traveled whenever requestedFloor is larger than currentFloor. This negative number is then added to totalFloorsTraveled; so the number of floors traveled in this case has mistakenly been deducted rather than added from totalFloorsTraveled. We can avoid this problem by adding the absolute value of (currentFloor

-requestedFloor), as has been done in line 23:

Math.Abs(currentFloor - requestedFloor);

The statement can be broken down into the following sub-instructions:

1. Math.Abs(currentFloor - requestedFloor)—Calculate the number of floors traveled on the next elevator ride.

2. totalFloorsTraveled + Math.Abs(currentFloor - requestedFloor)—Add the number of floors traveled on the next elevator ride to the current value of totalFloorsTraveled.

3. Assign the result of 2 to totalFloorsTraveled.

Having totalFloorsTraveled on both sides of the assignment operator might seem odd at first. However, this is a very useful operation adding the number of floors traveled on the latest elevator ride to the original value of totalFloorsTraveled. totalFloorsTraveled is the “odometer” of the elevator, constantly keeping track of the total amount of floors it has traveled. More details about this type of statement will be provided in Chapter 6.

Surprisingly, the simple assignment statement in line 24 represents the “elevator ride”. It satisfies the request from passenger. By assigning the value of the requestedFloor variable to

132 C# PRIMER PLUS

currentFloor, we can say that the elevator has “moved” from the currentFloor value it contained prior to this assignment to the requestedFloor.

The Person Class: The Template for an Elevator Passenger

Line 33 begins the definition of the second custom written class, which will be used as a template to create an object.

33: class Person

As we have seen, a Person object is created and kept inside an Elevator object from which its NewFloorRequest method is called, telling the Elevator object where its passenger wants to go next.

In Line 35, an object of the Person class has been enabled to make floor requests by equipping it with an object containing a method that can generate random numbers. This object is kept in the randomNumberGenerator variable. The class this object is instantiated from is found in the .NET class library and is called System.Random(). Line 35 declares the randomNumberGenerator to contain an object of type System.Random.

35:private System.Random randomNumberGenerator;

Because randomNumberGenerator is an instance variable of the Person class, it is declared private. Note that after this declaration, the randomNumberGenerator is still empty; no object has yet been assigned to this variable. We must assign an object to the randomNumberGenerator before any random numbers can be generated by this variable; lines 37–40 fulfill this important task by containing a constructor for the Person class.

The constructor concept has already been discussed and I won’t drill further into this subject now. It is not important to fully understand the constructor mechanism now, as long as you are aware that the essential part is line 39, where a new object of class System.Random is assigned to the randomNumberGenerator of any newly created object of class Person.

37:public Person()

38:{

39:randomNumberGenerator = new System.Random();

40:}

The NewFloorRequest defined in lines 42–46 will, when called, generate and return a random number that indicates passenger’s next floor request. Line 45 finds a random number between 1 and 30 (specified in the parentheses .Next(1,30)). The return keyword sends this random number back to the caller which, in this case, is the InitiateNewFloorRequest method of the Elevator object. The details of the System.Random class will not be discussed any further here. If you want to investigate this class further, please use the .NET Framework Documentation.

42:public int NewFloorRequest()

43:{

44:// Return randomly generated number

45:return randomNumberGenerator.Next(1,30);

46:}