Overloading Methods

Problem

You want to create two different versions of a method with the same name: two methods that differ in the arguments they take.

Solution

A Ruby class can have only one method with a given name. Within that single method, though, you can put logic that branches depending on how many and what kinds of objects were passed in as arguments.

Here's a Rectangle class that represents a rectangular shape on a grid. You can instantiate a Rectangle in one of two ways: by passing in the coordinates of its top-left and bottom-left corners, or by passing in its top-left corner along with its length and width. There's only one initialize method, but you can act as though there were two.

	# The Rectangle constructor accepts arguments in either of the following forms:
	# Rectangle.new([x_top, y_left], length, width)
	# Rectangle.new([x_top, y_left], [x_bottom, y_right])
	class Rectangle
	 def 
initialize(*args)
	 case args.size
	 when 2
	 @top_left, @bottom_right = args
	 when 3
	 @top_left, length, width = args
	 @bottom_right = [@top_left[0] + length, @top_left[1] - width]
	 else
	 raise ArgumentError, "This method takes either 2 or 3 arguments."
	 end

	 # Perform additional type/error checking on @top_left and
	 # @bottom_right…
	 end
	end

Here's the Rectangle constructor in action:

	'
	Rectangle.new([10, 23], [14, 13])
	# => #

	Rectangle.new([10, 23], 4, 10)
	# => #

	Rectangle.new
	# => ArgumentError: This method takes either 2 or 3 arguments.

 

Discussion

In strongly typed languages like C++ and Java, you must often create multiple versions of the same method with different arguments. For instance, Java's StringBuffer class implements over 10 variants of its append method: one that takes a boolean, one that takes a string, and so on.

Ruby's equivalent of StringBuffer is StringIO, and its equivalent of the append method is StringIO#<<. In Ruby, that method can only be defined once, but it can take an object of any type. There's no need to write different versions of the method for taking different kinds of object. If you need to do type checking (such as making sure the object has a string representation), you put it in the method body rather than in the method definition.

Ruby's loose typing eliminates most of the need for method overloading. Its default arguments, variable-length argument lists, and (simulated) keyword arguments eliminate most of the remaining cases. What's left? Mainly methods that can take two completely different sets of arguments, like the Rectangle constructor given in the Solution.

To handle these, write a method that takes a variable number of arguments, and give it some extra code at the front that figures out which set of arguments was passed. Rectangle#initialize rejects argument lists that are of the wrong length. Additional code could enforce duck typing to make sure that the arguments passed in are of the right type. See Recipe 10.16 for simple ways to do argument validation.

See Also

  • Recipe 8.11, "Accepting or Passing a Variable Number of Arguments"
  • Recipe 8.12, "Simulating Keyword Arguments"
  • Recipe 10.16, "Enforcing Software Contracts"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net