1. Trang chủ >
  2. Công Nghệ Thông Tin >
  3. Kỹ thuật lập trình >

1 Understanding self, the current/default object

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (5.14 MB, 519 trang )


117



Understanding self, the current/default object



5.1.1



Who gets to be self, and where

There is always one (and only one) current object or self. You can tell which object it

is by following a small set of rules. These rules are summarized in table 5.1; the table’s

contents will be explained and illustrated as we go along.

To know which object is self, you need to know what context you’re in. In practice,

there aren’t many contexts to worry about. There’s the top level (before you’ve

entered any other context, such as a class definition). There are class-definition

blocks, module-definition blocks, and method-definition blocks. Aside from a few subtleties in the way these contexts interact, that’s about it. As shown in table 5.1, self is

determined by which of these contexts you’re in (class and module definitions are

similar and closely related).

Figure 5.1 gives you a diagrammatic view of most of the cases in table 5.1. Both

show you that some object is always self and that which object is self depends on where

you are in the program.

Table 5.1



How the current object (self) is determined

Context



Example



Which object is self?



Top level of program



Any code outside of other blocks



main (built-in top-level default

object)



Class definition



class C

self



The class object C



Module definition



module M

self



The module object M



Method definitions



1. Top level (outside any definition block)



Whatever object is self when

the method is called; top-level

methods are available as private methods to all objects



def method_name

self

2. Instance method definition

in class



An instance of C, responding

to method_name



class C

def method_name

self

3. Instance method definition

in module



module M

def method_name

self

4. Singleton method on specific

object



I. Individual object extended

by M

II. Instance of class that

mixes in M



obj



def obj.method_name

self



Licensed to sam kaplan



118



CHAPTER 5



Figure 5.1



The default object (self), scope, and visibility



The determination of self in different contexts



The most basic and, in some respects, most unique program context is the top level:

the context of the program before any class or module definition has been opened, or

after they’ve all been closed. We’ll look next at the top level’s ideas about self.

THE TOP-LEVEL SELF OBJECT



The term top level refers to program code written outside of any class or module definition block. If you open a brand-new text file and type

x = 1



you’ve created a top-level local variable x. If you type

def m

end



you’ve created a top-level method. (We’ll look at top-level methods in much more

detail in section 5.4; they’re relevant here just as pointers to the existence of a toplevel self.) A number of our examples, particularly in the early chapters (for example,

the methods in chapter 2 demonstrating argument semantics), involved top-level

code. Once we started writing class and module definitions, more of our code began

to appear inside those definitions. The way self shifts in class, module, and method

definitions is uniform: the keyword (class, module, or def) marks a switch to a new

self. But what is self when you haven’t yet entered any definition block?

The answer is that Ruby provides you with a start-up self at the top level. If you ask

it to identify itself

ruby -e 'puts self'



it will tell you that it’s called main.



Licensed to sam kaplan



Understanding self, the current/default object



119



main is a special term that the default self object uses to refer to itself. You can’t

refer to it as main; Ruby will interpret your use of main as a regular variable or method

name. If you want to grab main for any reason, you need to assign it to a variable at the

top level:

m = self



It’s not likely that you’d need to do this, but that’s how it’s done. More commonly,

you’ll feel the need for a fairly fine-grained sense of what self is in your class, module,

and method definitions, where most of your programming will take place.



5.1.2



Self inside class, module, and method definitions

It pays to keep a close eye on self as you write classes, modules, and methods. There

aren’t that many rules to learn, and they’re applied consistently; but they’re worth

learning well up front, so you’re clear on why the various techniques you use that

depend on the value of self play out the way they do.

It’s all about self switching from one object to another, which it does when you

enter a class or module definition, an instance method definition, or a singleton

method (including class method) definition.

SELF IN CLASS AND MODULE DEFINITIONS



In a class or module definition, self is the class or module object. This innocentsounding rule is important. If you master it, you’ll save yourself from several of the

most common mistakes that people make when they’re learning Ruby.

You can see what self is at various levels of class and/or module definition by using

puts explicitly, as shown in listing 5.1.

Listing 5.1



Examining self via calls to puts in class and module definitions



class C

puts "Just started class C:

puts self

Output: C

module M

puts "Nested module C::M:"

puts self

Output: C::M

end

puts "Back in the outer level of C:"

puts self

Output: C

end



As soon as you cross a class or module keyword boundary, the class or module whose

definition block you’ve entered—the Class or Module object—becomes self.

Listing 5.1 shows two cases: entering C, and then entering C::M. When you leave C::M

but are still in C, self is once again C.

Of course, class and module-definition blocks do more than just begin and end.

They also contain method definitions; and method definitions, for both instance

methods and class methods, have rules determining self.



Licensed to sam kaplan



120



CHAPTER 5



The default object (self), scope, and visibility



SELF IN INSTANCE METHOD DEFINITIONS



The notion of self inside an instance method definition is subtle, for the following reason: when the interpreter encounters a def/end block, it defines the method immediately; but the code inside the method definition isn’t executed until later, when an

object capable of triggering its execution receives the appropriate message.

When you’re looking at a method definition on paper or on the screen, you can

only know in principle that, when the method is called, self will be the object that

called it (the receiver of the message). At the time the method gets defined, the

most you can say is that self inside this method will be some future object that calls

the method.

You can rig a method to show you self as it runs:

class C

def x

puts "Class C, method x:"

puts self

end

end

c = C.new

c.x

puts "That was a call to x by: #{c}"



This snippet outputs

Class C, method x:

#

That was a call to x by: #



The weird-looking item in the output (#) is Ruby’s way of saying “an

instance of C.” (The hexadecimal number after the colon is a memory-location reference. When you run the code on your system, you’ll probably get a different number.)

As you can see, the receiver of the “x” message, namely c, takes on the role of self during execution of x.

SELF IN SINGLETON METHOD AND CLASS METHOD DEFINITIONS



As you might expect, when a singleton method is executed, self is the object that owns

the method, as an object will readily tell you:

obj = Object.new

def obj.show_me

puts "Inside singleton method show_me of #{self}"

end

obj.show_me

puts "Back from call to show_me by #{obj}"



The output of this example is as follows:

Inside singleton method show_me of #

Back from call to show_me by #



It makes sense that if a method is written to be called by only one object, that object

gets to be self. Moreover, this is a good time to remember class methods—which are,



Licensed to sam kaplan



Understanding self, the current/default object



121



essentially, singleton methods attached to class objects. The following example reports

on self from inside a class method of C:

class C

def C.x

puts "Class method of class C"

puts "self: #{self}"

end

end

C.x



Here’s what it reports:

Class method of class C

self: C



Sure enough, self inside a singleton method (a class method, in this case) is the object

whose singleton method it is.



Using self instead of hard-coded class names

By way of a little programming tip, here’s a variation on the last example:

class C

def self.x

puts "Class method of class C"

puts "self: #{self}"

end

end



B



Note the use of self.x B rather than C.x. This way of writing a class method takes

advantage of the fact that in the class definition, self is C. So, def self.x is the

same as def C.x.

The self.x version offers a slight advantage: if you ever decide to rename the class,

self.x will adjust automatically to the new name. If you hard-code C.x, you’ll have

to change C to your class’s new name. But you do have to be careful. Remember that

self inside a method is always the object on which the method was called. You can

get into a situation where it feels like self should be one class object, but is actually

another:

class D < C

end

D.x



D gets to call x, because subclasses get to call the class methods of their superclasses. As you’ll see if you run the code, the method C.x reports self—correctly—

as being D, because it’s D on which the method is called.



Being self at a given point in the program comes with some privileges. The chief

privilege enjoyed by self is that of serving as the default receiver of messages, as

you’ll see next.



Licensed to sam kaplan



122



CHAPTER 5



5.1.3



The default object (self), scope, and visibility



Self as the default receiver of messages

Calling methods (that is, sending messages to objects) usually involves the dot notation:

obj.talk

ticket.venue

"abc".capitalize



That’s the normal, full form of the method-calling syntax in Ruby. But a special rule

governs method calls: if the receiver of the message is self, you can omit the receiver

and the dot. Ruby will use self as the default receiver, meaning the message you send

will be sent to self, as the following equivalencies show:

Same as self.talk

Same as self.venue

Same as self.capitalize



talk

venue

capitalize



WARNING



You can give a method and a local variable the same name, but it’s rarely

if ever a good idea. If both a method and a variable of a given name exist,

and you use the bare identifier (like talk), the variable takes precedence. To force Ruby to see the identifier as a method name, you’d have

to use self.talk or call the method with an empty argument list:

talk(). Because variables don’t take arguments, the parentheses establish that you mean the method rather than the variable. Again, it’s best to

avoid these name clashes if you can.



Let’s see this concept in action by inducing a situation where we know what self is and

then testing the dotless form of method calling. In the top level of a class-definition

block, self is the class object. And we know how to add methods directly to class

objects. So, we have the ingredients to do a default receiver demo:

class C

def C.no_dot

puts "As long as self is C, you can call this method with no dot"

end

no_dot

end

C.no_dot



B

C



The first call to no_dot B doesn’t have an explicit receiver; it’s a bareword. When

Ruby sees this (and determines that it’s a method call rather than a variable or keyword), it figures that you mean it as shorthand for

self.no_dot



In the case of our example, self.no_dot would be the same as C.no_dot, because

we’re inside C’s definition block and, therefore, self is C. The result is that the method

C.no_dot is called, and we see the output.

The second time we call the method C, we’re back outside the class definition

block. C is no longer self. Therefore, to call no_dot, we need to specify the receiver: C.



Licensed to sam kaplan



Understanding self, the current/default object



123



The result is a second call to no_dot (albeit with a dot) and another printing of the

output from that method.

The most common use of the dotless method call occurs when you’re calling one

instance method from another. Here’s an example:

class C

def x

puts "This is method 'x'"

end

def y

puts "This is method 'y', about to call x without a dot."

x

end

end

c = C.new

c.y



The output is as follows:

This is method 'y', about to call x without a dot.

This is method 'x'.



Upon calling c.y, the method y is executed, with self set to c (which is an instance of

C). Inside y, the bareword reference to x is interpreted as a message to be sent to self.

That, in turn, means the method x is executed.

There’s one situation where you can’t omit the object-plus-dot part of a method

call: when the method name ends with an equal sign—a setter method, in other words.

You have to do self.venue = "Town Hall" rather than venue = "Town Hall" if you want

to call the method venue= on self. The reason is that Ruby always interprets the

sequence identifier = value as an assignment to a local variable. To call the method

venue= on the current object, you need to include the explicit self. Otherwise, you

end up with a variable called venue and no call to the setter method.

The default to self as receiver for dotless method invocations allows you to streamline your code nicely in cases where one method makes use of another. A common

case is composing a whole name from its components: first, optional middle, and last.

Listing 5.2 shows a technique for doing this, using attributes for the three name values

and conditional logic to include the middle name, plus a trailing space, if and only if

there is a middle name.

Listing 5.2



Composing whole name from values, using method calls on implicit self



class Person

attr_accessor :first_name, :middle_name, :last_name

def whole_name

n = first_name + " "

n << "#{middle_name} " if middle_name

n << last_name

end

end



Licensed to sam kaplan



124



CHAPTER 5



The default object (self), scope, and visibility



david = Person.new

david.first_name = "David"

david.last_name = "Black"

puts "David's whole name: #{david.whole_name}"

david.middle_name = "Alan"

puts "David's new whole name: #{david.whole_name}"



The output from the calling code in listing 5.2 is as follows:

David's whole name: David Black

David's new whole name: David Alan Black



The definition of whole_name depends on the bareword method calls to first_name,

middle_name, and last_name being sent to self—self being the Person instance

(david in the example). The variable n serves as a string accumulator, with the components of the name added to it one by one. The return value of the entire method is

n —because the expression n << last_name has the effect of appending last_name to n

and returning the result of that operation.

In addition to serving automatically as the receiver for bareword messages, self also

enjoys the privilege of being the owner of instance variables.



5.1.4



Resolving instance variables through self

A simple rule governs instance variables and their resolution: every instance variable

you’ll ever see in a Ruby program belongs to whatever object is the current object

(self) at that point in the program.

Here’s a classic case where this knowledge comes in handy. See if you can figure

out what this code will print, before you run it:

class C

def show_var

@v = "I am an instance variable initialized to a string."

puts @v

end

@v = "Instance variables can appear anywhere...."

end



B



C



C.new.show_var



The code prints the following:

I am an instance variable initialized to a string.



The trap is that you may think it will print “Instance variables can appear anywhere...” The code prints what it does because the @v in the method definition B

and the @v outside it C are completely unrelated to each other. They’re both

instance variables, and both are named @v, but they aren’t the same variable. They

belong to different objects.

Whose are they?



Licensed to sam kaplan



Understanding self, the current/default object



125



The first @v lies inside the definition block of an instance method of C. That fact

has implications not for a single object, but for instances of C in general: each instance

of C that calls this method will have its own instance variable @v.

The second @v belongs to the class object C. This is one of the many occasions

where it pays to remember that classes are objects. Any object may have its own

instance variables—its own private stash of information and object state. Class objects

enjoy this privilege as much as any other object.

Again, the logic required to figure out what object owns a given instance variable is

simple and consistent: every instance variable belongs to whatever object is playing the

role of self at the moment the code containing the instance variable is executed.

Let’s do a quick rewrite of the example, this time making it a little chattier about

what’s going on. Listing 5.3 shows the rewrite.

Listing 5.3



Chatty examination of the relationship between instance variables and self



class C

puts "Just inside class definition block. Here's self:"

p self

@v = "I am an instance variable at the top level of a class body."

puts "And here's the instance variable @v, belonging to #{self}:"

p @v

def show_var

puts "Inside an instance method definition block. Here's self:"

p self

puts "And here's the instance variable @v, belonging to #{self}:"

p @v

end

end

c = C.new

c.show_var



The output from this version is as follows:

Just inside class definition block. Here's self:

C

And here's the instance variable @v, belonging to C:

"I am an instance variable at the top level of a class body."

Inside an instance method definition block. Here's self:

#

And here's the instance variable @v, belonging to #:

nil



Sure enough, each of these two different objects (the class object C and the instance

of C, c) has its own instance variable @v. The fact that the instance’s @v is nil demonstrates that the assignment to the class’s @v had nothing to do with the instance’s @v.

Understanding self—both the basic fact that such a role is being played by some

object at every point in a program and knowing how to tell which object is self—is one

of the most vital aspects of understanding Ruby. Another equally vital aspect is understanding scope, to which we’ll turn now.



Licensed to sam kaplan



126



5.2



CHAPTER 5



The default object (self), scope, and visibility



Determining scope

Scope refers to the reach or visibility of identifiers, specifically variables and constants.

Different types of identifiers have different scoping rules; using, say, the identifier x

for a local variable in each of two method definitions has a different effect than using

the global variable $x in the same two places, because local and global variables differ

as to scope. In this section, we’ll consider three types of variables: global, local, and

class variables. (As you’ve just seen, instance variables are self-bound, rather than

scope-bound.) We’ll also look at the rules for resolving constants.

Like the role of self, scope changes over the course of a program. Also as with self,

you can deduce what’s in what scope by reading the program as well as running it. But

scope and self aren’t the same thing. You can start a new local scope without self changing—but sometimes scope and self change together. They have in common the fact

that they’re both necessary to make sense of what your code is going to do. Like knowing who self is, knowing what scope you’re in tells you the significance of the code.

Let’s start with global variables—not the most commonly used construct, but an

important one to grasp.



5.2.1



Global scope and global variables

Global scope is scope that covers the entire program. Global scope is enjoyed by global

variables, which are recognizable by their initial dollar-sign ($) character. They’re

available everywhere. They walk through walls: even if you start a new class or method

definition, even if the identity of self changes, the global variables you’ve initialized

are still available to you.

In other words, global variables never go out of scope. In this example, a method

defined inside a class definition body (two scopes removed from the outer or top-level

scope of the program) has access to a global variable initialized at the top:

$gvar = "I'm a global!"

class C

def examine_global

puts $gvar

end

end

c = C.new

c.examine_global



You’ll be told by $gvar, in no uncertain terms, “I’m a global!” If you change all the

occurrences of $gvar to a nonglobal, such as var, you’ll see that the top-level var isn’t

in scope inside the method-definition block.

BUILT-IN GLOBAL VARIABLES



The Ruby interpreter starts up with a fairly large number of global variables already

initialized. These variables store information that’s of potential use anywhere and

everywhere in your program. For example, the global variable $0 contains the name

of the startup file for the currently running program. The global $: (dollar sign followed by a colon) contains the directories that make up the path Ruby searches when



Licensed to sam kaplan



Determining scope



127



you load an external file. $$ contains the process id of the Ruby process. And there

are more.

TIP



A good place to see descriptions of all the built-in global variables you’re

likely to need—and then some—is the file English.rb in your Ruby

installation. This file provides less cryptic names for the notoriously

cryptic global variable set. (Don’t blame Ruby for the names—most of

them come from shell languages and/or Perl and awk.) If you want to

use the slightly friendlier names in your programs, you can do require

"English", after which you can refer to $IGNORECASE instead of $=, $PID

instead of $$, and so forth. A few globals have their English-language

names preloaded; you can say $LOAD_PATH instead of $: even without

loading English.rb.



Creating your own global variables can be tempting, especially for beginning programmers and people learning a new language (not just Ruby, either). But they’re not

always a good idea.

THE PROS AND CONS OF GLOBAL VARIABLES



Globals appear to solve lots of design problems: you don’t have to worry about scope,

and multiple classes can share information by stashing it in globals rather than designing objects that have to be queried with method calls. Without doubt, global variables

have a certain allure.

But they’re used very little by experienced programmers. The reasons for avoiding

them are similar to the reasons they’re tempting. Using global variables tends to end

up being a substitute for solid, flexible program design, rather than contributing to it.

One of the main points of object-oriented programming is that data and actions are

encapsulated in objects. You’re supposed to have to query objects for information and

to request that they perform actions.

And objects are supposed to have a certain privacy. When you ask an object to do

something, you’re not supposed to care what the object does internally to get the job

done. Even if you yourself wrote the code for the object’s methods, when you send the

object a message, you treat the object as a black box that works behind the scenes and

provides a response.

Global variables distort the landscape by providing a layer of information shared

by every object in every context. The result is that objects stop talking to each other

and, instead, share information by setting global variables.

Here’s a small example—a rewrite of our earlier Person class (the one with the

first, optional middle, and last names). This time, instead of attributes on the object,

we’ll generate the whole name from globals:

class Person

def whole_name

n = $first_name + " "

n << "#{$middle_name} " if $middle_name

n << $last_name

end

end



Licensed to sam kaplan



Xem Thêm
Tải bản đầy đủ (.pdf) (519 trang)

×