CSC/ECE 517 Fall 2007/wiki1b 1 as: Difference between revisions
Line 72: | Line 72: | ||
== 3. Using Aliases to Avoid Conflict == | == 3. Using Aliases to Avoid Conflict == | ||
An alternative to using namespaces is to use alias to disambiguate method names in different modules. Again, looking at the first example, we have modified it to show the use of aliases. | |||
module Foo | |||
def bar | |||
"hello" | |||
end | |||
end | |||
module Test | |||
def bar | |||
"goodbye" | |||
end | |||
end | |||
In the Zap class, we define two aliases, one for Foo's bar method and another for Test's bar method. The alias syntax is ''alias :new_name :old_name''. So, to disambiguate, we first include Foo, and give an alias to its bar method. Then include Test and give an alias to its bar method. | |||
class Zap | |||
include Foo | |||
alias :Foo_bar :bar | |||
include Test | |||
alias :Test_bar :bar | |||
end | |||
The caller then can make a call to the new method names defined by the aliases to choose exactly which call they want to make. | |||
z = Zap.new | |||
puts z.Foo_bar | |||
puts z.Test_bar | |||
= Conclusion = | = Conclusion = |
Revision as of 20:27, 10 October 2007
Background
Ruby does not implement true multiple inheritance, but provides Modules as a way to reuse chunks of codes in many classes. Modules, unlike like classes in OO languages such as Java, cannot be instantiated or sub-classed. Modules are included in class definitions by using the ‘include’ method which will mix that module’s methods into the calling class. The module’s methods will then become instance methods.
A class can include several modules within the class definition. However, a problem exists when a class includes multiple modules that contain a method of the same name. Since the class will have access to both of these methods, unexpected behavior may occur when the names of the methods conflict.
Let’s look at a few examples!
Examples
1. Method Name Conflict
In the following example, we have the class Zap. This class includes the module Foo and the module Test. Both modules contain a method named bar.
module Foo def bar "hello" end end module Test def bar "goodbye" end end class Zap include Foo include Test end
If a call is made to Zap's bar method, the caller is unsure whether the result will be "hello" or "goodbye".
z = Zap.new puts z.bar
The result of this call is:
goodbye
This is because Ruby will first search the last module included, and continue in a descending order. The method that is invoked may not be the method that the caller expected to run. Therefore, precaution to be taken within the class to eliminate ambiguity.
2. Using Namespaces to Avoid Conflict
Using namespaces within the modules will make the method names unique and help disambiguate any conflicts. The namespace is created simply by prefixing the method name with the module name and a period (i.e., Module.method).
Let's modify the previous example to include namespaces.
module Foo def Foo.bar #the method name is qualified "hello" end end module Test def Test.bar #the method name is qualified "goodbye" end end class Zap include Foo include Test
Zap class defines a method called bar, and makes a call to both bar methods in Foo and Test. If the programmer knew for sure that any call to the bar method should invoke only one of the modules, this could be handled here by only calling the module of choice.
def bar puts Foo.bar puts Test.bar end
z = Zap.new puts z.bar
This will produce:
Hello Goodbye
3. Using Aliases to Avoid Conflict
An alternative to using namespaces is to use alias to disambiguate method names in different modules. Again, looking at the first example, we have modified it to show the use of aliases.
module Foo def bar "hello" end end
module Test def bar "goodbye" end end
In the Zap class, we define two aliases, one for Foo's bar method and another for Test's bar method. The alias syntax is alias :new_name :old_name. So, to disambiguate, we first include Foo, and give an alias to its bar method. Then include Test and give an alias to its bar method.
class Zap include Foo alias :Foo_bar :bar include Test alias :Test_bar :bar end
The caller then can make a call to the new method names defined by the aliases to choose exactly which call they want to make.
z = Zap.new puts z.Foo_bar puts z.Test_bar
Conclusion
As illustrated in the examples, there are at least two ways to ensure that your class runs as expected even when using modules with method name conflicts.
The two approaches are:
- using namespaces
- using an alias
In our opinion, using namespaces is the better approach. Inheriting from the module and using the qualified names to avoid conflicts is a more efficient OO design. It is also easier to read and maintain because in viewing the call, a reader will immediately gather the expected behavior of the call, as opposed to finding the alias definition and trying to make the connection.
With the alias approach, the inheritance is limited and the programmer will need to constantly update the list of aliases for methods needed as they arise.