Singleton Methods

A singleton method is a method that belongs to a single object rather than to an entire class. Many of the methods in the Ruby class library are singleton methods. This is because, as mentioned earlier, each class is an object of the type Class. Or, to put it simply, the class of every class is Class. This is true of all classes—both those you define yourself and those provided by the Ruby class library:

class_classes.rb

class MyClass
end

puts( MyClass.class )    #=> Class
puts( String.class )     #=> Class
puts( Object.class )     #=> Class
puts( Class.class )      #=> Class
puts( IO.class )         #=> Class

Now, some classes also have class methods—that is, methods that belong to the Class object itself. In that sense, these are singleton methods of the Class object. Indeed, if you evaluate the following, you will be shown an array of method names that match the names of IO class methods:

p( IO.singleton_methods )

This displays the following:

[:new, :open, :sysopen, :for_fd, :popen, :foreach, :readlines,
 :read, :binread, :select, :pipe, :try_convert, :copy_stream]

As explained earlier, when you write your own class methods, you do so by prefacing the method name with the name of the class:

def MyClass.classMethod

It turns out that you can use a similar syntax when creating singleton classes for specific objects. This time you preface the method name with the name of the object:

def myObject.objectMethod

class_hierarchy.rb

Let’s look at a concrete example. Suppose you have a program containing Creature objects of many different species (maybe you are a veterinarian, the head keeper at a zoo, or, like the author of this book, an enthusiastic player of adventure games); each creature has a method called talk that displays the vocal noise each creature usually makes.

Here’s my Creature class and a few creature objects:

singleton_meth1.rb

class Creature
   def initialize( aSpeech )
      @speech = aSpeech
   end


   def talk
      puts( @speech )
   end
end

cat = Creature.new( "miaow" )
dog = Creature.new( "woof" )
budgie = Creature.new( "Who's a pretty boy, then!" )
werewolf = Creature.new( "growl" )

Then you suddenly realize that one of those creatures, and one alone, has additional special behavior. On the night of a full moon, the werewolf not only talks (“growl”) but also howls (“How-oo-oo-oo-oo!”). It really needs a howl method.

You could go back and add such a method to the Creature class, but then you’d end up with howling dogs, cats, and budgies too—which is not what you want. You could create a new Werewolf class that descends from Creature, but you will only ever have one werewolf (they are, alas, an endangered species), so why do you want a whole class for just that? Wouldn’t it make more sense to have a werewolf object that is the same as every other creature object except that it also has a howl method? Okay, let’s do that by giving the werewolf its very own singleton method. Here goes:

def werewolf.howl
   puts( "How-oo-oo-oo-oo!" )
end

Heck, you can do better than that! It howls only on a full moon, so let’s make sure that, if asked to howl when the moon is new, it just growls. Here’s my finished method:

def werewolf.howl
    if FULLMOON then
      puts( "How-oo-oo-oo-oo!" )
   else
      talk
   end
end

Notice that, even though this method has been declared outside the Creature class, it is able to call the instance method talk. That’s because the howl method now lives “inside” the werewolf object so has the same scope within that object as the talk method. It does not, however, live inside any of the werewolf’s fellow creatures; the howl method belongs to him and him alone. Try to make the budgie.howl, and Ruby will inform you that howl is an undefined method.

Now, if you are debugging your code for your own use, having your program blow up thanks to an undefined method may be acceptable; however, if your program does so out in the big, bad world of the “end user,” it is definitely not acceptable.

If you think undefined methods are likely to be a problem, you can take avoidance measures by testing whether a singleton method exists before trying to use it. The Object class has a singleton_methods method that returns an array of singleton method names. You can test a method name for inclusion using the Array class’s include? method. In singleton_meth2.rb, for example, I’ve programmed an “open the box” game, which has a number of Box objects, only one of which, when opened, contains the star prize. I’ve named this special Box object starprize and given it a singleton method called congratulate:

singleton_meth2.rb

starprize = Box.new( "Star Prize" )
def starprize.congratulate
    puts( "You've won a fabulous holiday in Grimsby!" )
end

The congratulate method should be called when the starprize box is opened. This bit of code (in which item is a Box object) ensures that this method (which does not exist in any other object) is not called when some other box is opened:

if item.singleton_methods.include?("congratulate") then
    item.congratulate
end

An alternative way of checking the validity of a method would be to pass that method name as a symbol (an identifier preceded by a colon) to the Object class’s respond_to? method:

if item.respond_to?( :congratulate ) then
   item.congratulate
end

Note

You’ll see another way of handling nonexistent methods in Chapter 20.

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

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