Aliasing Methods

Problem

You (or your users) frequently misremember the name of a method. To reduce the confusion, you want to make the same method accessible under multiple names.

Alternatively, you're about to redefine a method and you'd like to keep the old version available.

Solution

You can create alias methods manually, but in most cases, you should let the alias command do it for you. In this example, I define an InventoryItem class that includes a price method to calculate the price of an item in quantity. Since it's likely that someone might misremember the name of the price method as cost, I'll create an alias:

class InventoryItem attr_accessor :name, :unit_price def initialize(name, unit_price) @name, @unit_price = name, unit_price end def price(quantity=1) @unit_price * quantity end #Make InventoryItem#cost an alias for InventoryItem#price alias :cost :price #The attr_accessor decorator created two methods called "unit_price" and #"unit_price=". I'll create aliases for those methods as well. alias :unit_cost :unit_price alias :unit_cost= :unit_price= end bacon = InventoryItem.new("Chunky Bacon", 3.95) bacon.price(100) # => 395.0 bacon.cost(100) # => 395.0 bacon.unit_price # => 3.95 bacon.unit_cost # => 3.95 bacon.unit_cost = 3.99 bacon.cost(100) # => 399.0

 

Discussion

It's difficult to pick the perfect name for a method: you must find the word or short phrase that best conveys an operation on a data structure, possibly an abstract operation that has different "meanings" depending on context.

Sometimes there will be no good name for a method and you'll just have to pick one; sometimes there will be too many good names for a method and you'll just have to pick one. In either case, your users may have difficulty remembering the "right" name of the method. You can help them out by creating aliases.

Ruby itself uses aliases in its standard library: for instance, for the method of Array that returns the number of items in the array. The terminology used in area varies widely. Some languages use length or len to find the length of a list, and some use size.[3]

[3] Java uses both: length is a member of a Java array, and size is a method that returns the size of a collection.

Ruby compromises by calling its method Array#length, but also creating an alias called Array#size.[4] You can use either Array#length or Array#size because they do the same thing based on the same code. If you come to Ruby from Python, you can make yourself a little more comfortable by creating yet another alias for length:

[4] Throughout this book, we use Array#size instead of Array#length. We do this mainly because it makes the lines of code a little shorter and easier to fit on the page. This is probably not a concern for you, so use whichever one you're comfortable with.

class Array alias :len :length end [1, 2, 3, 4].len # => 4

The alias command doesn't make a single method respond to two names, or create a shell method that delegates to the "real" method. It makes an entirely separate copy of the old method under the new name. If you then modify the original method, the alias will not be affected.

This may seem wasteful, but it's frequently useful to Ruby programmers, who love to redefine methods that aren't working the way they'd like. When you redefine a method, it's good practice to first alias the old method to a different name, usually the original name with an _old suffix. This way, the old functionality isn't lost.

This code (very unwisely) redefines Array#length, creating a copy of the original method with an alias:

class Array alias :length_old :length def length return length_old / 2 end end

Note that the alias Array#size still works as it did before:

array = [1, 2, 3, 4] array.length # => 2 array.size # => 4 array.length_old # => 4

Since the old implementation is still available, it can be aliased back to its original name once the overridden implementation is no longer needed.

class Array alias :length :length_old end array.length # => 4

If you find this behavior confusing, your best alternative is to avoid alias altogether. Instead, define a method with the new name that simply delegates to the "real" method. Here I'll modify the InventoryItem class so that cost delegates to price, rather than having alias create a copy of price and calling the copy cost.

class InventoryItem def cost(*args) price(*args) end end

If I then decide to modify price to tack on sales tax, cost will not have to be modified or realiased.

bacon.cost(100) # => 399.0 require 'bigdecimal' require 'bigdecimal/util' class InventoryItem def price(quantity=1, sales_tax=BigDecimal.new("0.0725")) base_price = (unit_price * quantity).to_d price = (base_price + (base_price * sales_tax).round(2)).to_f end end bacon.price(100) # => 427.93 bacon.cost(100) # => 427.93

We don't even need to change the signature of the cost method to match that of price, since we used the *args construction to accept and delegate any arguments at all:

bacon.cost(100, BigDecimal.new("0.05")) # => 418.95

 

See Also

Категории