Friday, November 6, 2009

Recipe 8.18. Implementing Class and Singleton Methods










Recipe 8.18. Implementing Class and Singleton Methods







Problem


You want to associate a new method with a class (as opposed to the instances of that class), or with a particular object (as opposed to other instances of the same class).




Solution


To define a class method, prefix the method name with the class name in the method definition. You can do this inside or outside of the class definition.


The Regexp.is_valid? method, defined below, checks whether a string can be compiled into a regular expression. It doesn't make sense to call it on an already instantiated Regexp, but it's clearly related functionality, so it belongs in the Regexp class (assuming you don't mind adding a method to a core Ruby class).



class Regexp
def Regexp.is_valid?(str)
begin
compile(str)
valid = true
rescue RegexpError
valid = false
end
end
end
Regexp.is_valid? "The horror!" # => true
Regexp.is_valid? "The)horror!" # => false



Here's a Fixnum.random method that generates a random number in a specified range:



def Fixnum.random(min, max)
raise ArgumentError, "min > max" if min > max
return min + rand(max-min+1)
end
Fixnum.random(10, 20) # => 13
Fixnum.random(-5, 0) # => -5
Fixnum.random(10, 10) # => 10
Fixnum.random(20, 10)
# ArgumentError: min > max



To define a method on one particular other object, prefix the method name with the variable name when you define the method:



company_name = 'Homegrown Software'
def company_name.legalese
return "#{self} is a registered trademark of ConglomCo International."
end

company_name.legalese
# => "Homegrown Software is a registered trademark of ConglomCo International."
'Some Other Company'.legalese
# NoMethodError: undefined method 'legalese' for "Some Other Company":String





Discussion


In Ruby, a
singleton method is a method defined on one specific object, and not available to other instances of the same class. This is kind of analagous to the Singleton pattern, in which all access to a certain class goes through a single instance, but the name is more confusing than helpful.



Class methods are actually a special case of
singleton methods. The object on which you define a new method is the Class object itself.


Some common types of
class methods are listed here, along with illustrative examples taken from Ruby's standard library:


  • Methods that instantiate objects, and methods for retrieving an object that implements the Singleton pattern. Examples: Regexp.compile, Date.parse, Dir. open, and Marshal.load (which can instantiate objects of many different types). Ruby's standard constructor, the new method, is another example.

  • Utility or helper methods that use logic associated with a class, but don't require an instance of that class to operate. Examples: Regexp.escape, Dir.entries, File.basename.

  • Accessors for class-level or Singleton data structures. Examples: THRead.current, Struct.members, Dir.pwd.

  • Methods that implicitly operate on an object that implements the Singleton pattern. Examples: Dir.chdir, GC.disable and GC.enable, and all the methods of Process.


When you define a
singleton method on an object other than a
class, it's usually to redefine an existing method for a particular object, rather than to define a brand new method. This behavior is common in frameworks, such as GUIs, where each individual object has customized behavior. Singleton method definition is a cheap substitute for subclassing when you only need to customize the behavior of a single object:



class Button
#A stub method to be overridden by subclasses or individual Button objects
def pushed
end
end

button_a = Button.new
def button_a.pushed
puts "You pushed me! I'm offended!"
end

button_b = Button.new
def button_b.pushed
puts "You pushed me; that's okay."
end

Button.new.pushed
#

button_a.pushed
# You pushed me! I'm offended!

button_b.pushed
# You pushed me; that's okay.



When you define a method on a particular object, Ruby acts behind the scenes to transform the object into an anonymous subclass of its former class. This new class is the one that actually defines the new method or overrides the methods of its superclass.













No comments: