4.3. Method Invocation and Dot Notation

Now that you understand how methods are formally specified, how do we represent in code that a method is being invoked on an object? The name of the reference variable representing the object that the method is called on is followed with a dot (period) and then the method (function) call. For example:

// Instantiate a Student object.
Student x = new Student();

// A method is called on Student object x, asking it to
// register for course MATH 101, section 10.
x.RegisterForCourse("MATH 101", 10);  // This is a method call

Because we are using a dot to "glue" the reference variable to the method name, this is informally known as dot notation. And, in referring to the following logic, we can describe this process as "calling a method on object x":

x.RegisterForCourse("MATH 101", 10);

4.3.1. Arguments vs. Parameters

A bit of terminology: to make sure that everyone understands the difference between parameters and arguments, both of which are programming language terms. Let's define both terms here.

A parameter is a locally scoped variable, declared in a method header, which temporarily comes into existence while a method is executing. An argument is a value (or reference) that is passed to the method. For example, when the following method is invoked:

public void Foo(int bar) {...}

a variable named bar of type int temporarily comes into existence and is initialized with the value of the argument that is passed in when the method is invoked from client code:

// Here, we're invoking the Foo method, passing in an argument value
// of 3.
x.Foo(3);

In this case, parameter bar assumes the argument value 3.

While the method is executing, it can then use the parameter as a variable as it sees fit:

public void Foo(int bar) {.
  // Use the value of bar as appropriate. 
						if (bar > 17) { 
    // Pseudocode.
    Do something nifty! 
}
// "bar" goes out of scope here...it's only defined within the Foo 
						//  method. 

The parameter bar ceases to exist—that is, goes out of scope (a concept we discussed in Chapter 1)—when the method exits.

Although the terms parameter and argument are frequently and casually used interchangeably, the bottom line is that they are different concepts: arguments are values; parameters are variables.

4.3.2. Objects As the Context for Method Invocation

In an object-oriented programming language (OOPL), an object serves as the context for a method call. We can thus think of the notation "x.method_call(...)" as "talking to object x"; specifically, "talking to object x to request it to perform a particular method." Let's use a simple example to illustrate this point.

With respect to household chores, a person is capable of

  • Mowing the lawn

  • Taking out the trash

  • Washing the dishes

Expressing this abstraction as C# code:

public class Person {
  // Fields omitted from this example.

  // Methods:

  public bool TakeOutTheTrash() {...}
  public bool MowTheLawn() {...}
  public bool WashTheDishes()  {...}
}

We decide that we want our teenaged sons Larry, Moe, and Curley to each do one of these three chores. How would we ask them to do this? If we were to say the following, chances are that none of the chores would get done, because we haven't tasked a specific son with fulfilling any of these requests:

  • "Please wash the dishes."

  • "Please take out the trash."

  • "Please mow the lawn."

Larry, Moe, and Curley will probably all stay glued to the TV, because although any of them could fulfill the requests, none will acknowledge that a request has been directed toward them.

On the other hand, if we were to instead say the following, we'd be assigning each task to a specific son:

  • "Larry, please wash the dishes."

  • "Moe, please take out the trash."

  • "Curley, please mow the lawn."

Again, using C# syntax, this might be expressed as follows:

// We create three Person objects/instances:
Person larry = new Person();
Person moe = new Person();
Person curley = new Person();

// We call a method on each, indicating the operation that we wish
// each of them to perform:
larry.WashTheDishes();
						moe.TakeOutTheTrash();
						curley.MowTheLawn();

By applying each method call to a different "son" (Person object reference), there is no ambiguity as to which object is being asked to perform which operation.

We'll learn in Chapter 7 that a class can also be the target of a method call for a special type of method known as a static method.


Assuming that WashTheDishes is a Person method as defined previously, the following code won't compile in C# (or, for that matter, in any OOPL).

public class BadCode
{
  static void Main() {
    // This next line won't compile -- where's the "dot"?

WashTheDishes();
  }
}

Because the compiler would expect such a method call to be associated with a particular Person object via dot notation; the following error message would result:

error: An object reference is required for the non-static field,
method, or property 'Person.WashTheDishes()'

4.3.3. C# Expressions, Revisited

When we defined the term simple expression in Chapter 1, there was one form of expression that we omitted because we hadn't yet talked about objects: method calls. We repeated our list of what constitutes C# expressions here, adding method calls to the mix:

  • A constant: 7, false

  • A char(acter) literal: 'A', '&'

  • A string literal: "foo"

  • The name of any variable declared to be of one of the predefined types that we've seen so far: myString, x

  • A method call: z.length()

  • Any two of the preceding expressions combined with one of the C# binary operators: x + 2

  • Any one of the preceding expressions that is modified by one of the C# unary operators: i++

  • Any of the preceding simple expressions enclosed in parentheses: (x + 2)

In Chapter 13, you'll learn about one more type of expression: a chain of two or more methods, concatenated by dots (.); for example, x.GetAdvisor().GetName();.


The type of a "method call expression" is, by definition, the type of result that the method returns when executed. For example, if RegisterForCourse is a method with a return type of bool, then the expression s.RegisterForCourse(...) is said to be an expression of type bool.

4.3.4. Capturing the Return Value from a Method Call

In an earlier example, although we declared the Student class's RegisterForCourse method to have a return type of bool:

bool RegisterForCourse(string courseId, int sectionNumber)

we didn't capture the returned bool value when we invoked the method:

x.RegisterForCourse("MATH 101", 10);

Whenever we invoke a non-void method, it's up to us whether to ignore or respond to the value that it returns. If we want to respond to the returned value, we can optionally capture the result in a specific variable:

bool outcome;
outcome = x.registerForCourse("MATH 101", 10);
						if (!outcome) {
  action to be taken if registration failed...
}

If we plan to use only the returned value once, however, then going to the trouble of declaring an explicit variable such as outcome to capture the result is overkill. We can instead react to the result simply by nesting a method call, which you learned a moment ago is considered to be a valid expression within a more complex statement. For example, we can rewrite the preceding code snippet to eliminate the variable outcome as follows:

// An if expression must evaluate to a Boolean result; the
// RegisterForCourse method does indeed return a Boolean value,
// however, and so the expression (method call) enclosed within
// parentheses represents valid syntax.
if (!(x.RegisterForCourse("MATH 101", 10))) {
  action to be taken if registration failed...
}

NOTE

In fact, we use the "method-call-as-expression" syntax liberally when developing OO applications; for example, when returning values from methods:

public string GetAdvisorName() {
  return advisor.GetName();
}

or printing to the console:

Console.WriteLine("The student's gpa is:  " + s.GetGPA());

The resulting code is more compact, and it eliminates the need to create objects that are used for one line of code only. However, "method-call-as-expression" syntax is one of the aspects of OOP that seems to take the most getting used to for folks who are just getting started with learning the OO paradigm.

4.3.5. Method Signatures

You already learned that a method header consists of the method's return type, name, and formal parameter list:

void SwitchMajor(string newDepartment, Professor newAdvisor)

From the standpoint of the code used to invoke a method on an object, however, the return type and parameter names aren't immediately evident upon inspection:

Student s = new Student();
Professor p = new Professor();

// Details omitted...

s.SwitchMajor("MATH", p);

We can infer the following from inspecting the line of code that invokes the method:

  • SwitchMajor is a method defined for the Student class because s is a Student and we're invoking SwitchMajor on s.

  • The SwitchMajor method requires two arguments of type string and Professor, respectively, because that's what we're passing in.

However, we can't see how the formal parameters were named in the corresponding method header, nor can we tell whether the method returns a result or not and, if so, what type of result it returns because we may simply be ignoring the result.

For this reason, we refer to a method signature as those aspects of a method header that are "discoverable" from the perspective of the code used to invoke the method: namely, the method's name, and the order, types, and number of arguments being passed into the method, but excluding the parameter names and method return type.

  • Method header: int GetAge(int ageType)

    • Method signature: GetAge(int)

  • Method header: void SwitchMajor(string newDepartment, Professor newAdvisor)

    • Method signature: SwitchMajor(string, Professor)

  • Method header: string GetName()

    • Method signature: GetName()

4.3.6. Object Interaction via Methods

Let's now look at an example of two objects interacting with each other. Assume that we have two classes defined—Student and Course—and that the methods listed in Table 4-1 are defined on each.

Table 4.1. Student and Course Class Methods
Student Method:
bool SuccessfullyCompleted(Course c)Given a reference c to a particular Course object, we're asking the Student object to confirm that they have indeed taken the course in question and received a passing grade.
Course Method:
bool Register(Student s)Given a reference s to a particular Student object, we are asking the Course method to do whatever is necessary to register the student. In this case, we expect the Course to ultimately respond true or false to indicate success or failure of the registration request.

Figure 4-2 reflects one possible sequence of method calls between a Course object c and a Student object s; each numbered step in the diagram is narrated in the text that follows.

Figure 4.2. Method calls on a Student and Course object

(Please refer to this diagram when reading through steps 1 through 4.)

  1. We register a student with a course by invoking the following method where s represents a particular Student object:

    c.Register(s);

    For now, we won't worry about where in the code this statement came from; it was most likely triggered by a user's interaction with the SRS GUI. You'll see the complete code context of how all of these methods are called later in this chapter, in the section entitled "Objects as Clients and Suppliers."

  2. In order for Course object c to officially determine whether or not s should be permitted to register, c invokes the following method:

    s.SuccessfullyCompleted(c2);

    on Student s, where c2 represents a reference to a different Course object that happens to be a prerequisite of Course c. (Don't worry about how Course c knows that c2 is one of its prerequisites; this involves interacting with c's internal prerequisites field, which we haven't talked about. Also, Course c2 isn't depicted in Figure 4-2 because, strictly speaking, c2 isn't engaged in this "discussion" between objects c and s; c2 is being talked about, but is not doing any talking itself!)

  3. If the SuccessfullyCompleted method returns the value true to c, indicating that s has successfully completed the prerequisite course. (For the time being, we will ignore the details as to how s determines this; it involves interacting with s's internal transcript field, which we haven't fully explained the structure of just yet.)

  4. Convinced that the student has complied with the prerequisite requirements for the course, Course object c finishes the job of registering the student (internal details omitted for now) and confirms the registration by responding with a value of true to the originator of the method call.

This example was overly simplistic; in reality, Course c may have had to interact with numerous other objects:

  • A Classroom object (the room in which the course is to be held, to make sure that it has sufficient room for another student)

  • A DegreeProgram object (the degree sought by the student, to make sure that the requested course is indeed required for the degree that the student is pursuing)

and so forth—before sending a true response to indicate that the request to register Student s had been fulfilled. We'll see a slightly more complicated version of interactions between objects later in the chapter.

4.3.7. Accessing Fields via Dot Notation

Just as we use dot notation to call methods on objects, we can also use dot notation to refer to an object's accessible fields. For example, if we declare a reference variable x to be of type Student, we can potentially refer to any of Student x's fields via the following notation:

x.field_name

where the dot is used to qualify the name of the field of interest with the name of the reference variable representing the object of interest: x.name, x.gpa, and so forth.

Here are a few additional examples:

// Instantiate three objects.
Student x = new Student();
Student y = new Student();
Professor z = new Professor();

// We may use dot notation to access the value of accessible fields.
						// Set student x's name...
						x.name = "John Smith";

//...and student y's name.
						y.name = "Joe Blow";
						// Set professor z's name to be the same as student x's name.
						z.name = x.name;
						// Compute the total of the two students' ages.
						int i = x.age + y.age;
						// Set the professor's age to be 40.
						z.age = 40;

However, we'll see later in this chapter that just because we can access fields this way doesn't mean that we should. There are many reasons why we'll want to restrict access to an object's data to give the object complete control over when and how its data is altered, and several mechanisms for how we can do so.

4.3.8. Delegation

If a request is made of an object A and, in fulfilling the request, A in turn requests assistance from another object B, this is known as delegation by A to B. The concept of delegation among objects is exactly the same as delegation between people in the real world: if your "significant other" asks you to mow the lawn while they are out running errands, and you in turn hire a neighborhood teenager to mow the lawn, then, as far as your partner is concerned, the lawn has been mowed. The fact that you delegated the activity to someone else is (hopefully!) irrelevant.

The fact that delegation has occurred between objects is often transparent to the initiator of a method call as well. In our previous message passing example, Course c delegated part of the work of registering Student s back to s when c asked s to verify a prerequisite course. However, from the perspective of the originator of the registration request—c.Register(s);—this seems like a simple interaction: namely, the requestor asked c to register a student, and it did so! All the "behind the scenes" details of what c had to do to accomplish this are hidden from the requestor (see Figure 4-3).

Figure 4.3. A requestor sees only the external details of a method call.

4.3.9. Access to Objects

The only way that an object A can call a method on an object B is if A has access to an object reference to B. This can happen in several different ways:

  • Object A might maintain a reference to B as one of A's fields; for example, here's the example from Chapter 3 of a Student object having a Professor reference as a field:

    public class Student
    {
      // Fields.
      string name;
      Professor facultyAdvisor;
      // etc.

  • Object A may be handed a reference to B as an argument of one of A's methods. This is how Course object c obtained access to Student object s in the preceding method calling example, when c's Register method was called:

    c.Register(s);

  • A reference to object B may be made "globally available" to the entire application, such that all other objects can access it. We'll discuss techniques for doing so when we construct the SRS in Part Three of the book.

  • Object A may have to explicitly request a reference to B by calling a method on some third object C. Because this is potentially the most complex way for A to obtain a reference to B, we'll illustrate this with an example.

Going back to the example interaction between Course object c and Student object s from a few pages ago, let's complicate the interaction a bit:

  • First, we introduce a third object: a Transcript object t, which represents a record of all courses taken by Student object s.

  • Furthermore, we assume that Student s maintains a reference to Transcript t as one of s's fields (specifically, the transcript field), and conversely that Transcript t maintains a reference back to its "owner," Student s, as one of t's fields (see Figure 4-4).

Figure 4.4. A more complex method calling example involving three objects

  1. In this enhanced object interaction, the first step is exactly as previously described: namely, the Register method is invoked on Course object c where s represents a Student object:

    c.Register(s);

  2. Now, instead of Course c invoking the method s.SuccessfullyCompleted(c2) on Student s as before, where c2 represents a prerequisite Course, the GetTranscript method is invoked on Student object s because c wants to check s's transcript firsthand:

    s.GetTranscript();

    This method is declared in the Student class with a header defined as follows:

    Transcript GetTranscript()

    Note that this method is defined to return a Transcript object reference: specifically, a reference to the Transcript object t belonging to this student.

  3. Because Student s maintains a reference to its Transcript object as a field, it's a snap for the GetTranscript method to return a reference to the student's Transcript object.

  4. Now that Course c has its own temporary reference to Transcript t, object c can talk directly to t. Object c proceeds to ask t whether t has any record of c's prerequisite course c2 having successfully been completed by Student s by invoking the method:

    t.SuccessfulCompletion(c2);

    This implies that there is a method declared in the Transcript class with the following header:

    bool SuccessfulCompletion(Course c)

  5. Transcript object t answers back with a response of true, indicating that Student s has indeed successfully completed the prerequisite course in question. (Note that Student s is unaware that c is talking to t; object s knows that it was asked by c to return a reference to t in an earlier message, but s has no insights as to why c asked for the reference.

    NOTE

    This is not unlike the real-world situation in which person A asks person B for person C's phone number, without telling B why they want to call C.

  6. Satisfied that Student s has complied with its prerequisite requirements, Course object c finishes the job of registering the student (internal details omitted for now) and confirms the registration by responding with a value of true to the originator of the registration request that first arose in step 1. Now that c has finished with this transaction, it discards its (temporary) reference to t.

    Note that from the perspective of whoever invoked the original method on Course c, this more complicated interaction appears identical to the earlier, simpler interaction, (see Figure 4-5):

    c.Register(s);

Figure 4.5. The external details of this more complex interaction appear identical from the requestor's standpoint.

All the caller of the original method knows is that the Register method call eventually responded with a value of true.

4.3.10. Objects As Clients and Suppliers

In the preceding example of object interaction between a Course object and a Student object, we can consider Course object c to be a client of Student object s because c is requesting that s perform one of its methods—namely, GetTranscript—as a service to c. This is analogous to the real-world concept of you, as a client, requesting the services of an accountant, or an attorney, or an architect. Similarly, c is a client of Transcript t when c asks t to perform its SuccessfulCompletion method. We therefore refer to code that invokes a method on an object A as client code relative to A because the code benefits from the service(s) performed by A.

Let's look at a few examples of client code. The following code corresponds to the object interaction example involving a Course, Student, and Transcript object from a few pages back, and as promised earlier, provides the context for the methods that were called in that example.

The following code snippet, taken from the Main method of an application, instantiates two objects and invokes a method on one of them, which gets them "talking":

static void Main() {
  Course c = new Course();
  Student s = new Student();

  // Details omitted.

  // Invoke a method on Course object c.
						c.Register(s);

  // etc.
}

In this example, the Main method is considered to be client code relative to Course object c because it calls upon c to perform its Register method.

Let's now look at the code that implements the body of the Register method, inside of the Course class:

public class Course

{
  // details omitted...

  public bool Register(Student s) {
    bool outcome = false;

    // Get a reference to Student s's Transcript object.
						Transcript t = s.GetTranscript();
						// Now, invoke a method on that Transcript object.
						// (Assume that c2 is a reference to a prerequisite Course)
						if (t.SuccessfulCompletion(c2)) {

      outcome = true;
    }
    else {
      outcome = false;
    }

    return outcome;
  }

  // etc.
}

We see that the Register method body is considered to be client code relative to both Student object s and Transcript object t because this code calls upon s and t to each perform an operation.

Whenever an object A is a client of object B, object B in turn can be thought of as a supplier to A.

Note that the roles of client and supplier are not absolute between two objects; such roles are only relevant for the duration of a particular method call event. Similarly, if I ask you to pass me the butter, I am your client, and you are my supplier; and if a moment later you ask me to pass you the bread, then you are my client, and I am your supplier.

NOTE

The notion of clients and suppliers is discussed further in Object-Oriented Software Construction by Bertrand Meyer (Prentice Hall).

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

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